39#define INITDATA(key, type, name) data[key] = dashData_t(type, name)
144 MyGUI::ResourceManager::getInstance().load(
"MyGUI_FontsDash.xml");
168 fmt::format(
"{}: Cannot register dashboard custom input \"{}\", name conflicts with existing one. Ignoring it.",
m_actor->
ar_design_name, name));
174 fmt::format(
"{}: Cannot register dashboard custom input \"{}\", invalid data type.",
m_actor->
ar_design_name, name));
189 for (
int i = 0; i <
data.size(); i++)
191 if (
data[i].name == str)
199 if (
id >= 0 &&
id <
data.size())
201 return data[id].name;
216 std::regex rpm_regex(R
"((\d+)rpm)");
218 if (std::regex_search(input, match, rpm_regex)) {
219 std::string rpm = match[1];
220 return std::atoi(rpm.c_str());
227 std::regex xph_regex(R
"(([km])ph)");
229 if (std::regex_search(input, match, xph_regex)) {
230 return match[1].str()[0];
243 this->filename = filename;
252 Ogre::FileInfoListPtr filelist
253 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->
resource_group, fmt::format(
"{}*.layout", basename));
255 if (filelist->empty())
266 return filelist->begin()->filename;
291 std::vector<DashCandidateLayout> candidates;
293 for (Ogre::FileInfo& fileinfo : *filelist)
295 candidates.emplace_back(fileinfo.filename);
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)
303 if (candidate.xph == desiredX)
305 float rpm_diff = (float)candidate.
rpm - redlineRPM;
306 if (rpm_diff < 0 && rpm_diff > least_undershoot)
308 least_undershoot = rpm_diff;
309 undershoot_candidate = &candidate;
311 else if (rpm_diff >= 0 && rpm_diff < least_overshoot)
313 least_overshoot = rpm_diff;
314 overshoot_candidate = &candidate;
319 if (overshoot_candidate)
321 return overshoot_candidate->
filename;
323 else if (undershoot_candidate)
326 fmt::format(
"{}: No ideal dashboard found, using one with least RPM undershoot",
m_actor->
ar_design_name));
327 return undershoot_candidate->
filename;
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)
337 float rpm_diff = (float)candidate.
rpm - redlineRPM;
338 if (rpm_diff < 0 && rpm_diff > least_undershoot)
340 least_undershoot = rpm_diff;
341 undershoot_candidate = &candidate;
343 else if (rpm_diff >= 0 && rpm_diff < least_overshoot)
345 least_overshoot = rpm_diff;
346 overshoot_candidate = &candidate;
350 if (overshoot_candidate)
352 return overshoot_candidate->
filename;
354 else if (undershoot_candidate)
357 fmt::format(
"{}: No ideal dashboard found, using one with least RPM undershoot",
m_actor->
ar_design_name));
358 return undershoot_candidate->
filename;
361 return filelist->begin()->filename;
368 DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(entry->
fname, entry->
resource_group);
378 bool valid_custom_input =
true;
380 int custom_input_data_type;
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; }
389 valid_custom_input =
false;
391 fmt::format(
"Dashboard Custom Input: Invalid data type \"{}\" for \"{}\"", data_type_str, custom_input_name));
394 if (valid_custom_input)
403 catch (Ogre::Exception& e)
405 RoR::LogFormat(
"[RoR|DashBoardManager] Error processing file '%s', message :%s",
406 entry->
fname.c_str(), e.getFullDescription().c_str());
418 std::string basename, ext, layoutfname, scriptfilename =
"";
419 Ogre::StringUtil::splitBaseFilename(filename, basename, ext);
420 if (ext ==
"dashboard")
426 fmt::format(
"DashboardManager: Could not find dashboard file '{}'", filename));
435 Ogre::FileInfoListPtr filelist
436 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->
resource_group, fmt::format(
"{}*.resource", basename));
437 for (Ogre::FileInfo& fileinfo : *filelist)
439 MyGUI::ResourceManager::getInstance().load(fileinfo.filename);
443 Ogre::FileInfoListPtr scriptFile
444 = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(entry->
resource_group, fmt::format(
"{}.as", basename));
445 if (scriptFile->size() > 0)
447 scriptfilename = scriptFile->front().filename;
452 layoutfname = filename;
455 if (layoutfname ==
"")
458 fmt::format(
"{}: Cannot load dashboard '{}' - no applicable layout file found",
m_actor->
ar_design_name, filename));
471 if (scriptfilename !=
"")
487 if (scriptfilename !=
"")
518 if (key >=
data.size())
521 switch (
data[key].type)
524 return data[key].data.value_bool ? 1.0f : 0.0f;
526 return (
float)
data[key].data.value_int;
528 return data[key].data.value_float;
538 if (!d->getIsTextureLayer())
540 d->setVisible(visibility);
549 if (d->getIsTextureLayer())
551 d->setVisible(visibility,
false);
571 , mainWidget(nullptr)
572 , textureLayerNum(_textureLayerNum)
575 prefix = MyGUI::utility::toString(
this,
"_");
598 MyGUI::LayoutManager::getInstance().unloadLayout(
widgets);
600 MyGUI::ResourceManager::getInstance().removeByName(
filename);
672 controls[i].
img->setImageTexture(String(
controls[i].graphicalAnimation.texture) +
"-on.png");
676 controls[i].
img->setImageTexture(String(
controls[i].graphicalAnimation.texture) +
"-off.png");
683 String fn = String(
controls[i].graphicalAnimation.texture) + String(
"-") +
TOSTRING((
int)val) + String(
".png");
692 if (strlen(
controls[i].graphicalAnimation.format) == 0)
694 s = Ogre::StringConverter::toString(val);
699 sprintf(tmp,
controls[i].graphicalAnimation.format, val);
702 if (strcmp(tmp,
controls[i].graphicalAnimation.format_neg_zero) == 0)
704 sprintf(tmp,
controls[i].graphicalAnimation.format, 0.f);
707 s = MyGUI::UString(tmp);
718 float finalHorizontalTranslation = 0,
719 finalVerticalTranslation = 0;
730 float angle = (val - animation.
vmin) * (animation.
wmax - animation.
wmin) / (animation.
vmax - animation.
vmin) + animation.
wmin;
733 if (angle < animation.
wmin)
734 angle = animation.
wmin;
735 else if (angle > animation.
wmax)
736 angle = animation.
wmax;
744 float scale = (val - animation.
vmin) * (animation.
wmax - animation.
wmin) / (animation.
vmax - animation.
vmin) + animation.
wmin;
770 float translation = (val - animation.
vmin) * (animation.
wmax - animation.
wmin) / (animation.
vmax - animation.
vmin) + animation.
wmin;
772 finalVerticalTranslation -= translation;
774 finalVerticalTranslation += translation;
776 finalHorizontalTranslation -= translation;
778 finalHorizontalTranslation += translation;
794 TexturePtr tex = TextureManager::getSingleton().getByName(
rttTexture);
796 mainWidget->setSize(tex->getWidth(), tex->getHeight());
800 MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();
805bool DashBoard::parseLink(std::string& linkArgs,
int& linkID,
char& condition,
float& conditionArgument,
const std::string& filename,
const std::string& name)
809 String linkName =
"";
810 if (linkArgs.empty())
812 LOG(
"Dashboard (" +
filename +
"/" + name +
"): empty Link");
817 if (linkArgs.find(
">") != linkArgs.npos)
819 Ogre::StringVector args = Ogre::StringUtil::split(linkArgs,
">");
820 if (args.size() == 2)
823 conditionArgument = StringConverter::parseReal(args[1]);
828 LOG(
"Dashboard (" +
filename +
"/" + name +
"): error in conditional Link: " + linkArgs);
832 else if (linkArgs.find(
"<") != linkArgs.npos)
834 Ogre::StringVector args = Ogre::StringUtil::split(linkArgs,
"<");
835 if (args.size() == 2)
838 conditionArgument = StringConverter::parseReal(args[1]);
843 LOG(
"Dashboard (" +
filename +
"/" + name +
"): error in conditional Link: " + linkArgs);
850 conditionArgument = 0;
856 if (linkIDResult < 0)
858 LOG(
"Dashboard (" +
filename +
"/" + name +
"): unknown Link: " + linkName);
862 linkID = linkIDResult;
805bool DashBoard::parseLink(std::string& linkArgs,
int& linkID,
char& condition,
float& conditionArgument,
const std::string& filename,
const std::string& name) {
…}
868 std::string name =
w->getName();
869 std::string debug =
w->getUserString(
"debug");
872 w->setUserString(
"interactive",
"0");
876 w->setVisible(
false);
881 if (name.size() >
prefix.size())
883 std::string prefixLessName = name.substr(
prefix.size());
884 if (prefixLessName ==
"_Main")
892 if (prefixLessName ==
"DEBUG")
894 w->setVisible(
false);
900 memset(&ctrl, 0,
sizeof(ctrl));
904 float conditionArgument;
914 std::string graphicalAnimationUsed =
"";
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";
929 std::string anim =
w->getUserString(animNumStr);
930 std::string linkArgs;
933 bool linkIDForVisibilitySet =
false;
936 linkArgs =
w->getUserString(linkNumStr);
939 if (!linkArgs.empty())
942 bool graphicalAnimation =
false,
943 graphicalAnimationAlreadySet =
false,
944 geometricAnimation =
false;
945 if (anim ==
"rotate")
948 geometricAnimation =
true;
950 else if (anim ==
"scale")
953 geometricAnimation =
true;
955 else if (anim ==
"translate")
958 geometricAnimation =
true;
960 else if (anim ==
"series")
963 graphicalAnimation =
true;
964 if (graphicalAnimationUsed ==
"")
965 graphicalAnimationUsed = anim;
967 graphicalAnimationAlreadySet =
true;
969 else if (anim ==
"textcolor" || anim ==
"textcolour")
972 graphicalAnimation =
true;
973 if (graphicalAnimationUsed ==
"")
974 graphicalAnimationUsed = anim;
976 graphicalAnimationAlreadySet =
true;
978 else if (anim ==
"textformat")
981 graphicalAnimation =
true;
982 if (graphicalAnimationUsed ==
"")
983 graphicalAnimationUsed = anim;
985 graphicalAnimationAlreadySet =
true;
987 else if (anim ==
"textstring")
990 graphicalAnimation =
true;
991 if (graphicalAnimationUsed ==
"")
992 graphicalAnimationUsed = anim;
994 graphicalAnimationAlreadySet =
true;
996 else if (anim ==
"lamp")
999 graphicalAnimation =
true;
1000 if (graphicalAnimationUsed ==
"")
1001 graphicalAnimationUsed = anim;
1003 graphicalAnimationAlreadySet =
true;
1007 LOG(
"Dashboard (" +
filename +
"/" + name +
"): Unknown animation \"" + anim +
"\".");
1011 if (graphicalAnimationAlreadySet)
1013 LOG(
"Dashboard (" +
filename +
"/" + name +
"): Animations \"" + anim +
"\" and \"" + graphicalAnimationUsed +
"\" can't be used at the same time.");
1022 strncpy(ctrl.
name, name.c_str(),
sizeof ctrl.
name);
1024 bool linkParsed =
parseLink(linkArgs, linkID, condition, conditionArgument,
filename, name);
1029 if (!linkIDForVisibilitySet)
1032 linkIDForVisibilitySet =
true;
1035 if (graphicalAnimation)
1042 String texture =
w->getUserString(textureNumStr);
1043 if (!texture.empty())
1046 String format =
w->getUserString(formatNumStr);
1047 if (!format.empty())
1052 ctrl.
img = (MyGUI::ImageBox*)
w;
1055 LOG(
"Dashboard (" +
filename +
"/" + name +
"): error loading series control");
1064 ctrl.
txt = (MyGUI::TextBox*)
w;
1068 LOG(
"Dashboard (" +
filename +
"/" + name +
"): textcolor controls must use the TextBox Control");
1077 ctrl.
txt = (MyGUI::TextBox*)
w;
1081 LOG(
"Dashboard (" +
filename +
"/" + name +
"): Lamp controls must use the ImageBox Control");
1097 ctrl.
txt = (MyGUI::TextBox*)
w;
1101 LOG(
"Dashboard (" +
filename +
"/" + name +
"): Lamp controls must use the ImageBox Control");
1121 ctrl.
img = (MyGUI::ImageBox*)
w;
1124 LOG(
"Dashboard (" +
filename +
"/" + name +
"): error loading Lamp control");
1129 else if (geometricAnimation)
1139 geometricAnim.
linkID = linkID;
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));
1150 String direction =
w->getUserString(directionNumStr);
1151 if (direction ==
"right")
1153 else if (direction ==
"left")
1155 else if (direction ==
"down")
1157 else if (direction ==
"up")
1159 else if (!direction.empty())
1161 LOG(
"Dashboard (" +
filename +
"/" + name +
"): unknown direction: " + direction);
1181 ctrl.
rotImg =
w->getSubWidgetMain()->castType<MyGUI::RotatingSkin>();
1185 LOG(
"Dashboard (" +
filename +
"/" + name +
"): Rotating controls must use the RotatingSkin");
1190 LOG(
"Dashboard (" +
filename +
"/" + name +
"): error loading rotation control");
1195 ctrl.
rotImg->setCenter(MyGUI::IntPoint(
w->getWidth() * 0.5f,
w->getHeight() * 0.5f));
1201 LOG(
"Dashboard (" +
filename +
"/" + name +
"): direction empty: scale needs a direction");
1209 LOG(
"Dashboard (" +
filename +
"/" + name +
"): direction empty: translate needs a direction");
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);
1241 linkArgs =
w->getUserString(
"link");
1243 if (!linkArgs.empty())
1245 bool linkParsed =
parseLink(linkArgs, linkID, condition, conditionArgument,
filename, name);
1266 MyGUI::EnumeratorWidgetPtr e =
w->getEnumerator();
1277 for (MyGUI::VectorWidgetPtr::iterator iter =
widgets.begin(); iter !=
widgets.end(); ++iter)
1293 for (MyGUI::VectorWidgetPtr::iterator iter =
widgets.begin(); iter !=
widgets.end(); ++iter)
1295 (*iter)->setVisible(v);
Central state/object manager and communications hub.
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define BITMASK_IS_1(VAR, FLAGS)
#define BITMASK_IS_0(VAR, FLAGS)
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 DD_MAX_GEOMETRIC_ANIMATIONS
Ogre::String ar_design_name
Name of the vehicle/machine/object this actor represents.
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Ogre::String fname
filename
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
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.
@ CONSOLE_MSGTYPE_INFO
Generic message.
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
void loadLayoutInternal()
MyGUI::WindowPtr mainWidget
layoutLink_t controls[MAX_CONTROLS]
ScriptUnitID_t scriptUnitID
DashBoard(DashBoardManager *manager, Ogre::String filename, int textureLayerNum)
void setVisible(bool visible, bool smooth=true)
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)
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)
int registeredCustomInputs
std::string determineLayoutFromDashboardMod(CacheEntryPtr &entry, std::string const &basename)
float getShiftUpRPM() const
Shift up RPM ('engine' attr #2)
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)
@ AIRPLANE
its an airplane
@ ACTOR
Defined in truck file under 'scripts', contains global variable BeamClass@ thisActor.
ScriptEngine * GetScriptEngine()
CacheSystem * GetCacheSystem()
CVar * gfx_speedo_imperial
@ DD_AEROENGINE_THROTTLE_4
@ DD_CUSTOM_LIGHT6
custom light 6 on
@ DD_CUSTOM_LIGHT7
custom light 7 on
@ DD_CUSTOM_LIGHT10
custom light 10 on
@ DD_CUSTOM_LIGHT2
custom light 2 on
@ DD_CUSTOM_LIGHT4
custom light 4 on
@ DD_ENGINE_NUM_GEAR
current gear
@ DD_LIGHTS_LEGACY
Alias of 'sidelights'.
@ DD_ENGINE_AUTOGEAR_STRING
string like "<current gear>/<max gear>"
@ DD_PARKINGBRAKE
chassis pitch
@ DD_AEROENGINE_THROTTLE_2
@ DD_CUSTOM_LIGHT9
custom light 9 on
@ DD_SIGNAL_WARNING
The warning-blink indicator is lit.
@ DD_CUSTOM_LIGHT3
custom light 3 on
@ DD_SIGNAL_TURNLEFT
Left blinker is lit.
@ DD_ENGINE_SPEEDO_MPH
speedo in kilometer per hour
@ DD_ENGINE_IGNITION
turbo gauge
@ DD_CUSTOM_LIGHT8
custom light 8 on
@ DD_AEROENGINE_THROTTLE_5
@ DD_ENGINE_GEAR
clutch warning lamp
@ DD_AEROENGINE_THROTTLE_0
@ DD_LOCKED
parking brake status
@ DD_ENGINE_AUTO_GEAR
string like "P R N G"
@ 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_ENGINE_CLUTCH
automatic gear
@ DD_CUSTOM_LIGHT1
custom light 1 on
@ DD_TRACTIONCONTROL_MODE
low pressure
@ DD_AEROENGINE_THROTTLE_3
@ 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)
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.
layoutGeometricAnimation_t geometricAnimations[DD_MAX_GEOMETRIC_ANIMATIONS]
MyGUI::IntSize initialSize
MyGUI::IntPoint initialPosition
MyGUI::RotatingSkin * rotImg
layoutGraphicalAnimation_t graphicalAnimation
int geometricAnimationCount
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.