diff --git a/resources/dashboards/default_boat-mini.png b/resources/dashboards/default_boat-mini.png new file mode 100644 index 0000000000..3abf76ebb8 Binary files /dev/null and b/resources/dashboards/default_boat-mini.png differ diff --git a/resources/dashboards/default_boat.dashboard b/resources/dashboards/default_boat.dashboard new file mode 100644 index 0000000000..c4f960eb9f --- /dev/null +++ b/resources/dashboards/default_boat.dashboard @@ -0,0 +1,19 @@ +; A dashboard mod MUST contain a '.dashboard' file, even if just empty. +; Recognized parameters: +; * dashboard_name +; * dashboard_description +; * dashboard_author +; * dashboard_category +; +; Custom fonts are supported via .resource files (XML, same syntax as MyGui_FontsDash.xml in 'dashboards.zip'). +; +; All layout+resource files must start the same as the .dashboard file. +; For example: foo.dashboard -> foo_dashboard.layout, foo_dashboard.resource +; Optional layout filename tags: +; * '_Nrpm' ~ N is the redline RPM +; * '_Xph' ~ X is units (k=Kilometers | m=Miles) +; ---------------------------------------------------------------------------------- + +dashboard_name "Default - Boat" +dashboard_description "Default dashboard for boats" +dashboard_category 202 diff --git a/resources/dashboards/default_dashboard_boat.layout b/resources/dashboards/default_boat.layout similarity index 98% rename from resources/dashboards/default_dashboard_boat.layout rename to resources/dashboards/default_boat.layout index 0593efb828..546df1e8e0 100644 --- a/resources/dashboards/default_dashboard_boat.layout +++ b/resources/dashboards/default_boat.layout @@ -1,70 +1,70 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dashboards/default_dashboard.layout b/resources/dashboards/default_dashboard.layout deleted file mode 100644 index aeef717f55..0000000000 --- a/resources/dashboards/default_dashboard.layout +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/dashboards/default_truck_analog-mini.png b/resources/dashboards/default_truck_analog-mini.png new file mode 100644 index 0000000000..2bb732b7eb Binary files /dev/null and b/resources/dashboards/default_truck_analog-mini.png differ diff --git a/resources/dashboards/default_truck_analog.dashboard b/resources/dashboards/default_truck_analog.dashboard new file mode 100644 index 0000000000..e1d4b08103 --- /dev/null +++ b/resources/dashboards/default_truck_analog.dashboard @@ -0,0 +1,20 @@ +; A dashboard mod MUST contain a '.dashboard' file, even if just empty. +; Recognized parameters: +; * dashboard_name +; * dashboard_description +; * dashboard_author +; * dashboard_category +; +; Custom fonts are supported via .resource files (XML, same syntax as MyGui_FontsDash.xml in 'dashboards.zip'). +; +; All layout+resource files must start the same as the .dashboard file. +; For example: foo.dashboard -> foo_dashboard.layout, foo_dashboard.resource +; Optional layout filename tags: +; * '_Nrpm' ~ N is the redline RPM +; * '_Xph' ~ X is units (k=Kilometers | m=Miles) +; ---------------------------------------------------------------------------------- + +dashboard_name "Default - Truck analog" +dashboard_description "Default dashboard with analog speedometer" +dashboard_author "7000 RPM tacho" -1 "Klink" +dashboard_category 201 \ No newline at end of file diff --git a/resources/dashboards/default_dashboard3500_analog.layout b/resources/dashboards/default_truck_analog_3500rpm_kph.layout similarity index 98% rename from resources/dashboards/default_dashboard3500_analog.layout rename to resources/dashboards/default_truck_analog_3500rpm_kph.layout index 9fc7679075..ff177261a3 100644 --- a/resources/dashboards/default_dashboard3500_analog.layout +++ b/resources/dashboards/default_truck_analog_3500rpm_kph.layout @@ -1,204 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dashboards/default_dashboard3500_analog_mph.layout b/resources/dashboards/default_truck_analog_3500rpm_mph.layout similarity index 98% rename from resources/dashboards/default_dashboard3500_analog_mph.layout rename to resources/dashboards/default_truck_analog_3500rpm_mph.layout index af89a30cf3..09947bc17c 100644 --- a/resources/dashboards/default_dashboard3500_analog_mph.layout +++ b/resources/dashboards/default_truck_analog_3500rpm_mph.layout @@ -1,204 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dashboards/default_dashboard7000_analog.layout b/resources/dashboards/default_truck_analog_7000rpm_kph.layout similarity index 98% rename from resources/dashboards/default_dashboard7000_analog.layout rename to resources/dashboards/default_truck_analog_7000rpm_kph.layout index 361e5a6c3a..60542bc7a1 100644 --- a/resources/dashboards/default_dashboard7000_analog.layout +++ b/resources/dashboards/default_truck_analog_7000rpm_kph.layout @@ -1,204 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dashboards/default_dashboard7000_analog_mph.layout b/resources/dashboards/default_truck_analog_7000rpm_mph.layout similarity index 98% rename from resources/dashboards/default_dashboard7000_analog_mph.layout rename to resources/dashboards/default_truck_analog_7000rpm_mph.layout index 8c930bd88d..0ac224c85e 100644 --- a/resources/dashboards/default_dashboard7000_analog_mph.layout +++ b/resources/dashboards/default_truck_analog_7000rpm_mph.layout @@ -1,204 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/dashboards/default_truck_digital-mini.png b/resources/dashboards/default_truck_digital-mini.png new file mode 100644 index 0000000000..1fed57db4b Binary files /dev/null and b/resources/dashboards/default_truck_digital-mini.png differ diff --git a/resources/dashboards/default_truck_digital.dashboard b/resources/dashboards/default_truck_digital.dashboard new file mode 100644 index 0000000000..d04f5aeb45 --- /dev/null +++ b/resources/dashboards/default_truck_digital.dashboard @@ -0,0 +1,20 @@ +; A dashboard mod MUST contain a '.dashboard' file, even if just empty. +; Recognized parameters: +; * dashboard_name +; * dashboard_description +; * dashboard_author +; * dashboard_category +; +; Custom fonts are supported via .resource files (XML, same syntax as MyGui_FontsDash.xml in 'dashboards.zip'). +; +; All layout+resource files must start the same as the .dashboard file. +; For example: foo.dashboard -> foo_dashboard.layout, foo_dashboard.resource +; Optional layout filename tags: +; * '_Nrpm' ~ N is the redline RPM +; * '_Xph' ~ X is units (k=Kilometers | m=Miles) +; ---------------------------------------------------------------------------------- + +dashboard_name "Default - Truck digital" +dashboard_description "Default dashboard with digital speedometer" +dashboard_author "7000 RPM tacho" -1 "Klink" +dashboard_category 201 \ No newline at end of file diff --git a/resources/dashboards/default_dashboard3500.layout b/resources/dashboards/default_truck_digital_3500rpm_kph.layout similarity index 100% rename from resources/dashboards/default_dashboard3500.layout rename to resources/dashboards/default_truck_digital_3500rpm_kph.layout diff --git a/resources/dashboards/default_dashboard3500_mph.layout b/resources/dashboards/default_truck_digital_3500rpm_mph.layout similarity index 100% rename from resources/dashboards/default_dashboard3500_mph.layout rename to resources/dashboards/default_truck_digital_3500rpm_mph.layout diff --git a/resources/dashboards/default_dashboard7000.layout b/resources/dashboards/default_truck_digital_7000rpm_kph.layout similarity index 100% rename from resources/dashboards/default_dashboard7000.layout rename to resources/dashboards/default_truck_digital_7000rpm_kph.layout diff --git a/resources/dashboards/default_dashboard7000_mph.layout b/resources/dashboards/default_truck_digital_7000rpm_mph.layout similarity index 100% rename from resources/dashboards/default_dashboard7000_mph.layout rename to resources/dashboards/default_truck_digital_7000rpm_mph.layout diff --git a/source/main/Application.cpp b/source/main/Application.cpp index b6745b0bb2..53dd51f0a4 100644 --- a/source/main/Application.cpp +++ b/source/main/Application.cpp @@ -242,7 +242,6 @@ CVar* gfx_fov_internal_default; CVar* gfx_static_cam_fov_exp; CVar* gfx_fixed_cam_tracking; CVar* gfx_fps_limit; -CVar* gfx_speedo_digital; CVar* gfx_speedo_imperial; CVar* gfx_flexbody_cache; CVar* gfx_reduce_shadows; @@ -264,6 +263,8 @@ CVar* ui_show_live_repair_controls; CVar* ui_show_vehicle_buttons; CVar* ui_preset; CVar* ui_hide_gui; +CVar* ui_default_truck_dash; +CVar* ui_default_boat_dash; // Instance access AppContext* GetAppContext () { return &g_app_context; }; diff --git a/source/main/Application.h b/source/main/Application.h index a175801eaa..62bc440014 100644 --- a/source/main/Application.h +++ b/source/main/Application.h @@ -321,6 +321,26 @@ enum LoaderType //!< Search mode for `ModCache::Query()` & Operation mode for `G LT_AddonPart, // No script alias, invoked manually, ext: addonpart LT_Tuneup, // No script alias, invoked manually, ext: tuneup LT_AssetPack, // No script alias, invoked manually, ext: assetpack + LT_DashBoard, // No script alias, invoked manually, ext: dashboard +}; + +enum CacheCategoryId +{ + CID_None = 0, + + CID_DashboardsGeneric = 200, + CID_DashboardsTruck = 201, + CID_DashboardsBoat = 202, + + CID_Projects = 8000, //!< For truck files under 'projects/' directory, to allow listing from editors. + CID_Tuneups = 8001, //!< For unsorted tuneup files. + + CID_Max = 9000, //!< SPECIAL VALUE - Maximum allowed to be present in any mod files. + CID_Unsorted = 9990, + CID_All = 9991, + CID_Fresh = 9992, + CID_Hidden = 9993, + CID_SearchResults = 9994, }; enum class TObjSpecialObject @@ -518,7 +538,6 @@ extern CVar* gfx_fov_internal_default; extern CVar* gfx_static_cam_fov_exp; extern CVar* gfx_fixed_cam_tracking; extern CVar* gfx_fps_limit; -extern CVar* gfx_speedo_digital; extern CVar* gfx_speedo_imperial; extern CVar* gfx_flexbody_cache; extern CVar* gfx_reduce_shadows; @@ -538,8 +557,10 @@ extern CVar* flexbody_defrag_invert_lookup; // GUI extern CVar* ui_show_live_repair_controls; //!< bool extern CVar* ui_show_vehicle_buttons; -extern CVar* ui_preset; //!< enum `RoR::UiPreset` -extern CVar* ui_hide_gui; //!< bool; The 'hide GUI' hotkey state +extern CVar* ui_preset; //!< enum `RoR::UiPreset` +extern CVar* ui_hide_gui; //!< bool; The 'hide GUI' hotkey state +extern CVar* ui_default_truck_dash; //!< string; name of the '.dashboard' file in modcache. +extern CVar* ui_default_boat_dash; //!< string; name of the '.dashboard' file in modcache. // ------------------------------------------------------------------------------------------------ // Global objects diff --git a/source/main/gui/DashBoardManager.cpp b/source/main/gui/DashBoardManager.cpp index dc02ee6cd1..07cc98456e 100644 --- a/source/main/gui/DashBoardManager.cpp +++ b/source/main/gui/DashBoardManager.cpp @@ -25,7 +25,11 @@ #include "DashBoardManager.h" +#include "Actor.h" #include "Application.h" +#include "CacheSystem.h" +#include "Console.h" +#include "GenericFileFormat.h" #include "Utils.h" using namespace Ogre; @@ -33,7 +37,7 @@ using namespace RoR; #define INITDATA(key, type, name) data[key] = dashData_t(type, name) -DashBoardManager::DashBoardManager(void) : visible(true) +DashBoardManager::DashBoardManager(ActorPtr actor) : visible(true), m_actor(actor) { // init data @@ -170,15 +174,146 @@ std::string DashBoardManager::getLinkNameForID(DashData id) } } -int DashBoardManager::loadDashBoard(Ogre::String filename, bool textureLayer) +// Helpers for `determineLayoutFromDashboardMod()` + +static int DashRPM(const std::string& input) { + std::regex rpm_regex(R"((\d+)rpm)"); + std::smatch match; + if (std::regex_search(input, match, rpm_regex)) { + std::string rpm = match[1]; + return std::atoi(rpm.c_str()); + } + return -1; // Return -1 if no match is found +} - DashBoard* d = new DashBoard(this, filename, textureLayer); - d->setVisible(true); +static char DashXPH(const std::string& input) { + std::regex xph_regex(R"(([km])ph)"); + std::smatch match; + if (std::regex_search(input, match, xph_regex)) { + return match[1].str()[0]; + } + return '\0'; // Return null character if no match is found +} - m_dashboards.push_back(d); +static std::string DashBestRPM(float redlineRPM, const std::string& input1, const std::string& input2) +{ + const float rpmdiff1 = (float)DashRPM(input1) - redlineRPM; + const float rpmdiff2 = (float)DashRPM(input2) - redlineRPM; - return 0; + if (rpmdiff1 < 0) return input2; + else if (rpmdiff2 < 0) return input1; + else if (rpmdiff1 < rpmdiff2) return input1; + else return input2; +} + +static std::string DashBestXPH(char desiredX, const std::string& input1, const std::string& input2) +{ + const char x1 = DashXPH(input1); + const char x2 = DashXPH(input2); + + if (x1 == desiredX) return input1; + else if (x2 == desiredX) return input2; + else return input1; +} + +std::string DashBoardManager::determineLayoutFromDashboardMod(CacheEntryPtr& entry, std::string const& basename) +{ + App::GetCacheSystem()->LoadResource(entry); + Ogre::FileInfoListPtr filelist + = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->resource_group, fmt::format("{}*.layout", basename)); + + std::string match_xph, match_rpm; + for (Ogre::FileInfo& fileinfo : *filelist) + { + if (m_actor->ar_driveable == TRUCK) + { + const int redlineRPM = (int)m_actor->ar_engine->getShiftUpRPM(); + const char desiredX = App::gfx_speedo_imperial->getBool() ? 'm' : 'k'; + if (DashRPM(fileinfo.filename) >= redlineRPM && DashXPH(fileinfo.filename) == desiredX) + { + return fileinfo.filename; // perfect match + } + else if (DashXPH(fileinfo.filename) == desiredX) + { + match_xph = fileinfo.filename; + } + else if (DashRPM(fileinfo.filename) >= redlineRPM) + { + match_rpm = fileinfo.filename; + } + } + else // boat + { + return fileinfo.filename; + } + } + // No ideal match? Return match we have. + return (match_xph != "") ? match_xph : match_rpm; +} + +void DashBoardManager::loadDashBoard(std::string const& filename, BitMask_t flags) +{ + // filename may be either '.layout' file (classic approach) or a new '.dashboard' mod. + // ---------------------------------------------------------------------------------- + + if (BITMASK_IS_0(flags, LOADDASHBOARD_SCREEN_HUD | LOADDASHBOARD_RTT_TEXTURE)) + return; // Nothing to do. + + std::string basename, ext, layoutfname; + Ogre::StringUtil::splitBaseFilename(filename, basename, ext); + if (ext == "dashboard") + { + CacheEntryPtr entry = App::GetCacheSystem()->FindEntryByFilename(LT_DashBoard, /*partial=*/false, filename); + if (!entry) + { + App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING, + fmt::format("DashboardManager: Could not find dashboard file '{}'", filename)); + return; + } + App::GetCacheSystem()->LoadResource(entry); + layoutfname = this->determineLayoutFromDashboardMod(entry, basename); + // load dash fonts + Ogre::FileInfoListPtr filelist + = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->resource_group, fmt::format("{}*.resource", basename)); + for (Ogre::FileInfo& fileinfo : *filelist) + { + MyGUI::ResourceManager::getInstance().load(fileinfo.filename); + } + } + else + { + layoutfname = filename; + } + + if (layoutfname == "") + { + App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_ACTOR, Console::CONSOLE_SYSTEM_WARNING, + fmt::format("{}: Cannot load dashboard '{}' - no applicable layout file found", m_actor->ar_design_name, filename)); + return; + } + + if (BITMASK_IS_1(flags, LOADDASHBOARD_RTT_TEXTURE)) + { + DashBoard* d = new DashBoard(this, layoutfname, /* textureLayer: */true); + d->setVisible(true); + m_dashboards.push_back(d); + if (BITMASK_IS_0(flags, LOADDASHBOARD_STACKABLE)) + { + m_rtt_loaded = true; + } + } + + if (BITMASK_IS_1(flags, LOADDASHBOARD_SCREEN_HUD)) + { + DashBoard* d = new DashBoard(this, layoutfname, /* textureLayer: */false); + d->setVisible(true); + m_dashboards.push_back(d); + if (BITMASK_IS_0(flags, LOADDASHBOARD_STACKABLE)) + { + m_hud_loaded = true; + } + } } void DashBoardManager::update(float dt) diff --git a/source/main/gui/DashBoardManager.h b/source/main/gui/DashBoardManager.h index 8f649f610e..b0863f1a2e 100644 --- a/source/main/gui/DashBoardManager.h +++ b/source/main/gui/DashBoardManager.h @@ -201,11 +201,18 @@ enum DashData DD_MAX }; +enum LoadDashBoardFlags +{ + LOADDASHBOARD_SCREEN_HUD = BITMASK(1), //!< Will be drawn to screen. Unless STACKABLE, it prevents the default dashboard from loading. + LOADDASHBOARD_RTT_TEXTURE = BITMASK(2), //!< Will be drawn to texture. Unless STACKABLE, it prevents the default dashboard from loading. + LOADDASHBOARD_STACKABLE = BITMASK(3) //!< Allows loading multiple dashboards at once (by default there's only one for screen and one for RTT). +}; + // this class is NOT intended to be thread safe - performance is required class DashBoardManager { public: - DashBoardManager(void); + DashBoardManager(ActorPtr actor); ~DashBoardManager(void); // Getter / Setter @@ -228,21 +235,26 @@ class DashBoardManager int getLinkIDForName(Ogre::String& str); std::string getLinkNameForID(DashData id); - int loadDashBoard(Ogre::String filename, bool textureLayer); + void loadDashBoard(const std::string& filename, BitMask_t flags); void update(float dt); void updateFeatures(); - bool WasDashboardLoaded() const { return (m_dashboards.size() > 0); }; + bool wasDashboardHudLoaded() const { return m_hud_loaded; }; + bool wasDashboardRttLoaded() const { return m_rtt_loaded; }; void setVisible(bool visibility); void setVisible3d(bool visibility); bool getVisible() { return visible; }; void windowResized(); protected: + std::string determineLayoutFromDashboardMod(CacheEntryPtr& entry, std::string const& basename); bool visible = false; dashData_t data[DD_MAX]; std::vector m_dashboards; + bool m_hud_loaded = false; + bool m_rtt_loaded = false; + ActorPtr m_actor; }; class DashBoard diff --git a/source/main/gui/OverlayWrapper.cpp b/source/main/gui/OverlayWrapper.cpp index 20b03881ec..d4d9367fc6 100644 --- a/source/main/gui/OverlayWrapper.cpp +++ b/source/main/gui/OverlayWrapper.cpp @@ -376,7 +376,7 @@ void OverlayWrapper::showDashboardOverlays(bool show, ActorPtr actor) m_dashboard_visible = show; // check if we use the new style dashboards - if (actor && actor->ar_dashboard && actor->ar_dashboard->WasDashboardLoaded()) + if (actor && actor->ar_dashboard && actor->ar_dashboard->wasDashboardHudLoaded()) { actor->ar_dashboard->setVisible(show); return; @@ -1080,7 +1080,14 @@ void AeroEngineWidget::Setup(std::string const& engfire_elemname, std::string co // Because highlighting works on per-material basis, we must clone the material for each element engfire_element = Ogre::OverlayManager::getSingleton().getOverlayElement(engfire_elemname); thr_element = Ogre::OverlayManager::getSingleton().getOverlayElement(thr_elemname); - thr_element->setMaterial(thr_element->getMaterial()->clone(thr_element->getMaterial()->getName() + "@" + thr_elemname)); + thr_material = thr_element->getMaterial()->clone(thr_element->getMaterial()->getName() + "@" + thr_elemname); + thr_element->setMaterial(thr_material); +} + +AeroEngineWidget::~AeroEngineWidget() +{ + // Remove cloned materials + Ogre::MaterialManager::getSingleton().remove(thr_material); } bool AeroEngineWidget::UpdateMouseHover() @@ -1119,6 +1126,13 @@ void AeroSwitchWidget::Setup(std::string const & elem_name, std::string const & off_material = Ogre::MaterialManager::getSingleton().getByName(mat_off)->clone(elem_name + "@" + mat_off); } +AeroSwitchWidget::~AeroSwitchWidget() +{ + // Remove cloned materials + Ogre::MaterialManager::getSingleton().remove(on_material); + Ogre::MaterialManager::getSingleton().remove(off_material); +} + bool AeroSwitchWidget::UpdateMouseHover() { // Element's current material switches dynamically based on game state, we must always sync both variants. @@ -1150,9 +1164,18 @@ void AeroTrimWidget::Setup(std::string const & up, std::string const & dn, std:: // Because highlighting works on per-material basis, we must clone the material for each element display = Ogre::OverlayManager::getSingleton().getOverlayElement(disp); up_button = Ogre::OverlayManager::getSingleton().getOverlayElement(up); - up_button->setMaterial(up_button->getMaterial()->clone(up + "$" + disp)); + up_material = up_button->getMaterial()->clone(up + "$" + disp); + up_button->setMaterial(up_material); dn_button = Ogre::OverlayManager::getSingleton().getOverlayElement(dn); - dn_button->setMaterial(dn_button->getMaterial()->clone(dn + "$" + disp)); + down_material = dn_button->getMaterial()->clone(dn + "$" + disp); + dn_button->setMaterial(down_material); +} + +AeroTrimWidget::~AeroTrimWidget() +{ + // Remove cloned materials + Ogre::MaterialManager::getSingleton().remove(up_material); + Ogre::MaterialManager::getSingleton().remove(down_material); } bool AeroTrimWidget::UpdateMouseHover() diff --git a/source/main/gui/OverlayWrapper.h b/source/main/gui/OverlayWrapper.h index ce0713c54e..e05d892c38 100644 --- a/source/main/gui/OverlayWrapper.h +++ b/source/main/gui/OverlayWrapper.h @@ -44,11 +44,13 @@ struct AeroInteractiveWidget struct AeroEngineWidget : public AeroInteractiveWidget { + ~AeroEngineWidget(); void Setup(std::string const& engfire_elemname, std::string const& thr_elemname); bool UpdateMouseHover() override; Ogre::OverlayElement* GetHoveredElement() const override; Ogre::OverlayElement *thr_element; + Ogre::MaterialPtr thr_material; Ogre::OverlayElement *engfire_element; Ogre::TextureUnitState *rpm_texture; Ogre::TextureUnitState *pitch_texture; @@ -57,6 +59,7 @@ struct AeroEngineWidget : public AeroInteractiveWidget struct AeroSwitchWidget: public AeroInteractiveWidget { + ~AeroSwitchWidget(); void Setup(std::string const & elem_name, std::string const & mat_on, std::string const & mat_off); void SetActive(bool value); bool UpdateMouseHover() override; @@ -69,6 +72,7 @@ struct AeroSwitchWidget: public AeroInteractiveWidget struct AeroTrimWidget : public AeroInteractiveWidget { + ~AeroTrimWidget(); void Setup(std::string const & up, std::string const & dn, std::string const & disp); void DisplayFormat(const char* fmt, ...); bool UpdateMouseHover() override; @@ -78,6 +82,8 @@ struct AeroTrimWidget : public AeroInteractiveWidget Ogre::OverlayElement *dn_button; Ogre::OverlayElement* hovered_button = nullptr; Ogre::OverlayElement *display; + Ogre::MaterialPtr up_material; + Ogre::MaterialPtr down_material; }; struct AeroDashOverlay diff --git a/source/main/gui/panels/GUI_GameSettings.cpp b/source/main/gui/panels/GUI_GameSettings.cpp index a3dfadc19d..a0a58388a4 100644 --- a/source/main/gui/panels/GUI_GameSettings.cpp +++ b/source/main/gui/panels/GUI_GameSettings.cpp @@ -341,9 +341,9 @@ void GameSettings::DrawUiSettings() this->DrawUiPresetCombo(); - ImGui::Separator(); + this->DrawUiDefaultDashboard(m_ui_known_dash_truck, App::ui_default_truck_dash, CID_DashboardsTruck, _LC("GameSettings", "Default truck dashboard")); + this->DrawUiDefaultDashboard(m_ui_known_dash_boat, App::ui_default_boat_dash, CID_DashboardsBoat, _LC("GameSettings", "Default boat dashboard")); - DrawGCheckbox(App::gfx_speedo_digital, _LC("GameSettings", "Digital speedometer")); DrawGCheckbox(App::gfx_speedo_imperial, _LC("GameSettings", "Imperial units")); DrawGCheckbox(App::ui_show_live_repair_controls, _LC("GameSettings", "Show controls in live repair box")); @@ -614,6 +614,7 @@ void GameSettings::DrawUiPresetCombo() { ImGui::PushID("uiPreset"); + ImGui::SetNextItemWidth(UI_SELECTOR_WIDTH); if (DrawGCombo(App::ui_preset, _LC("TopMenubar", "UI Preset"), m_cached_uipreset_combo_string.c_str())) { App::GetGuiManager()->ApplyUiPreset(); @@ -667,4 +668,23 @@ void GameSettings::DrawUiPresetCombo() } ImGui::PopID(); //"uiPreset" -} \ No newline at end of file +} + +void GameSettings::DrawUiDefaultDashboard(CacheEntryPtr& entry, CVar* cvar, CacheCategoryId category_id, const std::string& label) +{ + if (!entry || entry->fname != cvar->getStr()) + { + entry = App::GetCacheSystem()->FindEntryByFilename(LT_DashBoard, /* partial: */false, cvar->getStr()); + } + + ImGui::AlignTextToFramePadding(); + std::string caption = fmt::format("{}##truck_dash", entry ? entry->dname : cvar->getStr()); + if (ImGui::Button(caption.c_str(), ImVec2(UI_SELECTOR_WIDTH, 0.f))) + { + default_dash_being_selected = category_id; + LoaderType* payload = new LoaderType(LoaderType::LT_DashBoard); + App::GetGameContext()->PushMessage(Message(MSG_GUI_OPEN_SELECTOR_REQUESTED, (void*)payload)); + } + ImGui::SameLine(); + ImGui::Text("%s", label.c_str()); +} diff --git a/source/main/gui/panels/GUI_GameSettings.h b/source/main/gui/panels/GUI_GameSettings.h index e8460d3bbf..a2caf227af 100644 --- a/source/main/gui/panels/GUI_GameSettings.h +++ b/source/main/gui/panels/GUI_GameSettings.h @@ -33,6 +33,8 @@ class GameSettings bool IsVisible() const { return m_is_visible; } void SetVisible(bool v); + CacheCategoryId default_dash_being_selected = CID_None; + private: void DrawRenderSystemSettings(); void DrawGeneralSettings(); @@ -43,8 +45,12 @@ class GameSettings void DrawControlSettings(); void DrawDiagSettings(); - // Helpers + // UI settings + const float UI_SELECTOR_WIDTH = 275.0f; void DrawUiPresetCombo(); + void DrawUiDefaultDashboard(CacheEntryPtr& entry, CVar* cvar, CacheCategoryId category_id, const std::string& label); + CacheEntryPtr m_ui_known_dash_truck; + CacheEntryPtr m_ui_known_dash_boat; // GUI state bool m_is_visible = false; diff --git a/source/main/gui/panels/GUI_MainSelector.cpp b/source/main/gui/panels/GUI_MainSelector.cpp index 738da9646e..3d81585649 100644 --- a/source/main/gui/panels/GUI_MainSelector.cpp +++ b/source/main/gui/panels/GUI_MainSelector.cpp @@ -270,13 +270,16 @@ void MainSelector::Draw() sd_entry.sde_entry->filecachename, Ogre::RGN_DEFAULT); if (preview_tex) { - // Scale the image + // Scale the image (for dashboards only shrink but don't enlarge so players see actual in-game size). ImVec2 max_size = (ImGui::GetWindowSize() * PREVIEW_SIZE_RATIO); ImVec2 size(preview_tex->getWidth(), preview_tex->getHeight()); - size *= max_size.x / size.x; // Fit size along X - if (size.y > max_size.y) // Reduce size along Y if needed + if (m_loader_type != LT_DashBoard || size.x > max_size.x) { - size *= max_size.y / size.y; + size *= max_size.x / size.x; // Fit size along X + if (size.y > max_size.y) // Reduce size along Y if needed + { + size *= max_size.y / size.y; + } } // Draw the image ImGui::SetCursorPos((cursor_pos + ImGui::GetWindowSize()) - size); @@ -468,6 +471,10 @@ void MainSelector::UpdateDisplayLists() query.cqy_search_method = m_search_method; query.cqy_search_string = m_search_string; query.cqy_filter_guid = m_filter_guid; + if (m_loader_type == LT_DashBoard) // HACK for dashboards + { + query.cqy_filter_category_id = App::GetGuiManager()->GameSettings.default_dash_being_selected; + } App::GetCacheSystem()->Query(query); @@ -628,6 +635,25 @@ void MainSelector::Apply() App::GetGameContext()->PushMessage(Message(MSG_SIM_LOAD_TERRN_REQUESTED, sd_entry.sde_entry->fname)); this->Close(); } + else if (m_loader_type == LT_DashBoard && + App::app_state->getEnum() == AppState::MAIN_MENU) + { + switch (App::GetGuiManager()->GameSettings.default_dash_being_selected) + { + case CID_DashboardsTruck: + App::ui_default_truck_dash->setStr(sd_entry.sde_entry->fname); + break; + case CID_DashboardsBoat: + App::ui_default_boat_dash->setStr(sd_entry.sde_entry->fname); + break; + default: + LOG(fmt::format("[RoR|GameSettings] INTERNAL ERROR - Unrecognized dashboard type being selected ({})!", + (int)App::GetGuiManager()->GameSettings.default_dash_being_selected)); + break; + } + App::GetGuiManager()->GameSettings.default_dash_being_selected = CID_None; + this->Close(); + } else if (App::app_state->getEnum() == AppState::SIMULATION) { LoaderType type = m_loader_type; diff --git a/source/main/physics/ActorSpawner.cpp b/source/main/physics/ActorSpawner.cpp index 4cd0d1a4ad..3281e76fd2 100644 --- a/source/main/physics/ActorSpawner.cpp +++ b/source/main/physics/ActorSpawner.cpp @@ -362,7 +362,7 @@ void ActorSpawner::InitializeRig() m_actor->tc_ratio = 1.f; m_actor->tc_timer = 0.f; - m_actor->ar_dashboard = new DashBoardManager(); + m_actor->ar_dashboard = new DashBoardManager(m_actor); /* Collisions */ @@ -6826,88 +6826,33 @@ void ActorSpawner::FinalizeGfxSetup() { if (gs.key == "dashboard") { - m_actor->ar_dashboard->loadDashBoard(gs.value, false); + m_actor->ar_dashboard->loadDashBoard(gs.value, LOADDASHBOARD_SCREEN_HUD); } else if (gs.key == "texturedashboard") { - m_actor->ar_dashboard->loadDashBoard(gs.value, true); + m_actor->ar_dashboard->loadDashBoard(gs.value, LOADDASHBOARD_RTT_TEXTURE); } } } // If none specified, load default dashboard layouts - if (!m_actor->ar_dashboard->WasDashboardLoaded()) - { - if (m_actor->ar_driveable == TRUCK) // load default for a truck - { - if (App::gfx_speedo_digital->getBool()) - { - if (App::gfx_speedo_imperial->getBool()) - { - if (m_actor->ar_engine->getShiftUpRPM() > 3500) - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_mph.layout", false); //7000 rpm tachometer thanks to Klink - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_mph.layout", true); - } - else - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_mph.layout", false); - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_mph.layout", true); - } - } - else - { - if (m_actor->ar_engine->getShiftUpRPM() > 3500) - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000.layout", false); //7000 rpm tachometer thanks to Klink - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000.layout", true); - } - else - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500.layout", false); - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500.layout", true); - } - } - } - else // Analog speedometer - { - if (App::gfx_speedo_imperial->getBool()) - { - if (m_actor->ar_engine->getShiftUpRPM() > 3500) - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_analog_mph.layout", false); //7000 rpm tachometer thanks to Klink - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_analog_mph.layout", true); - } - else - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_analog_mph.layout", false); - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_analog_mph.layout", true); - } - } - else - { - if (m_actor->ar_engine->getShiftUpRPM() > 3500) - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_analog.layout", false); //7000 rpm tachometer thanks to Klink - m_actor->ar_dashboard->loadDashBoard("default_dashboard7000_analog.layout", true); - } - else - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_analog.layout", false); - m_actor->ar_dashboard->loadDashBoard("default_dashboard3500_analog.layout", true); - } - } - } - } - else if (m_actor->ar_driveable == BOAT) - { - m_actor->ar_dashboard->loadDashBoard("default_dashboard_boat.layout", false); - m_actor->ar_dashboard->loadDashBoard("default_dashboard_boat.layout", true); - } + BitMask_t defaultdash_flags = 0; + BITMASK_SET_1(defaultdash_flags, m_actor->ar_dashboard->wasDashboardHudLoaded() ? 0 : LOADDASHBOARD_SCREEN_HUD); + BITMASK_SET_1(defaultdash_flags, m_actor->ar_dashboard->wasDashboardRttLoaded() ? 0 : LOADDASHBOARD_RTT_TEXTURE); + switch (m_actor->ar_driveable) + { + case TRUCK: + m_actor->ar_dashboard->loadDashBoard(App::ui_default_truck_dash->getStr(), defaultdash_flags); + m_actor->ar_dashboard->setVisible(false); + break; + case BOAT: + m_actor->ar_dashboard->loadDashBoard(App::ui_default_boat_dash->getStr(), defaultdash_flags); + m_actor->ar_dashboard->setVisible(false); + break; + default: + break; } - m_actor->ar_dashboard->setVisible(false); - if (!m_help_material_name.empty()) { try diff --git a/source/main/resources/CacheSystem.cpp b/source/main/resources/CacheSystem.cpp index b966e570ee..eb1a32c5fe 100644 --- a/source/main/resources/CacheSystem.cpp +++ b/source/main/resources/CacheSystem.cpp @@ -139,6 +139,7 @@ CacheSystem::CacheSystem() m_known_extensions.push_back("addonpart"); m_known_extensions.push_back("tuneup"); m_known_extensions.push_back("assetpack"); + m_known_extensions.push_back("dashboard"); // register the dirs m_content_dirs.push_back("mods"); @@ -198,6 +199,7 @@ CacheEntryPtr CacheSystem::FindEntryByFilename(LoaderType type, bool partial, co for (CacheEntryPtr& entry : m_entries) { if ((type == LT_Terrain) != (entry->fext == "terrn2") || + (type == LT_DashBoard) != (entry->fext == "dashboard") || (type == LT_AllBeam && entry->fext == "skin")) continue; @@ -793,6 +795,12 @@ void CacheSystem::AddFile(String group, Ogre::FileInfo f, String ext) FillAssetPackDetailInfo(entry, ds); new_entries.push_back(entry); } + else if (ext == "dashboard") + { + CacheEntryPtr entry = new CacheEntry(); + FillDashboardDetailInfo(entry, ds); + new_entries.push_back(entry); + } else { CacheEntryPtr entry = new CacheEntry(); @@ -1285,6 +1293,43 @@ void CacheSystem::FillAssetPackDetailInfo(CacheEntryPtr &entry, Ogre::DataStream } } +void CacheSystem::FillDashboardDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr ds) +{ + GenericDocumentPtr doc = new GenericDocument(); + BitMask_t options = GenericDocument::OPTION_ALLOW_SLASH_COMMENTS; + doc->loadFromDataStream(ds, options); + + GenericDocContextPtr ctx = new GenericDocContext(doc); + while (!ctx->endOfFile()) + { + if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_name" && ctx->isTokString(1)) + { + entry->dname = ctx->getTokString(1); + } + else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_description" && ctx->isTokString(1)) + { + entry->description = ctx->getTokString(1); + } + else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_category" && ctx->isTokInt(1)) + { + entry->categoryid = ctx->getTokInt(1); + } + else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_author") + { + int n = ctx->countLineArgs(); + AuthorInfo author; + if (n > 1) { author.type = ctx->getTokString(1); } + if (n > 2) { author.id = ctx->getTokInt(2); } + if (n > 3) { author.name = ctx->getTokString(3); } + if (n > 4) { author.email = ctx->getTokString(4); } + entry->authors.push_back(author); + } + + ctx->seekNextLine(); + } + +} + void CacheSystem::FillTuneupDetailInfo(CacheEntryPtr &entry, TuneupDefPtr& tuneup_def) { if (!tuneup_def->author_name.empty()) @@ -2140,6 +2185,8 @@ size_t CacheSystem::Query(CacheQuery& query) add = (query.cqy_filter_type == LT_Tuneup); else if (entry->fext == "assetpack") add = (query.cqy_filter_type == LT_AssetPack); + else if (entry->fext == "dashboard") + add = (query.cqy_filter_type == LT_DashBoard); else if (entry->fext == "truck") add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Vehicle || query.cqy_filter_type == LT_Truck); else if (entry->fext == "car") diff --git a/source/main/resources/CacheSystem.h b/source/main/resources/CacheSystem.h index 15671b1f4d..223e1cb7f1 100644 --- a/source/main/resources/CacheSystem.h +++ b/source/main/resources/CacheSystem.h @@ -144,21 +144,6 @@ class CacheEntry: public RefCountingObject typedef RefCountingObjectPtr CacheEntryPtr; -enum CacheCategoryId -{ - CID_None = 0, - - CID_Projects = 8000, //!< For truck files under 'projects/' directory, to allow listing from editors. - CID_Tuneups = 8001, //!< For unsorted tuneup files. - - CID_Max = 9000, //!< SPECIAL VALUE - Maximum allowed to be present in any mod files. - CID_Unsorted = 9990, - CID_All = 9991, - CID_Fresh = 9992, - CID_Hidden = 9993, - CID_SearchResults = 9994, -}; - struct CacheQueryResult { CacheQueryResult(CacheEntryPtr entry, size_t score); @@ -370,6 +355,7 @@ class CacheSystem void FillAddonPartDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds); void FillTuneupDetailInfo(CacheEntryPtr &entry, TuneupDefPtr& tuneup_def); void FillAssetPackDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds); + void FillDashboardDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr ds); /// @} void GenerateHashFromFilenames(); //!< For quick detection of added/removed content @@ -429,6 +415,11 @@ class CacheSystem {875, _LC("ModCategory", "Submarine")}, + // dashboards + {200, _LC("ModCategory", "Dashboards - Generic")}, + {201, _LC("ModCategory", "Dashboards - Truck")}, + {202, _LC("ModCategory", "Dashboards - Boat")}, + // note: these categories are NOT in the repository: {5000, _LC("ModCategory", "Official Terrains")}, {5001, _LC("ModCategory", "Night Terrains")}, diff --git a/source/main/resources/ContentManager.cpp b/source/main/resources/ContentManager.cpp index 4fcf5600b2..a28b38cd17 100644 --- a/source/main/resources/ContentManager.cpp +++ b/source/main/resources/ContentManager.cpp @@ -278,6 +278,8 @@ void ContentManager::InitModCache(CacheValidity validity) ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), "content") , "FileSystem", RGN_CONTENT); std::string objects = PathCombine("resources", "beamobjects.zip"); ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), objects) , "Zip" , RGN_CONTENT); + std::string dashboards = PathCombine("resources", "dashboards.zip"); // To find 'default.dashboard' mod + ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), dashboards), "Zip", RGN_CONTENT); // Create RGN_TEMP in recursive mode to find all subdirectories. diff --git a/source/main/system/CVar.cpp b/source/main/system/CVar.cpp index a2c5b706b1..90cc73d0b2 100644 --- a/source/main/system/CVar.cpp +++ b/source/main/system/CVar.cpp @@ -184,7 +184,6 @@ void Console::cVarSetupBuiltins() App::gfx_static_cam_fov_exp = this->cVarCreate("gfx_static_cam_fov_exp", "", CVAR_ARCHIVE | CVAR_TYPE_FLOAT, "1.0"); App::gfx_fixed_cam_tracking = this->cVarCreate("gfx_fixed_cam_tracking", "", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false"); App::gfx_fps_limit = this->cVarCreate("gfx_fps_limit", "FPS-Limiter", CVAR_ARCHIVE | CVAR_TYPE_INT, "0"); - App::gfx_speedo_digital = this->cVarCreate("gfx_speedo_digital", "DigitalSpeedo", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "true"); App::gfx_speedo_imperial = this->cVarCreate("gfx_speedo_imperial", "gfx_speedo_imperial", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false"); App::gfx_flexbody_cache = this->cVarCreate("gfx_flexbody_cache", "Flexbody_UseCache", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false"); App::gfx_reduce_shadows = this->cVarCreate("gfx_reduce_shadows", "Shadow optimizations", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "true"); @@ -204,6 +203,8 @@ void Console::cVarSetupBuiltins() App::ui_show_vehicle_buttons = this->cVarCreate("ui_show_vehicle_buttons", "Show vehicle buttons menu", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "true"); App::ui_preset = this->cVarCreate("ui_preset", "", CVAR_ARCHIVE | CVAR_TYPE_INT, "0"/*(int)UiPreset::NOVICE*/); App::ui_hide_gui = this->cVarCreate("ui_hide_gui", "", CVAR_TYPE_BOOL, "false"); + App::ui_default_truck_dash = this->cVarCreate("ui_default_truck_dash", "", CVAR_ARCHIVE, "default_truck_digital.dashboard"); + App::ui_default_boat_dash = this->cVarCreate("ui_default_boat_dash", "", CVAR_ARCHIVE, "default_boat.dashboard"); } CVar* Console::cVarCreate(std::string const& name, std::string const& long_name,