diff options
author | Hop311 <Hop3114@gmail.com> | 2024-02-05 20:38:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-05 20:38:23 +0100 |
commit | 6489da997cddb20667db0455fa4f38319dc7c883 (patch) | |
tree | 4e343f81b13f166ad4a478962348d4734ea7e11b | |
parent | 068c13ede817d17df599ca3481261bf17ed95604 (diff) | |
parent | 87fa1c74281a651b23089079c4c1621d4fb66d73 (diff) |
Merge pull request #145 from OpenVicProject/gui-loading
Added support for loading all gui files
25 files changed, 515 insertions, 168 deletions
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index c6d6281..157ff04 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -283,21 +283,34 @@ bool Dataloader::_load_interface_files(UIManager& ui_manager) const { ui_manager.lock_sprites(); ui_manager.lock_fonts(); - // Hard-coded example until the mechanism for requesting them from GDScript is fleshed out + /* Hard-coded GUI file names, might be replaced with a dynamic system but everything should still be loaded on startup. */ static const std::vector<std::string_view> gui_files { - "province_interface.gui", "topbar.gui" + /* Contains generic listbox scrollbar */ + "core", + + /* Over-map menus */ + "province_interface", "topbar", "menubar", "outliner", + + /* Nation management screens */ + "country_production", "country_budget", "country_technology", "country_politics", "country_pops", "country_trade", + "country_diplomacy", "country_military" }; + static constexpr std::string_view gui_file_extension = ".gui"; + ui_manager.reserve_more_scenes(gui_files.size()); 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() + gui_file, parse_defines(lookup_file( + append_string_views(interface_directory, gui_file, gui_file_extension) + )).get_file_node() )) { Logger::error("Failed to load interface gui file: ", gui_file); ret = false; } } + ui_manager.lock_scenes(); return ret; diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index 7ab0dbe..297937a 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -169,6 +169,14 @@ node_callback_t NodeTools::expect_date(callback_t<Date> callback) { return expect_identifier(expect_date_str(callback)); } +node_callback_t NodeTools::expect_date_string(callback_t<Date> callback) { + return expect_string(expect_date_str(callback)); +} + +node_callback_t NodeTools::expect_date_identifier_or_string(callback_t<Date> callback) { + return expect_identifier_or_string(expect_date_str(callback)); +} + node_callback_t NodeTools::expect_years(callback_t<Timespan> callback) { return expect_uint<Timespan::day_t>([callback](Timespan::day_t val) -> bool { return callback(Timespan::from_years(val)); diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 54b61d0..c41c09e 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -119,6 +119,8 @@ namespace OpenVic { callback_t<std::string_view> expect_date_str(callback_t<Date> callback); node_callback_t expect_date(callback_t<Date> callback); + node_callback_t expect_date_string(callback_t<Date> callback); + node_callback_t expect_date_identifier_or_string(callback_t<Date> callback); node_callback_t expect_years(callback_t<Timespan> callback); node_callback_t expect_months(callback_t<Timespan> callback); node_callback_t expect_days(callback_t<Timespan> callback); @@ -371,15 +373,15 @@ namespace OpenVic { template<typename T, StringMapCase Case> Callback<std::string_view> auto expect_mapped_string( - template_string_map_t<T, Case> const& map, Callback<T> auto callback + template_string_map_t<T, Case> const& map, Callback<T> auto callback, bool warn = false ) { - return [&map, callback](std::string_view string) -> bool { + return [&map, callback, warn](std::string_view string) -> bool { const typename template_string_map_t<T, Case>::const_iterator it = map.find(string); if (it != map.end()) { return callback(it->second); } - Logger::error("String not found in map: ", string); - return false; + Logger::warn_or_error(warn, "String not found in map: ", string); + return warn; }; } @@ -470,24 +472,14 @@ namespace OpenVic { }; } - template<typename... Args> - bool warn_or_error(bool warn, Args&&... args) { - if (warn) { - Logger::warning(std::forward<Args>(args)...); - return true; - } else { - Logger::error(std::forward<Args>(args)...); - return false; - } - } - template<typename T, typename U, typename... SetArgs> Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set, bool warn = false) { return [&set, warn](T val) -> bool { if (set.emplace(std::move(val)).second) { return true; } - return warn_or_error(warn, "Duplicate set entry: \"", val, "\""); + Logger::warn_or_error(warn, "Duplicate set entry: \"", val, "\""); + return warn; }; } @@ -497,7 +489,8 @@ namespace OpenVic { if (set.emplace(&val).second) { return true; } - return warn_or_error(warn, "Duplicate set entry: \"", &val, "\""); + Logger::warn_or_error(warn, "Duplicate set entry: \"", &val, "\""); + return warn; }; } @@ -509,7 +502,8 @@ namespace OpenVic { if (map.emplace(key, std::move(value)).second) { return true; } - return warn_or_error(warn, "Duplicate map entry with key: \"", key, "\""); + Logger::warn_or_error(warn, "Duplicate map entry with key: \"", key, "\""); + return warn; }; } } diff --git a/src/openvic-simulation/history/DiplomaticHistory.cpp b/src/openvic-simulation/history/DiplomaticHistory.cpp index 55d6d6b..22ee765 100644 --- a/src/openvic-simulation/history/DiplomaticHistory.cpp +++ b/src/openvic-simulation/history/DiplomaticHistory.cpp @@ -32,20 +32,20 @@ AllianceHistory::AllianceHistory( Country const* new_first, Country const* new_second, const Period new_period -) : first { new_first }, second { new_second }, period {new_period} {} +) : first { new_first }, second { new_second }, period { new_period } {} ReparationsHistory::ReparationsHistory( Country const* new_receiver, Country const* new_sender, const Period new_period -) : receiver { new_receiver }, sender { new_sender }, period {new_period} {} +) : receiver { new_receiver }, sender { new_sender }, period { new_period } {} SubjectHistory::SubjectHistory( Country const* new_overlord, Country const* new_subject, const type_t new_type, const Period new_period -) : overlord { new_overlord }, subject { new_subject }, type { new_type }, period {new_period} {} +) : overlord { new_overlord }, subject { new_subject }, type { new_type }, period { new_period } {} void DiplomaticHistoryManager::reserve_more_wars(size_t size) { if (locked) { @@ -115,10 +115,10 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(CountryManager const& std::optional<Date> end {}; bool ret = expect_dictionary_keys( - "first", ONE_EXACTLY, expect_identifier_or_string(country_manager.expect_country_str(assign_variable_callback_pointer(first))), - "second", ONE_EXACTLY, expect_identifier_or_string(country_manager.expect_country_str(assign_variable_callback_pointer(second))), - "start_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(start))), - "end_date", ZERO_OR_ONE, expect_identifier_or_string(expect_date_str(assign_variable_callback(end))) + "first", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(first)), + "second", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(second)), + "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start)), + "end_date", ZERO_OR_ONE, expect_date_identifier_or_string(assign_variable_callback(end)) )(node); alliances.push_back({ first, second, { start, end } }); @@ -131,10 +131,10 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(CountryManager const& std::optional<Date> end {}; bool ret = expect_dictionary_keys( - "first", ONE_EXACTLY, expect_identifier_or_string(country_manager.expect_country_str(assign_variable_callback_pointer(overlord))), - "second", ONE_EXACTLY, expect_identifier_or_string(country_manager.expect_country_str(assign_variable_callback_pointer(subject))), - "start_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(start))), - "end_date", ZERO_OR_ONE, expect_identifier_or_string(expect_date_str(assign_variable_callback(end))) + "first", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(overlord)), + "second", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(subject)), + "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start)), + "end_date", ZERO_OR_ONE, expect_date_identifier_or_string(assign_variable_callback(end)) )(node); subjects.push_back({ overlord, subject, SubjectHistory::type_t::VASSAL, { start, end } }); @@ -147,10 +147,10 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(CountryManager const& std::optional<Date> end {}; bool ret = expect_dictionary_keys( - "first", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(overlord)), - "second", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(subject)), - "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start)), - "end_date", ZERO_OR_ONE, expect_date(assign_variable_callback(end)) + "first", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(overlord)), + "second", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(subject)), + "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start)), + "end_date", ZERO_OR_ONE, expect_date_identifier_or_string(assign_variable_callback(end)) )(node); subjects.push_back({ overlord, subject, SubjectHistory::type_t::UNION, { start, end } }); @@ -163,10 +163,10 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(CountryManager const& std::optional<Date> end {}; bool ret = expect_dictionary_keys( - "first", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(overlord)), - "second", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(subject)), - "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start)), - "end_date", ZERO_OR_ONE, expect_date(assign_variable_callback(end)) + "first", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(overlord)), + "second", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(subject)), + "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start)), + "end_date", ZERO_OR_ONE, expect_date_identifier_or_string(assign_variable_callback(end)) )(node); subjects.push_back({ overlord, subject, SubjectHistory::type_t::SUBSTATE, { start, end } }); @@ -179,10 +179,10 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file(CountryManager const& std::optional<Date> end {}; bool ret = expect_dictionary_keys( - "first", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(receiver)), - "second", ONE_EXACTLY, country_manager.expect_country_identifier(assign_variable_callback_pointer(sender)), - "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start)), - "end_date", ZERO_OR_ONE, expect_date(assign_variable_callback(end)) + "first", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(receiver)), + "second", ONE_EXACTLY, country_manager.expect_country_identifier_or_string(assign_variable_callback_pointer(sender)), + "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start)), + "end_date", ZERO_OR_ONE, expect_date_identifier_or_string(assign_variable_callback(end)) )(node); reparations.push_back({ receiver, sender, { start, end } }); diff --git a/src/openvic-simulation/history/Period.cpp b/src/openvic-simulation/history/Period.cpp index cb133dd..37758a1 100644 --- a/src/openvic-simulation/history/Period.cpp +++ b/src/openvic-simulation/history/Period.cpp @@ -5,7 +5,7 @@ using namespace OpenVic; Period::Period( const Date new_start_date, const std::optional<Date> new_end_date -) : start_date {new_start_date}, end_date {new_end_date} {} +) : start_date { new_start_date }, end_date { new_end_date } {} bool Period::is_date_in_period(const Date date) const { return start_date <= date && (!end_date.has_value() || end_date.value() >= date); diff --git a/src/openvic-simulation/interface/GFX.cpp b/src/openvic-simulation/interface/GFX.cpp index ca31419..ff2737c 100644 --- a/src/openvic-simulation/interface/GFX.cpp +++ b/src/openvic-simulation/interface/GFX.cpp @@ -4,8 +4,11 @@ using namespace OpenVic; using namespace OpenVic::GFX; using namespace OpenVic::NodeTools; -Font::Font(std::string_view new_identifier, colour_argb_t new_colour, std::string_view new_fontname) - : HasIdentifierAndAlphaColour { new_identifier, new_colour, false }, fontname { new_fontname } {} +Font::Font( + std::string_view new_identifier, colour_argb_t new_colour, std::string_view new_fontname, std::string_view new_charset, + uint32_t new_height +) : HasIdentifierAndAlphaColour { new_identifier, new_colour, false }, fontname { new_fontname }, charset { new_charset }, + height { new_height } {} node_callback_t Sprite::expect_sprites(length_callback_t length_callback, callback_t<std::unique_ptr<Sprite>&&> callback) { return expect_dictionary_keys_and_length( @@ -17,8 +20,9 @@ node_callback_t Sprite::expect_sprites(length_callback_t length_callback, callba "textSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback), "maskedShieldType", ZERO_OR_MORE, _expect_instance<Sprite, MaskedFlag>(callback), "tileSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TileTextureSprite>(callback), - // TODO - add the rest of the sprite types - "corneredTileSpriteType", ZERO_OR_MORE, success_callback, + "corneredTileSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, CorneredTileTextureSprite>(callback), + + /* Each only has one vanilla instance which isn't used anywhere. */ "BarChartType", ZERO_OR_MORE, success_callback, "scrollingSprite", ZERO_OR_MORE, success_callback ); @@ -47,8 +51,8 @@ TileTextureSprite::TileTextureSprite() : texture_file {}, size {} {} bool TileTextureSprite::_fill_key_map(case_insensitive_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)), - "size", ZERO_OR_ONE, expect_ivec2(assign_variable_callback(size)), + "texturefile", ONE_EXACTLY, expect_string(assign_variable_callback_string(texture_file)), + "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)), "norefcount", ZERO_OR_ONE, success_callback, "loadType", ZERO_OR_ONE, success_callback @@ -56,7 +60,22 @@ bool TileTextureSprite::_fill_key_map(case_insensitive_key_map_t& key_map) { return ret; } -ProgressBar::ProgressBar() : back_colour {}, progress_colour {} {} +CorneredTileTextureSprite::CorneredTileTextureSprite() : texture_file {}, size {}, border_size {} {} + +bool CorneredTileTextureSprite::_fill_key_map(case_insensitive_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)), + "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)), + "borderSize", ONE_EXACTLY, expect_ivec2(assign_variable_callback(border_size)), + + "allwaystransparent", ZERO_OR_ONE, success_callback, + "loadType", 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(case_insensitive_key_map_t& key_map) { bool ret = Sprite::_fill_key_map(key_map); @@ -90,6 +109,7 @@ bool LineChart::_fill_key_map(case_insensitive_key_map_t& 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; @@ -102,6 +122,7 @@ bool MaskedFlag::_fill_key_map(case_insensitive_key_map_t& key_map) { ret &= add_key_map_entries(key_map, "textureFile1", ONE_EXACTLY, expect_string(assign_variable_callback_string(overlay_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 diff --git a/src/openvic-simulation/interface/GFX.hpp b/src/openvic-simulation/interface/GFX.hpp index 21baa85..108ecb6 100644 --- a/src/openvic-simulation/interface/GFX.hpp +++ b/src/openvic-simulation/interface/GFX.hpp @@ -12,11 +12,16 @@ namespace OpenVic::GFX { friend class OpenVic::UIManager; private: - const std::string PROPERTY(fontname); + std::string PROPERTY(fontname); + std::string PROPERTY(charset); + uint32_t PROPERTY(height); // TODO - colorcodes, effect - Font(std::string_view new_identifier, colour_argb_t new_colour, std::string_view new_fontname); + Font( + std::string_view new_identifier, colour_argb_t new_colour, std::string_view new_fontname, + std::string_view new_charset, uint32_t new_height + ); public: Font(Font&&) = default; @@ -79,6 +84,25 @@ namespace OpenVic::GFX { OV_DETAIL_GET_TYPE }; + class CorneredTileTextureSprite final : public Sprite { + friend std::unique_ptr<CorneredTileTextureSprite> std::make_unique<CorneredTileTextureSprite>(); + + std::string PROPERTY(texture_file); + ivec2_t PROPERTY(size); + ivec2_t PROPERTY(border_size); + + protected: + CorneredTileTextureSprite(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + CorneredTileTextureSprite(CorneredTileTextureSprite&&) = default; + virtual ~CorneredTileTextureSprite() = default; + + OV_DETAIL_GET_TYPE + }; + class ProgressBar final : public Sprite { friend std::unique_ptr<ProgressBar> std::make_unique<ProgressBar>(); diff --git a/src/openvic-simulation/interface/GUI.cpp b/src/openvic-simulation/interface/GUI.cpp index 7aebfe8..94e1fe3 100644 --- a/src/openvic-simulation/interface/GUI.cpp +++ b/src/openvic-simulation/interface/GUI.cpp @@ -6,6 +6,16 @@ using namespace OpenVic; using namespace OpenVic::GUI; using namespace OpenVic::NodeTools; +Position::Position() : position {} {} + +bool Position::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { + bool ret = Named::_fill_key_map(key_map); + ret &= add_key_map_entry(key_map, + "position", ONE_EXACTLY, expect_fvec2(assign_variable_callback(position)) + ); + return ret; +} + Element::Element() : position {}, orientation { orientation_t::UPPER_LEFT } {} bool Element::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) { @@ -14,11 +24,14 @@ bool Element::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIMa 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 } + { "CENTER", CENTER }, { "CENTER_UP", CENTER_UP }, { "CENTER_DOWN", CENTER_DOWN } }; ret &= add_key_map_entries(key_map, - "position", ONE_EXACTLY, expect_fvec2(assign_variable_callback(position)), - "orientation", ZERO_OR_ONE, expect_string(expect_mapped_string(orientation_map, assign_variable_callback(orientation))) + "position", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(position)), + "orientation", ZERO_OR_ONE, expect_identifier_or_string(expect_mapped_string( + orientation_map, assign_variable_callback(orientation), + true /* Warn if the key here is invalid, leaving the default orientation UPPER_LEFT unchanged. */ + )) ); return ret; } @@ -29,22 +42,31 @@ bool Element::_fill_elements_key_map( bool ret = true; ret &= add_key_map_entries(key_map, "iconType", ZERO_OR_MORE, _expect_instance<Element, Icon>(callback, ui_manager), + "shieldtype", 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), + "editBoxType", ZERO_OR_MORE, _expect_instance<Element, TextEditBox>(callback, ui_manager), + "scrollbarType", ZERO_OR_MORE, _expect_instance<Element, Scrollbar>(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 + "eu3dialogtype", ZERO_OR_MORE, _expect_instance<Element, Window>(callback, ui_manager) ); return ret; } bool Scene::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) { - return Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool { + bool ret = Element::_fill_elements_key_map(key_map, [this](std::unique_ptr<Element>&& element) -> bool { return scene_elements.add_item(std::move(element)); }, ui_manager); + ret &= add_key_map_entry(key_map, + "positionType", ZERO_OR_MORE, Position::_expect_value<Position>([this](Position&& position) -> bool { + return scene_positions.add_item(std::move(position)); + }) + ); + return ret; } node_callback_t Scene::expect_scene( @@ -56,7 +78,7 @@ node_callback_t Scene::expect_scene( }, ui_manager); } -Window::Window() : moveable { false }, fullscreen { false } {} +Window::Window() : background {}, size {}, moveable { false }, fullscreen { false } {} bool Window::_fill_key_map(NodeTools::case_insensitive_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 { @@ -64,55 +86,71 @@ bool Window::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIMan }, 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_fvec2(assign_variable_callback(size)), - "moveable", ONE_EXACTLY, expect_int_bool(assign_variable_callback(moveable)), + "moveable", ZERO_OR_ONE, expect_int_bool(assign_variable_callback(moveable)), + "fullScreen", ZERO_OR_ONE, expect_bool(assign_variable_callback(fullscreen)), + "backGround", ZERO_OR_ONE, expect_string(assign_variable_callback_string(background), true), + "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)) + "upsound", ZERO_OR_ONE, success_callback, + "downsound", ZERO_OR_ONE, success_callback ); return ret; } -Icon::Icon() : sprite { nullptr }, frame { GFX::NO_FRAMES } {} +Icon::Icon() : sprite { nullptr }, frame { GFX::NO_FRAMES }, scale { 1 }, rotation { 0 } {} bool Icon::_fill_key_map(NodeTools::case_insensitive_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)) + // TODO - make these share a ONE_EXACTLY count + "spriteType", ZERO_OR_ONE, ui_manager.expect_sprite_string(assign_variable_callback_pointer(sprite)), + "buttonMesh", ZERO_OR_ONE, ui_manager.expect_sprite_string(assign_variable_callback_pointer(sprite)), + + "frame", ZERO_OR_ONE, expect_uint(assign_variable_callback(frame)), + "scale", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(scale)), + "rotation", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(rotation)) ); return ret; } -BaseButton::BaseButton() : sprite { nullptr } {} +BaseButton::BaseButton() : sprite { nullptr }, text {}, font { nullptr } {} bool BaseButton::_fill_key_map(NodeTools::case_insensitive_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, + // TODO - make these share a ONE_EXACTLY count "quadTextureSprite", ZERO_OR_ONE, - expect_string(ui_manager.expect_sprite_str(assign_variable_callback_pointer(sprite)), true), + ui_manager.expect_sprite_string(assign_variable_callback_pointer(sprite), true), "spriteType", ZERO_OR_ONE, - 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?) + ui_manager.expect_sprite_string(assign_variable_callback_pointer(sprite)), + + "buttonText", ZERO_OR_ONE, expect_string(assign_variable_callback_string(text), true), + /* Some buttons have multiple fonts listed with the last one being used. */ + "buttonFont", ZERO_OR_MORE, ui_manager.expect_font_string(assign_variable_callback_pointer(font), true), + + "shortcut", ZERO_OR_ONE, success_callback, // TODO - load and use shortcuts (how to integrate with custom keybinds?) + "tooltip", ZERO_OR_ONE, success_callback, + "tooltipText", ZERO_OR_ONE, success_callback, + "delayedTooltipText", ZERO_OR_ONE, success_callback ); return ret; } -Button::Button() : text {}, font { nullptr} {} +Button::Button() : size {}, rotation { 0 } {} bool Button::_fill_key_map(NodeTools::case_insensitive_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 + "size", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(size)), + "rotation", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(rotation)), + + "format", ZERO_OR_ONE, success_callback, /* Is always left from what I've seen. */ + "clicksound", ZERO_OR_ONE, success_callback, // TODO - clicksound!!! + "parent", ZERO_OR_ONE, success_callback /* Links buttons to a scrollbar, not needed thanks to contextual info. */ ); return ret; } @@ -128,7 +166,7 @@ bool AlignedElement::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_ma 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 } + { "left", left }, { "right", right }, { "centre", centre }, { "center", centre }, { "justified", justified } }; ret &= add_key_map_entries(key_map, "format", ZERO_OR_ONE, expect_identifier(expect_mapped_string(format_map, assign_variable_callback(format)) @@ -136,18 +174,19 @@ bool AlignedElement::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_ma return ret; } -Text::Text() : text {}, font { nullptr } {} +Text::Text() : text {}, font { nullptr }, max_size {} {} bool Text::_fill_key_map(NodeTools::case_insensitive_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_fixed_point(assign_variable_callback(max_size.x)), - "maxHeight", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(max_size.y)), + "font", ONE_EXACTLY, ui_manager.expect_font_string(assign_variable_callback_pointer(font)), + "maxWidth", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(max_size.x)), + "maxHeight", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(max_size.y)), "borderSize", ZERO_OR_ONE, success_callback, "fixedsize", ZERO_OR_ONE, success_callback, + "allwaystransparent", ZERO_OR_ONE, success_callback, // Add warning about redundant key? "textureFile", ZERO_OR_ONE, success_callback @@ -155,7 +194,7 @@ bool Text::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManag return ret; } -OverlappingElementsBox::OverlappingElementsBox() : size {} {} +OverlappingElementsBox::OverlappingElementsBox() : size {}, spacing {} {} bool OverlappingElementsBox::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) { bool ret = AlignedElement::_fill_key_map(key_map, ui_manager); @@ -166,16 +205,117 @@ bool OverlappingElementsBox::_fill_key_map(NodeTools::case_insensitive_key_map_t return ret; } -ListBox::ListBox() : size {} {} +ListBox::ListBox() : size {}, offset {}, spacing {}, scrollbar_name {} {} bool ListBox::_fill_key_map(NodeTools::case_insensitive_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, + "size", ONE_EXACTLY, expect_fvec2(assign_variable_callback(size)), + "offset", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(offset)), + "spacing", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(spacing)), + "scrollbartype", ZERO_OR_ONE, expect_string(assign_variable_callback_string(scrollbar_name)), + "backGround", ZERO_OR_ONE, success_callback, + "borderSize", ZERO_OR_ONE, success_callback, + "horizontal", ZERO_OR_ONE, success_callback, + "priority", ZERO_OR_ONE, success_callback, + "allwaystransparent", ZERO_OR_ONE, success_callback + ); + return ret; +} + +TextEditBox::TextEditBox() : text {}, font { nullptr }, texture_file {}, size {}, border_size {} {} + +bool TextEditBox::_fill_key_map(NodeTools::case_insensitive_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, + "text", ONE_EXACTLY, expect_string(assign_variable_callback_string(text), true), + "font", ONE_EXACTLY, ui_manager.expect_font_string(assign_variable_callback_pointer(font)), + "textureFile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file), true), + "size", ONE_EXACTLY, expect_fvec2(assign_variable_callback(size)), + "borderSize", ONE_EXACTLY, expect_fvec2(assign_variable_callback(border_size)) + ); + return ret; +} + +Scrollbar::Scrollbar() + : slider_button_name {}, track_button_name {}, less_button_name{}, more_button_name {}, size {}, border_size {}, + min_value {}, max_value {}, step_size {}, start_value {}, horizontal { false }, use_range_limit { false }, + range_limit_min {}, range_limit_max {}, range_limit_min_icon_name {}, range_limit_max_icon_name {} { + scrollbar_elements.reserve(4); /* Space for 4 buttons, might need 2 more for range limit icons. */ +} + +bool Scrollbar::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) { + bool ret = Element::_fill_key_map(key_map, ui_manager); + const auto add_element = [this](std::unique_ptr<Element>&& element) -> bool { + return scrollbar_elements.add_item(std::move(element)); + }; + ret &= add_key_map_entries(key_map, + "slider", ONE_EXACTLY, expect_string(assign_variable_callback_string(slider_button_name)), + "track", ONE_EXACTLY, expect_string(assign_variable_callback_string(track_button_name)), + "leftbutton", ONE_EXACTLY, expect_string(assign_variable_callback_string(less_button_name)), + "rightbutton", ONE_EXACTLY, expect_string(assign_variable_callback_string(more_button_name)), "size", ONE_EXACTLY, expect_fvec2(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 + "borderSize", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(border_size)), + "minValue", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(min_value)), + "maxValue", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(max_value)), + "stepSize", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(step_size)), + "startValue", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(start_value)), + "horizontal", ONE_EXACTLY, expect_int_bool(assign_variable_callback(horizontal)), + "useRangeLimit", ZERO_OR_ONE, expect_bool(assign_variable_callback(use_range_limit)), + "rangeLimitMin", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(range_limit_min)), + "rangeLimitMax", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(range_limit_max)), + "rangeLimitMinIcon", ZERO_OR_ONE, expect_string(assign_variable_callback_string(range_limit_min_icon_name)), + "rangeLimitMaxIcon", ZERO_OR_ONE, expect_string(assign_variable_callback_string(range_limit_max_icon_name)), + + "guiButtonType", ONE_OR_MORE, _expect_instance<Element, Button>(add_element, ui_manager), + "iconType", ZERO_OR_MORE, _expect_instance<Element, Icon>(add_element, ui_manager), + + "priority", ZERO_OR_ONE, success_callback ); return ret; } + +template<std::derived_from<Element> T> +T const* Scrollbar::get_element(std::string_view name, std::string_view type) const { + Element const* element = scrollbar_elements.get_item_by_identifier(name); + if (element != nullptr) { + T const* cast_element = element->cast_to<T>(); + if (cast_element != nullptr) { + return cast_element; + } else { + Logger::error( + "GUI Scrollbar ", get_name(), " ", type, " element ", name, " has wrong type: ", element->get_type(), + " (expected ", T::get_type_static(), ")" + ); + return nullptr; + } + } else { + Logger::error("GUI Scrollbar ", get_name(), " has no ", type, " element named ", name, "!"); + return nullptr; + } +} + +Button const* Scrollbar::get_slider_button() const { + return get_element<Button>(slider_button_name, "slider button"); +} + +Button const* Scrollbar::get_track_button() const { + return get_element<Button>(track_button_name, "track button"); +} + +Button const* Scrollbar::get_less_button() const { + return get_element<Button>(less_button_name, "less button"); +} + +Button const* Scrollbar::get_more_button() const { + return get_element<Button>(more_button_name, "more button"); +} + +Icon const* Scrollbar::get_range_limit_min_icon() const { + return get_element<Icon>(range_limit_min_icon_name, "range limit min icon"); +} + +Icon const* Scrollbar::get_range_limit_max_icon() const { + return get_element<Icon>(range_limit_max_icon_name, "range limit max icon"); +} diff --git a/src/openvic-simulation/interface/GUI.hpp b/src/openvic-simulation/interface/GUI.hpp index 96bb2a2..f044f7d 100644 --- a/src/openvic-simulation/interface/GUI.hpp +++ b/src/openvic-simulation/interface/GUI.hpp @@ -8,12 +8,27 @@ namespace OpenVic { namespace OpenVic::GUI { class Scene; + class Position final : public Named<> { + friend class LoadBase; + + fvec2_t PROPERTY(position); + + protected: + Position(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + Position(Position&&) = default; + virtual ~Position() = default; + }; + class Element : public Named<UIManager const&> { friend class Scene; public: enum class orientation_t { - UPPER_LEFT, LOWER_LEFT, LOWER_RIGHT, UPPER_RIGHT, CENTER + UPPER_LEFT, LOWER_LEFT, LOWER_RIGHT, UPPER_RIGHT, CENTER, CENTER_UP, CENTER_DOWN }; private: @@ -37,10 +52,13 @@ namespace OpenVic::GUI { OV_DETAIL_GET_TYPE }; + using element_instance_registry_t = NamedInstanceRegistry<Element, UIManager const&>; + class Scene : public Named<UIManager const&> { friend std::unique_ptr<Scene> std::make_unique<Scene>(); - NamedInstanceRegistry<Element, UIManager const&> IDENTIFIER_REGISTRY(scene_element); + element_instance_registry_t IDENTIFIER_REGISTRY(scene_element); + NamedRegistry<Position> IDENTIFIER_REGISTRY(scene_position); protected: Scene() = default; @@ -62,12 +80,14 @@ namespace OpenVic::GUI { class Window final : public Element { friend std::unique_ptr<Window> std::make_unique<Window>(); - NamedInstanceRegistry<Element, UIManager const&> IDENTIFIER_REGISTRY(window_element); + element_instance_registry_t IDENTIFIER_REGISTRY(window_element); + std::string PROPERTY(background); /* The name of a child button who's sprite is used as the background. */ fvec2_t PROPERTY(size); bool PROPERTY(moveable); bool PROPERTY(fullscreen); - // TODO - background, dontRender, horizontalBorder, verticalBorder + + // TODO - dontRender, horizontalBorder, verticalBorder protected: Window(); @@ -86,6 +106,8 @@ namespace OpenVic::GUI { GFX::Sprite const* PROPERTY(sprite); GFX::frame_t PROPERTY(frame); + fixed_point_t PROPERTY(scale); + fixed_point_t PROPERTY(rotation); /* In radians, usually one of 0, PI/2 or -PI/2. */ protected: Icon(); @@ -101,6 +123,9 @@ namespace OpenVic::GUI { class BaseButton : public Element { GFX::Sprite const* PROPERTY(sprite); + std::string PROPERTY(text); + GFX::Font const* PROPERTY(font); + // TODO - shortcut protected: @@ -118,8 +143,8 @@ namespace OpenVic::GUI { class Button final : public BaseButton { friend std::unique_ptr<Button> std::make_unique<Button>(); - std::string PROPERTY(text); - GFX::Font const* PROPERTY(font); + fvec2_t PROPERTY(size); + fixed_point_t PROPERTY(rotation); /* In radians, usually one of 0, PI/2 or -PI/2. */ // TODO - clicksound @@ -153,7 +178,7 @@ namespace OpenVic::GUI { class AlignedElement : public Element { public: enum class format_t { - left, centre, right + left, centre, right, justified }; private: @@ -176,7 +201,7 @@ namespace OpenVic::GUI { std::string PROPERTY(text); GFX::Font const* PROPERTY(font); - fvec2_t PROPERTY(max_size); // maxWidth, maxHeight + fvec2_t PROPERTY(max_size); /* Defines keys: maxWidth, maxHeight */ // TODO - borderSize, fixedsize, textureFile @@ -214,8 +239,11 @@ namespace OpenVic::GUI { friend std::unique_ptr<ListBox> std::make_unique<ListBox>(); fvec2_t PROPERTY(size); + fvec2_t PROPERTY(offset); + fixed_point_t PROPERTY(spacing); + std::string PROPERTY(scrollbar_name); /* In vanilla this is always core's standardlistbox_slider */ - // TODO - backGround, spacing, scrollbartype, borderSize + // TODO - backGround, borderSize protected: ListBox(); @@ -228,4 +256,72 @@ namespace OpenVic::GUI { OV_DETAIL_GET_TYPE }; + + class TextEditBox final : public Element { + friend std::unique_ptr<TextEditBox> std::make_unique<TextEditBox>(); + + std::string PROPERTY(text); + GFX::Font const* PROPERTY(font); + std::string PROPERTY(texture_file); + fvec2_t PROPERTY(size); + fvec2_t PROPERTY(border_size); + + protected: + TextEditBox(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) override; + + public: + TextEditBox(TextEditBox&&) = default; + virtual ~TextEditBox() = default; + + OV_DETAIL_GET_TYPE + }; + + class Scrollbar final : public Element { + friend std::unique_ptr<Scrollbar> std::make_unique<Scrollbar>(); + + element_instance_registry_t IDENTIFIER_REGISTRY(scrollbar_element); + + std::string PROPERTY(slider_button_name); + std::string PROPERTY(track_button_name); + std::string PROPERTY(less_button_name); + std::string PROPERTY(more_button_name); + + fvec2_t PROPERTY(size); + fvec2_t PROPERTY(border_size); + fixed_point_t PROPERTY(min_value); + fixed_point_t PROPERTY(max_value); + fixed_point_t PROPERTY(step_size); + fixed_point_t PROPERTY(start_value); + bool PROPERTY_CUSTOM_PREFIX(horizontal, is) + + bool PROPERTY(use_range_limit); + fixed_point_t PROPERTY(range_limit_min); + fixed_point_t PROPERTY(range_limit_max); + std::string PROPERTY(range_limit_min_icon_name); + std::string PROPERTY(range_limit_max_icon_name); + + template<std::derived_from<Element> T> + T const* get_element(std::string_view name, std::string_view type) const; + + protected: + Scrollbar(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIManager const& ui_manager) override; + + public: + Scrollbar(Scrollbar&&) = default; + virtual ~Scrollbar() = default; + + Button const* get_slider_button() const; + Button const* get_track_button() const; + Button const* get_less_button() const; + Button const* get_more_button() const; + + Icon const* get_range_limit_min_icon() const; + Icon const* get_range_limit_max_icon() const; + + OV_DETAIL_GET_TYPE + }; } diff --git a/src/openvic-simulation/interface/LoadBase.hpp b/src/openvic-simulation/interface/LoadBase.hpp index 10b0169..29de165 100644 --- a/src/openvic-simulation/interface/LoadBase.hpp +++ b/src/openvic-simulation/interface/LoadBase.hpp @@ -23,6 +23,18 @@ namespace OpenVic { return ret; } + template<std::derived_from<LoadBase<Context...>> T> + static NodeTools::node_callback_t _expect_value( + NodeTools::callback_t<T&&> callback, Context... context + ) { + return [callback, &context...](ast::NodeCPtr node) -> bool { + T value {}; + bool ret = value.load(node, context...); + ret &= callback(std::move(value)); + 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 diff --git a/src/openvic-simulation/interface/UI.cpp b/src/openvic-simulation/interface/UI.cpp index b977406..2646bb7 100644 --- a/src/openvic-simulation/interface/UI.cpp +++ b/src/openvic-simulation/interface/UI.cpp @@ -7,7 +7,9 @@ using namespace OpenVic::NodeTools; using namespace OpenVic::GFX; using namespace OpenVic::GUI; -bool UIManager::add_font(std::string_view identifier, colour_argb_t colour, std::string_view fontname) { +bool UIManager::add_font( + std::string_view identifier, colour_argb_t colour, std::string_view fontname, std::string_view charset, uint32_t height +) { if (identifier.empty()) { Logger::error("Invalid font identifier - empty!"); return false; @@ -20,23 +22,39 @@ bool UIManager::add_font(std::string_view identifier, colour_argb_t colour, std: Logger::error("Invalid fontname for font ", identifier, " - empty!"); return false; } - return fonts.add_item({ identifier, colour, fontname }, duplicate_warning_callback); + return fonts.add_item({ identifier, colour, fontname, charset, height }, duplicate_warning_callback); } bool UIManager::_load_font(ast::NodeCPtr node) { - std::string_view identifier, fontname; + std::string_view identifier, fontname, charset; colour_argb_t colour = colour_argb_t::null(); + uint32_t height = 0; 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)), + "charset", ZERO_OR_ONE, expect_string(assign_variable_callback(charset)), + "height", ZERO_OR_ONE, expect_uint(assign_variable_callback(height)), "colorcodes", ZERO_OR_ONE, success_callback, "effect", ZERO_OR_ONE, success_callback )(node); - ret &= add_font(identifier, colour, fontname); + ret &= add_font(identifier, colour, fontname, charset, height); return ret; } +NodeCallback auto UIManager::_load_fonts(std::string_view font_key) { + return expect_dictionary_reserve_length( + fonts, + [this, font_key](std::string_view key, ast::NodeCPtr node) -> bool { + if (key != font_key) { + Logger::error("Invalid key: \"", key, "\" (expected ", font_key, ")"); + return false; + } + return _load_font(node); + } + ); +} + bool UIManager::load_gfx_file(ast::NodeCPtr root) { return expect_dictionary_keys( "spriteTypes", ZERO_OR_ONE, Sprite::expect_sprites( @@ -65,19 +83,10 @@ bool UIManager::load_gfx_file(ast::NodeCPtr root) { return sprites.add_item(std::move(sprite), duplicate_warning_callback); } ), - "bitmapfonts", ZERO_OR_ONE, expect_dictionary_reserve_length( - fonts, - [this](std::string_view key, ast::NodeCPtr node) -> bool { - if (key != "bitmapfont") { - Logger::error("Invalid bitmapfonts key: ", key); - return false; - } - return _load_font(node); - } - ), + "bitmapfonts", ZERO_OR_ONE, _load_fonts("bitmapfont"), + "fonts", ZERO_OR_ONE, _load_fonts("font"), "objectTypes", ZERO_OR_ONE, success_callback, - "lightTypes", ZERO_OR_ONE, success_callback, - "fonts", ZERO_OR_ONE, success_callback + "lightTypes", ZERO_OR_ONE, success_callback )(root); } diff --git a/src/openvic-simulation/interface/UI.hpp b/src/openvic-simulation/interface/UI.hpp index 286e4f7..c8ffa98 100644 --- a/src/openvic-simulation/interface/UI.hpp +++ b/src/openvic-simulation/interface/UI.hpp @@ -10,9 +10,13 @@ namespace OpenVic { IdentifierRegistry<GFX::Font> IDENTIFIER_REGISTRY(font); bool _load_font(ast::NodeCPtr node); + NodeTools::NodeCallback auto _load_fonts(std::string_view font_key); public: - bool add_font(std::string_view identifier, colour_argb_t colour, std::string_view fontname); + bool add_font( + std::string_view identifier, colour_argb_t colour, std::string_view fontname, std::string_view charset, + uint32_t height + ); 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/Map.cpp b/src/openvic-simulation/map/Map.cpp index 9a97143..a5cd6a6 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -80,7 +80,7 @@ Province::distance_t Map::calculate_distance_between(Province const& from, Provi ) ); - return fvec2_t { min_x, to_pos.y - from_pos.y}.length_squared().sqrt(); + return fvec2_t { min_x, to_pos.y - from_pos.y }.length_squared().sqrt(); } using adjacency_t = Province::adjacency_t; diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index 14640ec..4e2693d 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -80,7 +80,7 @@ bool DeploymentManager::load_oob_file( bool ret = expect_dictionary_keys( "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(leader_name)), - "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(leader_date))), + "date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(leader_date)), "type", ONE_EXACTLY, UnitManager::expect_branch_identifier(assign_variable_callback(leader_branch)), "personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager().expect_leader_trait_identifier_or_string( diff --git a/src/openvic-simulation/misc/Define.cpp b/src/openvic-simulation/misc/Define.cpp index e4e6b52..9ec5d49 100644 --- a/src/openvic-simulation/misc/Define.cpp +++ b/src/openvic-simulation/misc/Define.cpp @@ -95,9 +95,7 @@ bool DefineManager::load_defines_file(ast::NodeCPtr root) { } else if (key == "start_date" || key == "end_date") { - return expect_identifier_or_string(expect_date_str( - std::bind_front(&DefineManager::add_date_define, this, key) - ))(value); + return expect_date_identifier_or_string(std::bind_front(&DefineManager::add_date_define, this, key))(value); } else { return false; diff --git a/src/openvic-simulation/scripts/Condition.cpp b/src/openvic-simulation/scripts/Condition.cpp index 045649b..aab8d93 100644 --- a/src/openvic-simulation/scripts/Condition.cpp +++ b/src/openvic-simulation/scripts/Condition.cpp @@ -620,7 +620,7 @@ node_callback_t ConditionManager::expect_condition_node( } else if (identifier == "relation") { expect_pair("who", "value"); // { who = [tag/this/from] value = x } } else if (identifier == "unemployment_by_type") { - expect_pair("type", "value"); // {type = [poptype] value = x } + expect_pair("type", "value"); // { type = [poptype] value = x } } else if (identifier == "upper_house") { expect_pair("ideology", "value"); // { ideology = name value = 0.x } } else if (identifier == "work_available") { diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp index 06a6b36..304d9de 100644 --- a/src/openvic-simulation/types/Colour.hpp +++ b/src/openvic-simulation/types/Colour.hpp @@ -12,7 +12,6 @@ #include <type_traits> #include <utility> -#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Utility.hpp" namespace OpenVic { @@ -105,7 +104,10 @@ namespace OpenVic { /* Colour represented by an unsigned integer, either 24-bit RGB or 32-bit ARGB. */ template<typename ValueT, typename ColourIntT, typename ColourTraits = colour_traits<ValueT, ColourIntT>> - struct basic_colour_t : ReturnByValueProperty { + struct basic_colour_t { + /* PROPERTY generated getter functions will return colours by value, rather than const reference. */ + using ov_return_by_value = void; + using colour_traits = ColourTraits; using value_type = typename colour_traits::value_type; using integer_type = typename colour_traits::integer_type; diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp index 9178e6e..9376293 100644 --- a/src/openvic-simulation/types/Date.hpp +++ b/src/openvic-simulation/types/Date.hpp @@ -1,16 +1,19 @@ #pragma once #include <algorithm> +#include <array> #include <cstdint> #include <ostream> #include <string> -#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Utility.hpp" namespace OpenVic { // A relative period between points in time, measured in days - struct Timespan : ReturnByValueProperty { + struct Timespan { + /* PROPERTY generated getter functions will return timespans by value, rather than const reference. */ + using ov_return_by_value = void; + using day_t = int64_t; private: @@ -86,7 +89,10 @@ namespace OpenVic { // Represents an in-game date // Note: Current implementation does not account for leap-years, or dates before Year 0 - struct Date : ReturnByValueProperty { + struct Date { + /* PROPERTY generated getter functions will return dates by value, rather than const reference. */ + using ov_return_by_value = void; + using year_t = uint16_t; using month_t = uint8_t; using day_t = uint8_t; diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index 7e6fdb3..f1edc95 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -269,30 +269,39 @@ namespace OpenVic { } \ } \ constexpr NodeTools::Callback<std::string_view> auto expect_item_str( \ - NodeTools::Callback<external_value_type CONST&> auto callback, bool warn \ + NodeTools::Callback<external_value_type CONST&> auto callback, bool allow_empty, bool warn \ ) CONST { \ - return [this, callback, warn](std::string_view identifier) -> bool { \ + return [this, callback, allow_empty, warn](std::string_view identifier) -> bool { \ + if (identifier.empty()) { \ + if (allow_empty) { \ + return true; \ + } else { \ + Logger::warn_or_error(warn, "Invalid ", name, " identifier: empty!"); \ + return warn; \ + } \ + } \ external_value_type CONST* item = get_item_by_identifier(identifier); \ if (item != nullptr) { \ return callback(*item); \ } \ - return NodeTools::warn_or_error(warn, "Invalid ", name, " identifier: ", identifier); \ + Logger::warn_or_error(warn, "Invalid ", name, " identifier: ", identifier); \ + return warn; \ }; \ } \ constexpr NodeTools::NodeCallback auto expect_item_identifier( \ NodeTools::Callback<external_value_type CONST&> auto callback, bool warn \ ) CONST { \ - return NodeTools::expect_identifier(expect_item_str(callback, warn)); \ + return NodeTools::expect_identifier(expect_item_str(callback, false, warn)); \ } \ constexpr NodeTools::NodeCallback auto expect_item_string( \ - NodeTools::Callback<external_value_type CONST&> auto callback, bool warn \ + NodeTools::Callback<external_value_type CONST&> auto callback, bool allow_empty, bool warn \ ) CONST { \ - return NodeTools::expect_string(expect_item_str(callback, warn)); \ + return NodeTools::expect_string(expect_item_str(callback, allow_empty, warn), allow_empty); \ } \ constexpr NodeTools::NodeCallback auto expect_item_identifier_or_string( \ - NodeTools::Callback<external_value_type CONST&> auto callback, bool warn \ + NodeTools::Callback<external_value_type CONST&> auto callback, bool allow_empty, bool warn \ ) CONST { \ - return NodeTools::expect_identifier_or_string(expect_item_str(callback, warn)); \ + return NodeTools::expect_identifier_or_string(expect_item_str(callback, allow_empty, warn), allow_empty); \ } \ constexpr NodeTools::NodeCallback auto expect_item_assign_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ @@ -541,9 +550,10 @@ private: return registry.get_items(); \ } \ constexpr NodeTools::Callback<std::string_view> auto expect_##singular##_str( \ - NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool warn = false \ + NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool allow_empty = false, \ + bool warn = false \ ) const_kw { \ - return registry.expect_item_str(callback, warn); \ + return registry.expect_item_str(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_identifier( \ NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool warn = false \ @@ -551,14 +561,16 @@ private: return registry.expect_item_identifier(callback, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_string( \ - NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool warn = false \ + NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool allow_empty = false, \ + bool warn = false \ ) const_kw { \ - return registry.expect_item_string(callback, warn); \ + return registry.expect_item_string(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_identifier_or_string( \ - NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback, bool warn = false \ + NodeTools::Callback<decltype(registry)::external_value_type const_kw&> auto callback,bool allow_empty = false, \ + bool warn = false \ ) const_kw { \ - return registry.expect_item_identifier_or_string(callback, warn); \ + return registry.expect_item_identifier_or_string(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_assign_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ diff --git a/src/openvic-simulation/types/Vector.hpp b/src/openvic-simulation/types/Vector.hpp index 5514cc3..6897835 100644 --- a/src/openvic-simulation/types/Vector.hpp +++ b/src/openvic-simulation/types/Vector.hpp @@ -6,6 +6,9 @@ namespace OpenVic { template<typename T> struct vec2_t { + /* PROPERTY generated getter functions will return 2D vectors by value, rather than const reference. */ + using ov_return_by_value = void; + using type = T; T x, y; diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 8d3a74b..6a47194 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -1,22 +1,21 @@ #pragma once -#include <cerrno> -#include <cmath> #include <cstdint> #include <cstdlib> #include <limits> #include <sstream> #include <string_view> -#include "openvic-simulation/utility/Getters.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointLUT.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/NumberUtils.hpp" #include "openvic-simulation/utility/StringUtils.hpp" -#include "FixedPointLUT.hpp" - namespace OpenVic { - struct fixed_point_t : ReturnByValueProperty { + struct fixed_point_t { + /* PROPERTY generated getter functions will return fixed points by value, rather than const reference. */ + using ov_return_by_value = void; + static constexpr size_t SIZE = 8; static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION; @@ -27,7 +26,7 @@ namespace OpenVic { constexpr fixed_point_t(int32_t new_value) : value { static_cast<int64_t>(new_value) << PRECISION } {} // Trivial destructor - ~fixed_point_t() = default; + constexpr ~fixed_point_t() = default; static constexpr fixed_point_t max() { return std::numeric_limits<int64_t>::max(); diff --git a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp b/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp index 45cb639..a5d585f 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp @@ -1,15 +1,10 @@ #pragma once -#include <array> -#include <cmath> -#include <cstddef> #include <cstdint> -#include <numbers> -#include <utility> namespace OpenVic::FPLUT { -#include "FixedPointLUT_sin.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp" constexpr int32_t SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2; diff --git a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp b/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp index c0b5f42..6cdb926 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp @@ -1,6 +1,6 @@ #pragma once -#include "FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" namespace OpenVic::FPMath { constexpr fixed_point_t sin(fixed_point_t number) { diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp index 33aa5a2..fa76e74 100644 --- a/src/openvic-simulation/utility/Getters.hpp +++ b/src/openvic-simulation/utility/Getters.hpp @@ -51,13 +51,15 @@ public: \ ACCESS: namespace OpenVic { - /* Any struct extending ReturnByValueProperty will be returned by value by PROPERTY-generated getter functions, + /* Any struct tagged with ov_return_by_value will be returned by value by PROPERTY-generated getter functions, * instead of by const reference as structs are by default. Use this for small structs which don't contain any - * dynamically allocated memory, e.g. Date and fixed_point_t. */ - struct ReturnByValueProperty { - constexpr bool operator==(ReturnByValueProperty const&) const = default; - constexpr std::strong_ordering operator<=>(ReturnByValueProperty const&) const = default; - }; + * dynamically allocated memory, e.g. dates and colours. The tag must be public, as in the example below: + * + * public: + * using ov_return_by_value = void; + */ + template<typename T> + concept ReturnByValue = requires { typename T::ov_return_by_value; }; /* * Template function used to choose the return type and provide the implementation @@ -72,7 +74,7 @@ namespace OpenVic { /* Return std::string_view looking at std::string */ return std::string_view { property }; } else if constexpr ( - std::integral<T> || std::floating_point<T> || std::is_enum_v<T> || std::derived_from<T, ReturnByValueProperty> + std::integral<T> || std::floating_point<T> || std::is_enum_v<T> || ReturnByValue<T> ) { /* Return value */ return T { property }; diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp index 53decb3..7a2c3d0 100644 --- a/src/openvic-simulation/utility/Logger.hpp +++ b/src/openvic-simulation/utility/Logger.hpp @@ -75,16 +75,16 @@ namespace OpenVic { size_t message_count; }; - template<typename... Ts> + template<typename... Args> struct log { - log(log_channel_t& log_channel, Ts&&... ts, source_location const& location) { + log(log_channel_t& log_channel, Args&&... args, source_location const& location) { std::stringstream stream; stream << StringUtils::get_filename(location.file_name()) << "(" /* Function name removed to reduce clutter. It is already included * in Godot's print functions, so this was repeating it. */ //<< location.line() << ") `" << location.function_name() << "`: "; << location.line() << "): "; - ((stream << std::forward<Ts>(ts)), ...); + ((stream << std::forward<Args>(args)), ...); stream << std::endl; log_channel.queue.push(stream.str()); if (log_channel.func) { @@ -109,19 +109,28 @@ public: \ static inline size_t get_##name##_count() { \ return name##_channel.message_count; \ } \ - template<typename... Ts> \ + template<typename... Args> \ struct name { \ - name(Ts&&... ts, source_location const& location = source_location::current()) { \ - log<Ts...> { name##_channel, std::forward<Ts>(ts)..., location }; \ + name(Args&&... args, source_location const& location = source_location::current()) { \ + log<Args...> { name##_channel, std::forward<Args>(args)..., location }; \ } \ }; \ - template<typename... Ts> \ - name(Ts&&...) -> name<Ts...>; + template<typename... Args> \ + name(Args&&...) -> name<Args...>; LOG_FUNC(info) LOG_FUNC(warning) LOG_FUNC(error) #undef LOG_FUNC + + template<typename... Args> + static inline constexpr void warn_or_error(bool warn, Args&&... args) { + if (warn) { + warning(std::forward<Args>(args)...); + } else { + error(std::forward<Args>(args)...); + } + } }; } |