Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
DashBoardManager.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
24
25
26#include "DashBoardManager.h"
27
28#include "Actor.h"
29#include "Application.h"
30#include "CacheSystem.h"
31#include "Console.h"
32#include "GenericFileFormat.h"
33#include "ScriptEngine.h"
34#include "Utils.h"
35
36using namespace Ogre;
37using namespace RoR;
38
39#define INITDATA(key, type, name) data[key] = dashData_t(type, name)
40
41DashBoardManager::DashBoardManager(ActorPtr actor) : visible(true), m_actor(actor)
42{
43 // Reserve memory for built-in inputs.
44 data.resize(DD_MAX);
45
46 // init data
48 INITDATA(DD_ENGINE_SPEEDO_KPH , DC_FLOAT, "speedo_kph");
49 INITDATA(DD_ENGINE_SPEEDO_MPH , DC_FLOAT, "speedo_mph");
50 INITDATA(DD_ENGINE_TURBO , DC_FLOAT, "engine_turbo");
51 INITDATA(DD_ENGINE_IGNITION , DC_BOOL , "engine_ignition");
52 INITDATA(DD_ENGINE_BATTERY , DC_BOOL , "engine_battery");
53 INITDATA(DD_ENGINE_CLUTCH_WARNING , DC_BOOL , "engine_clutch_warning");
54 INITDATA(DD_ENGINE_GEAR , DC_INT , "engine_gear");
55 INITDATA(DD_ENGINE_NUM_GEAR , DC_INT , "engine_num_gear");
56 INITDATA(DD_ENGINE_GEAR_STRING , DC_CHAR , "engine_gear_string");
57 INITDATA(DD_ENGINE_AUTOGEAR_STRING , DC_CHAR , "engine_autogear_string");
58 INITDATA(DD_ENGINE_AUTO_GEAR , DC_INT , "engine_auto_gear");
59 INITDATA(DD_ENGINE_CLUTCH , DC_FLOAT, "engine_clutch");
60 INITDATA(DD_BRAKE , DC_FLOAT, "brake");
61 INITDATA(DD_ACCELERATOR , DC_FLOAT, "accelerator");
62 INITDATA(DD_ROLL , DC_FLOAT, "roll");
63 INITDATA(DD_ROLL_CORR , DC_FLOAT, "roll_corr");
64 INITDATA(DD_ROLL_CORR_ACTIVE , DC_BOOL , "roll_corr_active");
65 INITDATA(DD_PITCH , DC_FLOAT, "pitch");
66 INITDATA(DD_PARKINGBRAKE , DC_BOOL , "parkingbrake");
67 INITDATA(DD_LOCKED , DC_BOOL , "locked");
68 INITDATA(DD_LOW_PRESSURE , DC_BOOL , "low_pressure");
69 INITDATA(DD_TRACTIONCONTROL_MODE , DC_INT , "tractioncontrol_mode");
70 INITDATA(DD_ANTILOCKBRAKE_MODE , DC_INT , "antilockbrake_mode");
71 INITDATA(DD_TIES_MODE , DC_INT , "ties_mode");
72 INITDATA(DD_SCREW_THROTTLE_0 , DC_FLOAT, "screw_throttle_0");
73 INITDATA(DD_SCREW_THROTTLE_1 , DC_FLOAT, "screw_throttle_1");
74 INITDATA(DD_SCREW_THROTTLE_2 , DC_FLOAT, "screw_throttle_2");
75 INITDATA(DD_SCREW_THROTTLE_3 , DC_FLOAT, "screw_throttle_3");
76 INITDATA(DD_SCREW_THROTTLE_4 , DC_FLOAT, "screw_throttle_4");
77 INITDATA(DD_SCREW_THROTTLE_5 , DC_FLOAT, "screw_throttle_5");
78 INITDATA(DD_SCREW_STEER_0 , DC_FLOAT, "screw_steer_0");
79 INITDATA(DD_SCREW_STEER_1 , DC_FLOAT, "screw_steer_1");
80 INITDATA(DD_SCREW_STEER_2 , DC_FLOAT, "screw_steer_2");
81 INITDATA(DD_SCREW_STEER_3 , DC_FLOAT, "screw_steer_3");
82 INITDATA(DD_SCREW_STEER_4 , DC_FLOAT, "screw_steer_4");
83 INITDATA(DD_SCREW_STEER_5 , DC_FLOAT, "screw_steer_5");
84 INITDATA(DD_WATER_DEPTH , DC_FLOAT, "water_depth");
85 INITDATA(DD_WATER_SPEED , DC_FLOAT, "water_speed");
86 INITDATA(DD_AEROENGINE_THROTTLE_0 , DC_FLOAT, "aeroengine_throttle_0");
87 INITDATA(DD_AEROENGINE_THROTTLE_1 , DC_FLOAT, "aeroengine_throttle_1");
88 INITDATA(DD_AEROENGINE_THROTTLE_2 , DC_FLOAT, "aeroengine_throttle_2");
89 INITDATA(DD_AEROENGINE_THROTTLE_3 , DC_FLOAT, "aeroengine_throttle_3");
90 INITDATA(DD_AEROENGINE_THROTTLE_4 , DC_FLOAT, "aeroengine_throttle_4");
91 INITDATA(DD_AEROENGINE_THROTTLE_5 , DC_FLOAT, "aeroengine_throttle_5");
92 INITDATA(DD_AEROENGINE_FAILED_0 , DC_BOOL , "aeroengine_failed_0");
93 INITDATA(DD_AEROENGINE_FAILED_1 , DC_BOOL , "aeroengine_failed_1");
94 INITDATA(DD_AEROENGINE_FAILED_2 , DC_BOOL , "aeroengine_failed_2");
95 INITDATA(DD_AEROENGINE_FAILED_3 , DC_BOOL , "aeroengine_failed_3");
96 INITDATA(DD_AEROENGINE_FAILED_4 , DC_BOOL , "aeroengine_failed_4");
97 INITDATA(DD_AEROENGINE_FAILED_5 , DC_BOOL , "aeroengine_failed_5");
98 INITDATA(DD_AEROENGINE_RPM_0 , DC_FLOAT, "aeroengine_rpm_0");
99 INITDATA(DD_AEROENGINE_RPM_1 , DC_FLOAT, "aeroengine_rpm_1");
100 INITDATA(DD_AEROENGINE_RPM_2 , DC_FLOAT, "aeroengine_rpm_2");
101 INITDATA(DD_AEROENGINE_RPM_3 , DC_FLOAT, "aeroengine_rpm_3");
102 INITDATA(DD_AEROENGINE_RPM_4 , DC_FLOAT, "aeroengine_rpm_4");
103 INITDATA(DD_AEROENGINE_RPM_5 , DC_FLOAT, "aeroengine_rpm_5");
104 INITDATA(DD_AIRSPEED , DC_FLOAT, "airspeed");
105 INITDATA(DD_WING_AOA_0 , DC_FLOAT, "wing_aoa_0");
106 INITDATA(DD_WING_AOA_1 , DC_FLOAT, "wing_aoa_1");
107 INITDATA(DD_WING_AOA_2 , DC_FLOAT, "wing_aoa_2");
108 INITDATA(DD_WING_AOA_3 , DC_FLOAT, "wing_aoa_3");
109 INITDATA(DD_WING_AOA_4 , DC_FLOAT, "wing_aoa_4");
110 INITDATA(DD_WING_AOA_5 , DC_FLOAT, "wing_aoa_5");
111 INITDATA(DD_ALTITUDE , DC_FLOAT, "altitude");
112 INITDATA(DD_ALTITUDE_STRING , DC_CHAR , "altitude_string");
113
114 INITDATA(DD_ODOMETER_TOTAL , DC_FLOAT, "odometer_total");
115 INITDATA(DD_ODOMETER_USER , DC_FLOAT, "odometer_user");
116
117 // Lights (mirrors RoRnet::Lightmask)
118
119 INITDATA(DD_CUSTOM_LIGHT1 , DC_BOOL, "custom_light1");
120 INITDATA(DD_CUSTOM_LIGHT2 , DC_BOOL, "custom_light2");
121 INITDATA(DD_CUSTOM_LIGHT3 , DC_BOOL, "custom_light3");
122 INITDATA(DD_CUSTOM_LIGHT4 , DC_BOOL, "custom_light4");
123 INITDATA(DD_CUSTOM_LIGHT5 , DC_BOOL, "custom_light5");
124 INITDATA(DD_CUSTOM_LIGHT6 , DC_BOOL, "custom_light6");
125 INITDATA(DD_CUSTOM_LIGHT7 , DC_BOOL, "custom_light7");
126 INITDATA(DD_CUSTOM_LIGHT8 , DC_BOOL, "custom_light8");
127 INITDATA(DD_CUSTOM_LIGHT9 , DC_BOOL, "custom_light9");
128 INITDATA(DD_CUSTOM_LIGHT10 , DC_BOOL, "custom_light10");
129
130 INITDATA(DD_HEADLIGHTS , DC_BOOL, "headlights");
131 INITDATA(DD_HIGHBEAMS , DC_BOOL, "highbeams");
132 INITDATA(DD_FOGLIGHTS , DC_BOOL, "foglights");
133 INITDATA(DD_SIDELIGHTS , DC_BOOL, "sidelights");
134 INITDATA(DD_BRAKE_LIGHTS , DC_BOOL, "brake_lights");
135 INITDATA(DD_REVERSE_LIGHT , DC_BOOL, "reverse_light");
136 INITDATA(DD_BEACONS , DC_BOOL, "beacons");
137 INITDATA(DD_LIGHTS_LEGACY , DC_BOOL, "lights"); // Alias of 'sidelights'
138
139 INITDATA(DD_SIGNAL_TURNLEFT , DC_BOOL, "signal_turnleft");
140 INITDATA(DD_SIGNAL_TURNRIGHT , DC_BOOL, "signal_turnright");
141 INITDATA(DD_SIGNAL_WARNING , DC_BOOL, "signal_warning");
142
143 // load dash fonts
144 MyGUI::ResourceManager::getInstance().load("MyGUI_FontsDash.xml");
145}
146
148{
149 // free all objects
150 while (m_dashboards.size() > 0)
151 {
152 delete m_dashboards.back();
153 m_dashboards.pop_back();
154 }
155
156 data.clear();
157}
158
159int DashBoardManager::registerCustomInput(Ogre::String name, int dataType)
160{
161 int newKey = -1;
162 bool valid = true;
163
164 if (getLinkIDForName(name) != -1)
165 {
166 valid = false;
168 fmt::format("{}: Cannot register dashboard custom input \"{}\", name conflicts with existing one. Ignoring it.", m_actor->ar_design_name, name));
169 }
170 if (dataType == DC_INVALID || dataType >= DC_MAX || dataType <= DC_MIN)
171 {
172 valid = false;
174 fmt::format("{}: Cannot register dashboard custom input \"{}\", invalid data type.", m_actor->ar_design_name, name));
175 }
176
177 if (valid)
178 {
180 data.push_back(dashData_t(dataType, name));
182 }
183
184 return newKey;
185}
186
188{
189 for (int i = 0; i < data.size(); i++)
190 {
191 if (data[i].name == str)
192 return i;
193 }
194 return -1;
195}
196
198{
199 if (id >= 0 && id < data.size())
200 {
201 return data[id].name;
202 }
203 else
204 {
205 return "";
206 }
207}
208
209// Helper funcs and structs for `determineLayoutFromDashboardMod()` below.
210
211constexpr int DASHTAG_RPM_NONE = -1;
212constexpr char DASHTAG_XPH_NONE = '\0';
213
214static int DashRPM(const std::string& input)
215{
216 std::regex rpm_regex(R"((\d+)rpm)");
217 std::smatch match;
218 if (std::regex_search(input, match, rpm_regex)) {
219 std::string rpm = match[1];
220 return std::atoi(rpm.c_str());
221 }
222 return DASHTAG_RPM_NONE;
223}
224
225static char DashXPH(const std::string& input)
226{
227 std::regex xph_regex(R"(([km])ph)");
228 std::smatch match;
229 if (std::regex_search(input, match, xph_regex)) {
230 return match[1].str()[0];
231 }
232 return DASHTAG_XPH_NONE;
233}
234
236{
237 std::string filename;
240
241 DashCandidateLayout(const std::string& filename)
242 {
243 this->filename = filename;
244 this->rpm = DashRPM(filename);
245 this->xph = DashXPH(filename);
246 }
247};
248
249std::string DashBoardManager::determineLayoutFromDashboardMod(CacheEntryPtr& entry, std::string const& basename)
250{
252 Ogre::FileInfoListPtr filelist
253 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->resource_group, fmt::format("{}*.layout", basename));
254
255 if (filelist->empty())
256 {
258 fmt::format("{}: No layout files found in dashboard '{}'", m_actor->ar_design_name, basename));
259 return "";
260 }
261
262 // Boat and aircraft dashboards are separate from trucks and have no tags to evaluate.
263 if (m_actor->ar_driveable == BOAT ||
265 {
266 return filelist->begin()->filename;
267 }
268
269 if (m_actor->ar_driveable == TRUCK)
270 {
271 return this->determineTruckLayoutFromDashboardMod(filelist);
272 }
273
274 return "";
275}
276
277std::string DashBoardManager::determineTruckLayoutFromDashboardMod(Ogre::FileInfoListPtr& filelist)
278{
279 // Algorithm:
280 // A. Consider only layouts with matching Xph tag, find best RPM match (see below).
281 // B. If no match found, consider also layouts without Xph tag, find best RPM match (see below).
282 //
283 // Best RPM matching:
284 // 1. Consider just layouts with Xrpm tag, find one with with smallest RPM overshoot.
285 // 2. If all undershoot, take one with least undershoot and log a warning
286 // 3. If there's no layout with Xrpm tag, Consider layouts without Xrpm tag, pick random .
287 // ---------------------------------------------------------------------------------------
288
289 const int redlineRPM = (int)m_actor->ar_engine->getShiftUpRPM();
290 const char desiredX = App::gfx_speedo_imperial->getBool() ? 'm' : 'k';
291 std::vector<DashCandidateLayout> candidates;
292
293 for (Ogre::FileInfo& fileinfo : *filelist)
294 {
295 candidates.emplace_back(fileinfo.filename);
296 }
297
298 // A. Consider only layouts with matching Xph tag, find best RPM match (see above).
299 float least_overshoot = std::numeric_limits<float>::max(); DashCandidateLayout* overshoot_candidate = nullptr;
300 float least_undershoot = -std::numeric_limits<float>::max(); DashCandidateLayout* undershoot_candidate = nullptr;
301 for (auto& candidate : candidates)
302 {
303 if (candidate.xph == desiredX)
304 {
305 float rpm_diff = (float)candidate.rpm - redlineRPM;
306 if (rpm_diff < 0 && rpm_diff > least_undershoot)
307 {
308 least_undershoot = rpm_diff;
309 undershoot_candidate = &candidate;
310 }
311 else if (rpm_diff >= 0 && rpm_diff < least_overshoot)
312 {
313 least_overshoot = rpm_diff;
314 overshoot_candidate = &candidate;
315 }
316 }
317 }
318
319 if (overshoot_candidate)
320 {
321 return overshoot_candidate->filename;
322 }
323 else if (undershoot_candidate)
324 {
326 fmt::format("{}: No ideal dashboard found, using one with least RPM undershoot", m_actor->ar_design_name));
327 return undershoot_candidate->filename;
328 }
329
330 // B. If no match found, consider also layouts without Xph tag, find best RPM match (see above).
332 fmt::format("{}: Selected dashboard has no '{}ph' layouts, ignoring setting", m_actor->ar_design_name, desiredX));
333 least_overshoot = std::numeric_limits<float>::max(); overshoot_candidate = nullptr;
334 least_undershoot = -std::numeric_limits<float>::max(); undershoot_candidate = nullptr;
335 for (auto& candidate : candidates)
336 {
337 float rpm_diff = (float)candidate.rpm - redlineRPM;
338 if (rpm_diff < 0 && rpm_diff > least_undershoot)
339 {
340 least_undershoot = rpm_diff;
341 undershoot_candidate = &candidate;
342 }
343 else if (rpm_diff >= 0 && rpm_diff < least_overshoot)
344 {
345 least_overshoot = rpm_diff;
346 overshoot_candidate = &candidate;
347 }
348 }
349
350 if (overshoot_candidate)
351 {
352 return overshoot_candidate->filename;
353 }
354 else if (undershoot_candidate)
355 {
357 fmt::format("{}: No ideal dashboard found, using one with least RPM undershoot", m_actor->ar_design_name));
358 return undershoot_candidate->filename;
359 }
360
361 return filelist->begin()->filename; // If all else failed, just pick random.
362}
363
365{
366 try
367 {
368 DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(entry->fname, entry->resource_group);
369 GenericDocumentPtr dash_doc = new GenericDocument();
371 dash_doc->loadFromDataStream(ds, options);
372
373 GenericDocContextPtr ctx = new GenericDocContext(dash_doc);
374 while (!ctx->endOfFile())
375 {
376 if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_custom_input" && ctx->countLineArgs() > 2)
377 {
378 bool valid_custom_input = true;
379 std::string custom_input_name = ctx->getTokString(1);
380 int custom_input_data_type;
381
382 std::string data_type_str = ctx->getTokString(2);
383 if (data_type_str == "bool") { custom_input_data_type = DC_BOOL; }
384 else if (data_type_str == "float") { custom_input_data_type = DC_FLOAT; }
385 else if (data_type_str == "int") { custom_input_data_type = DC_INT; }
386 else if (data_type_str == "string") { custom_input_data_type = DC_CHAR; }
387 else
388 {
389 valid_custom_input = false;
391 fmt::format("Dashboard Custom Input: Invalid data type \"{}\" for \"{}\"", data_type_str, custom_input_name));
392 }
393
394 if (valid_custom_input)
395 {
396 this->registerCustomInput(custom_input_name, custom_input_data_type);
397 }
398 }
399
400 ctx->seekNextLine();
401 }
402 }
403 catch (Ogre::Exception& e)
404 {
405 RoR::LogFormat("[RoR|DashBoardManager] Error processing file '%s', message :%s",
406 entry->fname.c_str(), e.getFullDescription().c_str());
407 }
408}
409
410void DashBoardManager::loadDashBoard(std::string const& filename, BitMask_t flags)
411{
412 // filename may be either '.layout' file (classic approach) or a new '.dashboard' mod.
413 // ----------------------------------------------------------------------------------
414
416 return; // Nothing to do.
417
418 std::string basename, ext, layoutfname, scriptfilename = "";
419 Ogre::StringUtil::splitBaseFilename(filename, basename, ext);
420 if (ext == "dashboard")
421 {
422 CacheEntryPtr entry = App::GetCacheSystem()->FindEntryByFilename(LT_DashBoard, /*partial=*/false, filename);
423 if (!entry)
424 {
426 fmt::format("DashboardManager: Could not find dashboard file '{}'", filename));
427 return;
428 }
430 layoutfname = this->determineLayoutFromDashboardMod(entry, basename);
431
433
434 // load dash fonts
435 Ogre::FileInfoListPtr filelist
436 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->resource_group, fmt::format("{}*.resource", basename));
437 for (Ogre::FileInfo& fileinfo : *filelist)
438 {
439 MyGUI::ResourceManager::getInstance().load(fileinfo.filename);
440 }
441
442 // Load dashboard script
443 Ogre::FileInfoListPtr scriptFile
444 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->resource_group, fmt::format("{}.as", basename));
445 if (scriptFile->size() > 0)
446 {
447 scriptfilename = scriptFile->front().filename;
448 }
449 }
450 else
451 {
452 layoutfname = filename;
453 }
454
455 if (layoutfname == "")
456 {
458 fmt::format("{}: Cannot load dashboard '{}' - no applicable layout file found", m_actor->ar_design_name, filename));
459 return;
460 }
461
463 {
464 DashBoard* d = new DashBoard(this, layoutfname, loadedRTTDashboards + 1);
466 // This dashboard shouldn't be visible at this point, since
467 // it's not linked to the current player actor yet.
468 // GameContext.ChangePlayerActor() will change the visibility
469 // when needed.
470 d->setVisible(false);
471 if (scriptfilename != "")
472 {
473 d->loadScript(scriptfilename, m_actor);
474 }
475 m_dashboards.push_back(d);
476
478 {
479 m_rtt_loaded = true;
480 }
481 }
482
484 {
485 DashBoard* d = new DashBoard(this, layoutfname, NO_RTT_DASHBOARD);
486 d->setVisible(true);
487 if (scriptfilename != "")
488 {
489 d->loadScript(scriptfilename, m_actor);
490 }
491 m_dashboards.push_back(d);
492
494 {
495 m_hud_loaded = true;
496 }
497 }
498}
499
501{
502 for (DashBoard* d : m_dashboards)
503 {
504 d->update(dt);
505 }
506}
507
509{
510 for (DashBoard* d : m_dashboards)
511 {
512 d->updateFeatures();
513 }
514}
515
517{
518 if (key >= data.size())
519 return 0;
520
521 switch (data[key].type)
522 {
523 case DC_BOOL:
524 return data[key].data.value_bool ? 1.0f : 0.0f;
525 case(DC_INT):
526 return (float)data[key].data.value_int;
527 case(DC_FLOAT):
528 return data[key].data.value_float;
529 }
530 return 0;
531}
532
533void DashBoardManager::setVisible(bool visibility)
534{
535 visible = visibility;
536 for (DashBoard* d : m_dashboards)
537 {
538 if (!d->getIsTextureLayer())
539 {
540 d->setVisible(visibility);
541 }
542 }
543}
544
546{
547 for (DashBoard* d : m_dashboards)
548 {
549 if (d->getIsTextureLayer())
550 {
551 d->setVisible(visibility, /*smooth:*/false);
552 }
553 }
554}
555
557{
558 for (DashBoard* d : m_dashboards)
559 {
560 d->windowResized();
561 }
562}
563
564// DASHBOARD class below
565
566DashBoard::DashBoard(DashBoardManager* manager, Ogre::String filename, int _textureLayerNum)
567 : manager(manager)
568 , filename(filename)
569 , free_controls(0)
570 , visible(false)
571 , mainWidget(nullptr)
572 , textureLayerNum(_textureLayerNum)
573{
574 // use 'this' class pointer to make layout unique
575 prefix = MyGUI::utility::toString(this, "_");
576
577 if (getIsTextureLayer())
578 {
579 rttLayer = fmt::format("RTTLayer{}", textureLayerNum);
580 rttTexture = fmt::format("RTTTexture{}", textureLayerNum);
581 }
582
583 memset(&controls, 0, sizeof(controls));
585 // hide first
586 if (mainWidget)
587 mainWidget->setVisible(false);
588}
589
591{
592 // Unload dashboard script
594 {
596 }
597 // Clear the GUI widgets
598 MyGUI::LayoutManager::getInstance().unloadLayout(widgets);
599 // Force unloading the '.layout' file from memory
600 MyGUI::ResourceManager::getInstance().removeByName(filename);
601}
602
603void DashBoard::loadScript(std::string scriptFilename, ActorPtr associatedActor)
604{
606 return;
607
608 scriptUnitID = App::GetScriptEngine()->loadScript(scriptFilename, ScriptCategory::ACTOR, associatedActor);
609}
610
612{
613 // this hides / shows parts of the gui depending on the vehicle features
614 for (int i = 0; i < free_controls; i++)
615 {
616 bool enabled = manager->getEnabled(controls[i].linkIDForVisibility);
617
618 controls[i].widget->setVisible(enabled);
619 }
620}
621
622const float DASH_SMOOTHING = 0.02;
623
624float DashBoard::getSmoothNumeric(int linkID, float& lastVal)
625{
626 if (manager->getDataType(linkID) != DC_FLOAT)
627 {
628 return manager->getNumeric(linkID); // Only smoothen FLOAT inputs
629 }
630 else
631 {
632 const float curVal = manager->getNumeric(linkID);
633 const float val = lastVal * (1 - DASH_SMOOTHING) + curVal * DASH_SMOOTHING;
634 lastVal = curVal;
635 return val;
636 }
637}
638
639void DashBoard::update(float dt)
640{
641 // walk all controls and animate them
642 for (int i = 0; i < free_controls; i++)
643 {
644 // Process graphical animation
645 if (controls[i].graphicalAnimation.animationType == ANIM_LAMP)
646 {
647 // or a lamp?
648 bool state = false;
649 // conditional
650 if (controls[i].graphicalAnimation.condition == CONDITION_GREATER)
651 {
652 float val = manager->getNumeric(controls[i].graphicalAnimation.linkID);
654 }
655 else if (controls[i].graphicalAnimation.condition == CONDITION_LESSER)
656 {
657 float val = manager->getNumeric(controls[i].graphicalAnimation.linkID);
659 }
660 else
661 {
662 state = (manager->getNumeric(controls[i].graphicalAnimation.linkID) > 0);
663 }
664
665 if (state == controls[i].lastState)
666 continue;
667 controls[i].lastState = state;
668
669 // switch states
670 if (state)
671 {
672 controls[i].img->setImageTexture(String(controls[i].graphicalAnimation.texture) + "-on.png");
673 }
674 else
675 {
676 controls[i].img->setImageTexture(String(controls[i].graphicalAnimation.texture) + "-off.png");
677 }
678 }
679 else if (controls[i].graphicalAnimation.animationType == ANIM_SERIES)
680 {
681 const float val = this->getSmoothNumeric(controls[i].graphicalAnimation.linkID, controls[i].graphicalAnimation.lastVal);
682
683 String fn = String(controls[i].graphicalAnimation.texture) + String("-") + TOSTRING((int)val) + String(".png");
684
685 controls[i].img->setImageTexture(fn);
686 }
687 else if (controls[i].graphicalAnimation.animationType == ANIM_TEXTFORMAT)
688 {
689 const float val = this->getSmoothNumeric(controls[i].graphicalAnimation.linkID, controls[i].graphicalAnimation.lastVal);
690
691 MyGUI::UString s;
692 if (strlen(controls[i].graphicalAnimation.format) == 0)
693 {
694 s = Ogre::StringConverter::toString(val);
695 }
696 else
697 {
698 char tmp[1024] = "";
699 sprintf(tmp, controls[i].graphicalAnimation.format, val);
700
701 // Detect and eliminate negative zero (-0) on output
702 if (strcmp(tmp, controls[i].graphicalAnimation.format_neg_zero) == 0)
703 {
704 sprintf(tmp, controls[i].graphicalAnimation.format, 0.f);
705 }
706
707 s = MyGUI::UString(tmp);
708 }
709
710 controls[i].txt->setCaption(s);
711 }
712 else if (controls[i].graphicalAnimation.animationType == ANIM_TEXTSTRING)
713 {
714 char* val = manager->getChar(controls[i].graphicalAnimation.linkID);
715 controls[i].txt->setCaption(MyGUI::UString(val));
716 }
717
718 float finalHorizontalTranslation = 0,
719 finalVerticalTranslation = 0;
720 for (int animIdx = 0; animIdx < controls[i].geometricAnimationCount; animIdx++)
721 {
723
724 // get its value from its linkage
725 if (animation.animationType == ANIM_ROTATE)
726 {
727 // get the value
728 const float val = this->getSmoothNumeric(animation.linkID, animation.lastVal);
729 // calculate the angle
730 float angle = (val - animation.vmin) * (animation.wmax - animation.wmin) / (animation.vmax - animation.vmin) + animation.wmin;
731
732 // enforce limits
733 if (angle < animation.wmin)
734 angle = animation.wmin;
735 else if (angle > animation.wmax)
736 angle = animation.wmax;
737 // rotate finally
738 controls[i].rotImg->setAngle(Ogre::Degree(angle).valueRadians());
739 }
740 else if (animation.animationType == ANIM_SCALE)
741 {
742 const float val = this->getSmoothNumeric(animation.linkID, animation.lastVal);
743
744 float scale = (val - animation.vmin) * (animation.wmax - animation.wmin) / (animation.vmax - animation.vmin) + animation.wmin;
745 if (animation.direction == DIRECTION_UP)
746 {
747 controls[i].widget->setPosition(controls[i].initialPosition.left, controls[i].initialPosition.top - scale);
748 controls[i].widget->setSize(controls[i].initialSize.width, controls[i].initialSize.height + scale);
749 }
750 else if (animation.direction == DIRECTION_DOWN)
751 {
752 controls[i].widget->setPosition(controls[i].initialPosition.left, controls[i].initialPosition.top);
753 controls[i].widget->setSize(controls[i].initialSize.width, controls[i].initialSize.height + scale);
754 }
755 else if (animation.direction == DIRECTION_LEFT)
756 {
757 controls[i].widget->setPosition(controls[i].initialPosition.left - scale, controls[i].initialPosition.top);
758 controls[i].widget->setSize(controls[i].initialSize.width + scale, controls[i].initialSize.height);
759 }
760 else if (animation.direction == DIRECTION_RIGHT)
761 {
762 controls[i].widget->setPosition(controls[i].initialPosition.left, controls[i].initialPosition.top);
763 controls[i].widget->setSize(controls[i].initialSize.width + scale, controls[i].initialSize.height);
764 }
765 }
766 else if (animation.animationType == ANIM_TRANSLATE)
767 {
768 const float val = this->getSmoothNumeric(animation.linkID, animation.lastVal);
769
770 float translation = (val - animation.vmin) * (animation.wmax - animation.wmin) / (animation.vmax - animation.vmin) + animation.wmin;
771 if (animation.direction == DIRECTION_UP)
772 finalVerticalTranslation -= translation;
773 else if (animation.direction == DIRECTION_DOWN)
774 finalVerticalTranslation += translation;
775 else if (animation.direction == DIRECTION_LEFT)
776 finalHorizontalTranslation -= translation;
777 else if (animation.direction == DIRECTION_RIGHT)
778 finalHorizontalTranslation += translation;
779 }
780 }
781
782 controls[i].widget->setPosition(controls[i].initialPosition.left + finalHorizontalTranslation, controls[i].initialPosition.top + finalVerticalTranslation);
783 }
784}
785
787{
788 if (!mainWidget)
789 return;
790 mainWidget->setPosition(0, 0);
791 if (getIsTextureLayer())
792 {
793 // texture layers are independent from the screen size, but rather from the layer texture size
794 TexturePtr tex = TextureManager::getSingleton().getByName(rttTexture);
795 if (tex)
796 mainWidget->setSize(tex->getWidth(), tex->getHeight());
797 }
798 else
799 {
800 MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();
801 mainWidget->setSize(screenSize);
802 }
803}
804
805bool DashBoard::parseLink(std::string& linkArgs, int& linkID, char& condition, float& conditionArgument, const std::string& filename, const std::string& name)
806{
807 replaceString(linkArgs, "&gt;", ">");
808 replaceString(linkArgs, "&lt;", "<");
809 String linkName = "";
810 if (linkArgs.empty())
811 {
812 LOG("Dashboard (" + filename + "/" + name + "): empty Link");
813 return false;
814 }
815 // conditional checks
816 // TODO: improve the logic, this is crap ...
817 if (linkArgs.find(">") != linkArgs.npos)
818 {
819 Ogre::StringVector args = Ogre::StringUtil::split(linkArgs, ">");
820 if (args.size() == 2)
821 {
822 linkName = args[0];
823 conditionArgument = StringConverter::parseReal(args[1]);
824 condition = CONDITION_GREATER;
825 }
826 else
827 {
828 LOG("Dashboard (" + filename + "/" + name + "): error in conditional Link: " + linkArgs);
829 return false;
830 }
831 }
832 else if (linkArgs.find("<") != linkArgs.npos)
833 {
834 Ogre::StringVector args = Ogre::StringUtil::split(linkArgs, "<");
835 if (args.size() == 2)
836 {
837 linkName = args[0];
838 conditionArgument = StringConverter::parseReal(args[1]);
839 condition = CONDITION_LESSER;
840 }
841 else
842 {
843 LOG("Dashboard (" + filename + "/" + name + "): error in conditional Link: " + linkArgs);
844 return false;
845 }
846 }
847 else
848 {
849 condition = CONDITION_NONE;
850 conditionArgument = 0;
851 linkName = linkArgs;
852 }
853
854 // now try to get the enum id for it
855 int linkIDResult = manager->getLinkIDForName(linkName);
856 if (linkIDResult < 0)
857 {
858 LOG("Dashboard (" + filename + "/" + name + "): unknown Link: " + linkName);
859 return false;
860 }
861
862 linkID = linkIDResult;
863 return true;
864}
865
866void DashBoard::loadLayoutRecursive(MyGUI::WidgetPtr w)
867{
868 std::string name = w->getName();
869 std::string debug = w->getUserString("debug");
870
871 // make it unclickable
872 w->setUserString("interactive", "0");
873
874 if (!debug.empty())
875 {
876 w->setVisible(false);
877 return;
878 }
879
880 // find the root widget and ignore debug widgets
881 if (name.size() > prefix.size())
882 {
883 std::string prefixLessName = name.substr(prefix.size());
884 if (prefixLessName == "_Main")
885 {
886 mainWidget = (MyGUI::WindowPtr)w;
887 // resize it
889 }
890
891 // ignore debug widgets
892 if (prefixLessName == "DEBUG")
893 {
894 w->setVisible(false);
895 return;
896 }
897 }
898
899 layoutLink_t ctrl;
900 memset(&ctrl, 0, sizeof(ctrl));
901
902 int linkID;
903 char condition;
904 float conditionArgument;
905
906 // Variable used to determine if one of the following
907 // animations is used:
908 // - series
909 // - textcolor/textcolour
910 // - textformat
911 // - textstring
912 // - lamp
913 // Only one of them can be specified for each widget.
914 std::string graphicalAnimationUsed = "";
915
916 int animNum = 1;
917 // Numbered properties (anim, anim2, link, link2, etc.)
918 // Allows to define multiple animations for a widget.
919 std::string animNumStr = "anim";
920 std::string linkNumStr = "link";
921 std::string minNumStr = "min";
922 std::string maxNumStr = "max";
923 std::string vminNumStr = "vmin";
924 std::string vmaxNumStr = "vmax";
925 std::string textureNumStr = "texture";
926 std::string formatNumStr = "format";
927 std::string directionNumStr = "direction";
928
929 std::string anim = w->getUserString(animNumStr);
930 std::string linkArgs;
931 if (anim != "")
932 {
933 bool linkIDForVisibilitySet = false;
934 while (anim != "")
935 {
936 linkArgs = w->getUserString(linkNumStr);
937
938 // animations for this control?
939 if (!linkArgs.empty())
940 {
941 char animType;
942 bool graphicalAnimation = false,
943 graphicalAnimationAlreadySet = false,
944 geometricAnimation = false;
945 if (anim == "rotate")
946 {
947 animType = ANIM_ROTATE;
948 geometricAnimation = true;
949 }
950 else if (anim == "scale")
951 {
952 animType = ANIM_SCALE;
953 geometricAnimation = true;
954 }
955 else if (anim == "translate")
956 {
957 animType = ANIM_TRANSLATE;
958 geometricAnimation = true;
959 }
960 else if (anim == "series")
961 {
962 animType = ANIM_SERIES;
963 graphicalAnimation = true;
964 if (graphicalAnimationUsed == "")
965 graphicalAnimationUsed = anim;
966 else
967 graphicalAnimationAlreadySet = true;
968 }
969 else if (anim == "textcolor" || anim == "textcolour")
970 {
971 animType = ANIM_TEXTCOLOR;
972 graphicalAnimation = true;
973 if (graphicalAnimationUsed == "")
974 graphicalAnimationUsed = anim;
975 else
976 graphicalAnimationAlreadySet = true;
977 }
978 else if (anim == "textformat")
979 {
980 animType = ANIM_TEXTFORMAT;
981 graphicalAnimation = true;
982 if (graphicalAnimationUsed == "")
983 graphicalAnimationUsed = anim;
984 else
985 graphicalAnimationAlreadySet = true;
986 }
987 else if (anim == "textstring")
988 {
989 animType = ANIM_TEXTSTRING;
990 graphicalAnimation = true;
991 if (graphicalAnimationUsed == "")
992 graphicalAnimationUsed = anim;
993 else
994 graphicalAnimationAlreadySet = true;
995 }
996 else if (anim == "lamp")
997 {
998 animType = ANIM_LAMP;
999 graphicalAnimation = true;
1000 if (graphicalAnimationUsed == "")
1001 graphicalAnimationUsed = anim;
1002 else
1003 graphicalAnimationAlreadySet = true;
1004 }
1005 else
1006 {
1007 LOG("Dashboard (" + filename + "/" + name + "): Unknown animation \"" + anim + "\".");
1008 return;
1009 }
1010
1011 if (graphicalAnimationAlreadySet)
1012 {
1013 LOG("Dashboard (" + filename + "/" + name + "): Animations \"" + anim + "\" and \"" + graphicalAnimationUsed + "\" can't be used at the same time.");
1014 return;
1015 }
1016
1017 ctrl.widget = w;
1018 ctrl.initialSize = w->getSize();
1019 ctrl.initialPosition = w->getPosition();
1020 ctrl.lastState = false;
1021 if (!name.empty())
1022 strncpy(ctrl.name, name.c_str(), sizeof ctrl.name);
1023
1024 bool linkParsed = parseLink(linkArgs, linkID, condition, conditionArgument, filename, name);
1025 if (!linkParsed)
1026 return;
1027
1028 // The first animation will determine the visibility of the widget.
1029 if (!linkIDForVisibilitySet)
1030 {
1031 ctrl.linkIDForVisibility = linkID;
1032 linkIDForVisibilitySet = true;
1033 }
1034
1035 if (graphicalAnimation)
1036 {
1037 ctrl.graphicalAnimation.linkID = linkID;
1038 ctrl.graphicalAnimation.animationType = animType;
1039 ctrl.graphicalAnimation.condition = condition;
1040 ctrl.graphicalAnimation.conditionArgument = conditionArgument;
1041
1042 String texture = w->getUserString(textureNumStr);
1043 if (!texture.empty())
1044 strncpy(ctrl.graphicalAnimation.texture, texture.c_str(), sizeof ctrl.graphicalAnimation.texture);
1045
1046 String format = w->getUserString(formatNumStr);
1047 if (!format.empty())
1048 strncpy(ctrl.graphicalAnimation.format, format.c_str(), sizeof ctrl.graphicalAnimation.format);
1049
1050 if (animType == ANIM_SERIES)
1051 {
1052 ctrl.img = (MyGUI::ImageBox*)w; //w->getSubWidgetMain()->castType<MyGUI::ImageBox>();
1053 if (!ctrl.img)
1054 {
1055 LOG("Dashboard (" + filename + "/" + name + "): error loading series control");
1056 return;
1057 }
1058 }
1059 else if (animType == ANIM_TEXTCOLOR)
1060 {
1061 // try to cast, will throw
1062 try
1063 {
1064 ctrl.txt = (MyGUI::TextBox*)w;
1065 }
1066 catch (...)
1067 {
1068 LOG("Dashboard (" + filename + "/" + name + "): textcolor controls must use the TextBox Control");
1069 return;
1070 }
1071 }
1072 else if (animType == ANIM_TEXTFORMAT)
1073 {
1074 // try to cast, will throw
1075 try
1076 {
1077 ctrl.txt = (MyGUI::TextBox*)w; // w->getSubWidgetMain()->castType<MyGUI::TextBox>();
1078 }
1079 catch (...)
1080 {
1081 LOG("Dashboard (" + filename + "/" + name + "): Lamp controls must use the ImageBox Control");
1082 return;
1083 }
1084
1085 // Prepare for eliminating negative zero (-0.0) display
1086 // Must be done on string-level because -0.001 with format "%1.0f" would still produce "-0"
1087 if (std::strlen(ctrl.graphicalAnimation.format))
1088 {
1090 }
1091 }
1092 else if (animType == ANIM_TEXTSTRING)
1093 {
1094 // try to cast, will throw
1095 try
1096 {
1097 ctrl.txt = (MyGUI::TextBox*)w; // w->getSubWidgetMain()->castType<MyGUI::TextBox>();
1098 }
1099 catch (...)
1100 {
1101 LOG("Dashboard (" + filename + "/" + name + "): Lamp controls must use the ImageBox Control");
1102 return;
1103 }
1104 }
1105 else if (animType == ANIM_LAMP)
1106 {
1107 // try to cast, will throw
1108 /*
1109 {
1110 try
1111 {
1112 w->getSubWidgetMain()->castType<MyGUI::ImageBox>();
1113 }
1114 catch (...)
1115 {
1116 LOG("Dashboard ("+filename+"/"+name+"): Lamp controls must use the ImageBox Control");
1117 continue;
1118 }
1119 }
1120 */
1121 ctrl.img = (MyGUI::ImageBox*)w; //w->getSubWidgetMain()->castType<MyGUI::ImageBox>();
1122 if (!ctrl.img)
1123 {
1124 LOG("Dashboard (" + filename + "/" + name + "): error loading Lamp control");
1125 return;
1126 }
1127 }
1128 }
1129 else if (geometricAnimation)
1130 {
1132 {
1133 LOG("Dashboard (" + filename + "/" + name + "): Maximum amount of geometric animations reached (" + TOSTRING(DD_MAX_GEOMETRIC_ANIMATIONS) + "). Ignoring the rest.");
1134 return;
1135 }
1136 else
1137 {
1139 geometricAnim.linkID = linkID;
1140 geometricAnim.animationType = animType;
1141
1143
1144 // parse more attributes
1145 geometricAnim.wmin = StringConverter::parseReal(w->getUserString(minNumStr));
1146 geometricAnim.wmax = StringConverter::parseReal(w->getUserString(maxNumStr));
1147 geometricAnim.vmin = StringConverter::parseReal(w->getUserString(vminNumStr));
1148 geometricAnim.vmax = StringConverter::parseReal(w->getUserString(vmaxNumStr));
1149
1150 String direction = w->getUserString(directionNumStr);
1151 if (direction == "right")
1152 geometricAnim.direction = DIRECTION_RIGHT;
1153 else if (direction == "left")
1154 geometricAnim.direction = DIRECTION_LEFT;
1155 else if (direction == "down")
1156 geometricAnim.direction = DIRECTION_DOWN;
1157 else if (direction == "up")
1158 geometricAnim.direction = DIRECTION_UP;
1159 else if (!direction.empty())
1160 {
1161 LOG("Dashboard (" + filename + "/" + name + "): unknown direction: " + direction);
1162 return;
1163 }
1164
1165 // then specializations
1166 if (animType == ANIM_ROTATE)
1167 {
1168 // check if its the correct control
1169 // try to cast, will throw
1170 // and if the link is a float
1171 /*
1172 if (manager->getDataType(ctrl.linkID) != DC_FLOAT)
1173 {
1174 LOG("Dashboard ("+filename+"/"+name+"): Rotating controls can only link to floats");
1175 continue;
1176 }
1177 */
1178
1179 try
1180 {
1181 ctrl.rotImg = w->getSubWidgetMain()->castType<MyGUI::RotatingSkin>();
1182 }
1183 catch (...)
1184 {
1185 LOG("Dashboard (" + filename + "/" + name + "): Rotating controls must use the RotatingSkin");
1186 return;
1187 }
1188 if (!ctrl.rotImg)
1189 {
1190 LOG("Dashboard (" + filename + "/" + name + "): error loading rotation control");
1191 return;
1192 }
1193
1194 // special: set rotation center now into the middle
1195 ctrl.rotImg->setCenter(MyGUI::IntPoint(w->getWidth() * 0.5f, w->getHeight() * 0.5f));
1196 }
1197 else if (animType == ANIM_SCALE)
1198 {
1199 if (geometricAnim.direction == DIRECTION_NONE)
1200 {
1201 LOG("Dashboard (" + filename + "/" + name + "): direction empty: scale needs a direction");
1202 return;
1203 }
1204 }
1205 else if (animType == ANIM_TRANSLATE)
1206 {
1207 if (geometricAnim.direction == DIRECTION_NONE)
1208 {
1209 LOG("Dashboard (" + filename + "/" + name + "): direction empty: translate needs a direction");
1210 return;
1211 }
1212 }
1213 }
1214 }
1215 }
1216
1217 animNum++;
1218 animNumStr = fmt::format("anim{}", animNum);
1219 linkNumStr = fmt::format("link{}", animNum);
1220 minNumStr = fmt::format("min{}", animNum);
1221 maxNumStr = fmt::format("max{}", animNum);
1222 vminNumStr = fmt::format("vmin{}", animNum);
1223 vmaxNumStr = fmt::format("vmax{}", animNum);
1224 textureNumStr = fmt::format("texture{}", animNum);
1225 formatNumStr = fmt::format("format{}", animNum);
1226 directionNumStr = fmt::format("direction{}", animNum);
1227 anim = w->getUserString(animNumStr);
1228 }
1229
1230 controls[free_controls] = ctrl;
1231 free_controls++;
1233 {
1234 LOG("maximum amount of controls reached, discarding the rest: " + TOSTRING(MAX_CONTROLS));
1235 return;
1236 }
1237 }
1238 else
1239 {
1240 // Control with no animations. Process normal link.
1241 linkArgs = w->getUserString("link");
1242 // links for this control?
1243 if (!linkArgs.empty())
1244 {
1245 bool linkParsed = parseLink(linkArgs, linkID, condition, conditionArgument, filename, name);
1246 if (!linkParsed)
1247 return;
1248
1249 ctrl.linkIDForVisibility = linkID;
1250 ctrl.widget = w;
1251 ctrl.initialSize = w->getSize();
1252 ctrl.initialPosition = w->getPosition();
1253 ctrl.lastState = false;
1254
1255 controls[free_controls] = ctrl;
1256 free_controls++;
1258 {
1259 LOG("maximum amount of controls reached, discarding the rest: " + TOSTRING(MAX_CONTROLS));
1260 return;
1261 }
1262 }
1263 }
1264
1265 // walk the children now
1266 MyGUI::EnumeratorWidgetPtr e = w->getEnumerator();
1267 while (e.next())
1268 {
1269 loadLayoutRecursive(e.current());
1270 }
1271}
1272
1274{
1275 widgets = MyGUI::LayoutManager::getInstance().loadLayout(filename, prefix, nullptr); // never has a parent
1276
1277 for (MyGUI::VectorWidgetPtr::iterator iter = widgets.begin(); iter != widgets.end(); ++iter)
1278 {
1279 loadLayoutRecursive(*iter);
1280 }
1281
1282 // if this thing should be rendered to texture, relocate the main window to the RTT layer
1284 mainWidget->detachFromWidget(rttLayer);
1285}
1286
1287void DashBoard::setVisible(bool v, bool smooth)
1288{
1289 visible = v;
1290
1291 if (!mainWidget)
1292 {
1293 for (MyGUI::VectorWidgetPtr::iterator iter = widgets.begin(); iter != widgets.end(); ++iter)
1294 {
1295 (*iter)->setVisible(v);
1296 }
1297 return;
1298 }
1299
1300 /*
1301 // buggy for some reason
1302 if (smooth)
1303 mainWidget->setVisibleSmooth(v);
1304 */
1305 mainWidget->setVisible(v);
1306}
Central state/object manager and communications hub.
#define TOSTRING(x)
Definition Application.h:57
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define BITMASK_IS_1(VAR, FLAGS)
Definition BitFlags.h:14
#define BITMASK_IS_0(VAR, FLAGS)
Definition BitFlags.h:13
uint32_t BitMask_t
Definition BitFlags.h:7
A database of user-installed content alias 'mods' (vehicles, terrains...)
constexpr int DASHTAG_RPM_NONE
constexpr char DASHTAG_XPH_NONE
static char DashXPH(const std::string &input)
const float DASH_SMOOTHING
#define INITDATA(key, type, name)
static int DashRPM(const std::string &input)
#define MAX_CONTROLS
#define DD_MAX_GEOMETRIC_ANIMATIONS
#define NO_RTT_DASHBOARD
Generic text file parser.
EnginePtr ar_engine
Definition Actor.h:432
Ogre::String ar_design_name
Name of the vehicle/machine/object this actor represents.
Definition Actor.h:406
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:431
bool getBool() const
Definition CVar.h:98
Ogre::String fname
filename
Definition CacheSystem.h:67
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
Definition CacheSystem.h:89
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
void LoadResource(CacheEntryPtr &t)
Loads the associated resource bundle if not already done.
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition Console.h:63
@ CONSOLE_MSGTYPE_INFO
Generic message.
Definition Console.h:60
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_ERROR
Definition Console.h:52
@ CONSOLE_SYSTEM_WARNING
Definition Console.h:53
MyGUI::WindowPtr mainWidget
Ogre::String filename
layoutLink_t controls[MAX_CONTROLS]
ScriptUnitID_t scriptUnitID
DashBoard(DashBoardManager *manager, Ogre::String filename, int textureLayerNum)
void setVisible(bool visible, bool smooth=true)
std::string rttTexture
void update(float dt)
float getSmoothNumeric(int linkID, float &lastVal)
void loadScript(std::string scriptFilename, ActorPtr associatedActor)
bool parseLink(std::string &linkArgs, int &linkID, char &condition, float &conditionArgument, const std::string &filename, const std::string &name)
std::string rttLayer
MyGUI::VectorWidgetPtr widgets
DashBoardManager * manager
void loadLayoutRecursive(MyGUI::WidgetPtr ptr)
void setVisible(bool visibility)
char * getChar(size_t key)
bool getEnabled(size_t key)
std::vector< dashData_t > data
int getLinkIDForName(Ogre::String &str)
void setVisible3d(bool visibility)
void loadDashboardModDetails(CacheEntryPtr &entry)
int getDataType(size_t key)
std::string determineTruckLayoutFromDashboardMod(Ogre::FileInfoListPtr &filelist)
int registerCustomInput(Ogre::String name, int dataType)
virtual ~DashBoardManager() override
std::string getLinkNameForID(DashData id)
std::vector< DashBoard * > m_dashboards
DashBoardManager(ActorPtr actor)
float getNumeric(size_t key)
void loadDashBoard(const std::string &filename, BitMask_t flags)
std::string determineLayoutFromDashboardMod(CacheEntryPtr &entry, std::string const &basename)
float getShiftUpRPM() const
Shift up RPM ('engine' attr #2)
Definition Engine.h:58
ScriptUnitID_t loadScript(Ogre::String filename, ScriptCategory category=ScriptCategory::TERRAIN, ActorPtr associatedActor=nullptr, std::string buffer="")
Loads a script.
void unloadScript(ScriptUnitID_t unique_id)
Unloads a script.
@ TRUCK
its a truck (or other land vehicle)
Definition SimData.h:85
@ BOAT
its a boat
Definition SimData.h:87
@ AIRPLANE
its an airplane
Definition SimData.h:86
@ ACTOR
Defined in truck file under 'scripts', contains global variable BeamClass@ thisActor.
Console * GetConsole()
ScriptEngine * GetScriptEngine()
CacheSystem * GetCacheSystem()
CVar * gfx_speedo_imperial
@ LT_DashBoard
@ DD_ENGINE_RPM
@ DD_AEROENGINE_THROTTLE_4
@ DD_SCREW_STEER_2
@ DD_CUSTOM_LIGHT6
custom light 6 on
@ DD_CUSTOM_LIGHT7
custom light 7 on
@ DD_ODOMETER_TOTAL
@ DD_CUSTOM_LIGHT10
custom light 10 on
@ DD_ENGINE_BATTERY
@ DD_SCREW_THROTTLE_2
@ DD_CUSTOM_LIGHT2
custom light 2 on
@ DD_CUSTOM_LIGHT4
custom light 4 on
@ DD_ENGINE_NUM_GEAR
current gear
@ DD_AEROENGINE_FAILED_4
@ DD_SCREW_THROTTLE_4
@ DD_AEROENGINE_RPM_1
@ DD_LIGHTS_LEGACY
Alias of 'sidelights'.
@ DD_ENGINE_SPEEDO_KPH
@ DD_AEROENGINE_FAILED_5
@ DD_SCREW_STEER_5
@ DD_ENGINE_AUTOGEAR_STRING
string like "<current gear>/<max gear>"
@ DD_AEROENGINE_RPM_5
@ DD_AEROENGINE_FAILED_2
@ DD_SCREW_STEER_1
@ DD_AEROENGINE_RPM_4
@ DD_PARKINGBRAKE
chassis pitch
@ DD_AEROENGINE_THROTTLE_2
@ DD_SCREW_THROTTLE_3
@ DD_CUSTOM_LIGHT9
custom light 9 on
@ DD_ENGINE_GEAR_STRING
@ DD_SIGNAL_WARNING
The warning-blink indicator is lit.
@ DD_SCREW_STEER_4
@ DD_CUSTOM_LIGHT3
custom light 3 on
@ DD_BRAKE_LIGHTS
@ DD_ANTILOCKBRAKE_MODE
@ DD_SIGNAL_TURNLEFT
Left blinker is lit.
@ DD_ENGINE_SPEEDO_MPH
speedo in kilometer per hour
@ DD_AEROENGINE_FAILED_3
@ DD_SCREW_THROTTLE_1
@ DD_ENGINE_IGNITION
turbo gauge
@ DD_AEROENGINE_RPM_0
@ DD_CUSTOM_LIGHT8
custom light 8 on
@ DD_ROLL_CORR_ACTIVE
@ DD_AEROENGINE_THROTTLE_5
@ DD_ENGINE_GEAR
clutch warning lamp
@ DD_AEROENGINE_THROTTLE_0
@ DD_AEROENGINE_FAILED_0
@ DD_AEROENGINE_FAILED_1
@ DD_AEROENGINE_RPM_2
@ DD_SCREW_THROTTLE_5
@ DD_LOCKED
parking brake status
@ DD_ENGINE_AUTO_GEAR
string like "P R N G"
@ DD_ODOMETER_USER
@ DD_SCREW_STEER_3
@ DD_REVERSE_LIGHT
@ DD_SIGNAL_TURNRIGHT
Right blinker is lit.
@ DD_CUSTOM_LIGHT5
custom light 5 on
@ DD_ENGINE_CLUTCH_WARNING
battery lamp
@ DD_LOW_PRESSURE
locked lamp
@ DD_ENGINE_TURBO
speedo in miles per hour
@ DD_AEROENGINE_RPM_3
@ DD_ENGINE_CLUTCH
automatic gear
@ DD_CUSTOM_LIGHT1
custom light 1 on
@ DD_ALTITUDE_STRING
@ DD_TRACTIONCONTROL_MODE
low pressure
@ DD_AEROENGINE_THROTTLE_3
@ DD_SCREW_STEER_0
@ DD_SCREW_THROTTLE_0
ties locked
@ DD_AEROENGINE_THROTTLE_1
@ LOADDASHBOARD_RTT_TEXTURE
Will be drawn to texture. Unless STACKABLE, it prevents the default dashboard from loading.
@ LOADDASHBOARD_SCREEN_HUD
Will be drawn to screen. Unless STACKABLE, it prevents the default dashboard from loading.
@ LOADDASHBOARD_STACKABLE
Allows loading multiple dashboards at once (by default there's only one for screen and one for RTT).
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
static const ScriptUnitID_t SCRIPTUNITID_INVALID
void replaceString(std::string &str, std::string searchString, std::string replaceString)
Definition Utils.h:51
DashCandidateLayout(const std::string &filename)
char format_neg_zero[255]
Test for undesired '-0.0' on display. Only for link type "format". Empty if not applicable.
bool isTokKeyword(int offset=0) const
std::string getTokKeyword(int offset=0) const
bool endOfFile(int offset=0) const
std::string getTokString(int offset=0) const
static const BitMask_t OPTION_ALLOW_SLASH_COMMENTS
Allow comments starting with //.
virtual void loadFromDataStream(Ogre::DataStreamPtr datastream, BitMask_t options=0)
static const BitMask_t OPTION_ALLOW_NAKED_STRINGS
Allow strings without quotes, for backwards compatibility.