RigsofRods
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
RigDef_Parser.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  Copyright 2013-2021 Petr Ohlidal
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
25 
26 #include "RigDef_Parser.h"
27 
28 #include "Actor.h"
29 #include "Application.h"
30 #include "SimConstants.h"
31 #include "CacheSystem.h"
32 #include "Console.h"
33 #include "RigDef_File.h"
34 #include "RigDef_Regexes.h"
35 #include "Utils.h"
36 
37 #include <OgreException.h>
38 #include <OgreString.h>
39 #include <OgreStringVector.h>
40 #include <OgreStringConverter.h>
41 
42 #include <algorithm>
43 
44 using namespace RoR;
45 
46 namespace RigDef
47 {
48 
49 inline bool IsWhitespace(char c)
50 {
51  return (c == ' ') || (c == '\t');
52 }
53 
54 inline bool IsSeparator(char c)
55 {
56  return IsWhitespace(c) || (c == ':') || (c == '|') || (c == ',');
57 }
58 
59 inline bool StrEqualsNocase(std::string const & s1, std::string const & s2)
60 {
61  if (s1.size() != s2.size()) { return false; }
62  for (size_t i = 0; i < s1.size(); ++i)
63  {
64  if (tolower(s1[i]) != tolower(s2[i])) { return false; }
65  }
66  return true;
67 }
68 
69 Parser::Parser()
70 {
71  // Push defaults
72  m_ror_default_inertia = std::shared_ptr<Inertia>(new Inertia);
73  m_ror_node_defaults = std::shared_ptr<NodeDefaults>(new NodeDefaults);
74 }
75 
76 void Parser::ProcessCurrentLine()
77 {
78  // Ignore comment lines
79  if ((m_current_line[0] == ';') || (m_current_line[0] == '/'))
80  {
81  return;
82  }
83 
84  // First line in file (except blanks or comments) is the actor name
85  if (m_definition->name == "" && m_current_line != "")
86  {
87  m_definition->name = m_current_line; // Already trimmed
88  return;
89  }
90 
91  // Split line to tokens
92  if (m_current_block != Keyword::COMMENT &&
93  m_current_block != Keyword::DESCRIPTION)
94  {
95  this->TokenizeCurrentLine();
96  }
97 
98  // Detect keyword on current line
99  Keyword keyword = Keyword::INVALID;
100  // Quick check - keyword always starts with ASCII letter
101  char c = tolower(m_current_line[0]); // Note: line comes in trimmed
102  if (c >= 'a' && c <= 'z')
103  {
104  keyword = Parser::IdentifyKeyword(m_current_line);
105  }
106  m_log_keyword = keyword;
107  switch (keyword)
108  {
109  // No keyword - Continue below to process current block.
110  case Keyword::INVALID:
111  break; // << NOT RETURN.
112 
113  // Directives without arguments: just record, do not change current block.
114  case Keyword::DISABLEDEFAULTSOUNDS:
115  case Keyword::ENABLE_ADVANCED_DEFORMATION:
116  case Keyword::FORWARDCOMMANDS:
117  case Keyword::HIDEINCHOOSER:
118  case Keyword::IMPORTCOMMANDS:
119  case Keyword::LOCKGROUP_DEFAULT_NOLOCK:
120  case Keyword::RESCUER:
121  case Keyword::ROLLON:
122  case Keyword::SLIDENODE_CONNECT_INSTANTLY:
123  this->ProcessGlobalDirective(keyword);
124  return;
125  case Keyword::END_SECTION:
126  this->ProcessChangeModuleLine(keyword);
127  return;
128 
129  // Directives with arguments: process immediately, do not change current block.
130  case Keyword::ADD_ANIMATION:
131  this->ParseDirectiveAddAnimation();
132  return;
133  case Keyword::ANTILOCKBRAKES:
134  this->ParseAntiLockBrakes();
135  return;
136  case Keyword::AUTHOR:
137  this->ParseAuthor();
138  return;
139  case Keyword::BACKMESH:
140  this->ParseDirectiveBackmesh();
141  return;
142  case Keyword::CRUISECONTROL:
143  this->ParseCruiseControl();
144  return;
145  case Keyword::DEFAULT_SKIN:
146  this->ParseDirectiveDefaultSkin();
147  return;
148  case Keyword::DETACHER_GROUP:
149  this->ParseDirectiveDetacherGroup();
150  return;
151  case Keyword::EXTCAMERA:
152  this->ParseExtCamera();
153  return;
154  case Keyword::FILEFORMATVERSION:
155  this->ParseFileFormatVersion();
156  return;
157  case Keyword::FILEINFO:
158  this->ParseFileinfo();
159  return;
160  case Keyword::FLEXBODY_CAMERA_MODE:
161  this->ParseDirectiveFlexbodyCameraMode();
162  return;
163  case Keyword::FORSET:
164  this->ParseDirectiveForset();
165  return;
166  case Keyword::GUID:
167  this->ParseGuid();
168  return;
169  case Keyword::PROP_CAMERA_MODE:
170  this->ParseDirectivePropCameraMode();
171  return;
172  case Keyword::SECTION:
173  this->ParseDirectiveSection();
174  return;
175  case Keyword::SET_BEAM_DEFAULTS:
176  this->ParseDirectiveSetBeamDefaults();
177  return;
178  case Keyword::SET_BEAM_DEFAULTS_SCALE:
179  this->ParseDirectiveSetBeamDefaultsScale();
180  return;
181  case Keyword::SET_COLLISION_RANGE:
182  this->ParseSetCollisionRange();
183  return;
184  case Keyword::SET_DEFAULT_MINIMASS:
185  this->ParseDirectiveSetDefaultMinimass();
186  return;
187  case Keyword::SET_INERTIA_DEFAULTS:
188  this->ParseDirectiveSetInertiaDefaults();
189  return;
190  case Keyword::SET_MANAGEDMATERIALS_OPTIONS:
191  this->ParseDirectiveSetManagedMaterialsOptions();
192  return;
193  case Keyword::SET_NODE_DEFAULTS:
194  this->ParseDirectiveSetNodeDefaults();
195  return;
196  case Keyword::SET_SKELETON_SETTINGS:
197  this->ParseSetSkeletonSettings();
198  return;
199  case Keyword::SPEEDLIMITER:
200  this->ParseSpeedLimiter();
201  return;
202  case Keyword::SUBMESH:
203  this->ParseDirectiveSubmesh();
204  return;
205  case Keyword::SUBMESH_GROUNDMODEL:
206  this->ParseSubmeshGroundModel();
207  return;
208  case Keyword::TRACTIONCONTROL:
209  this->ParseTractionControl();
210  return;
211 
212  // Keywords which end current block:
213  case Keyword::END_COMMENT:
214  case Keyword::END_DESCRIPTION:
215  case Keyword::END:
216  this->BeginBlock(Keyword::INVALID);
217  return;
218 
219  // Ignored keywords (obsolete):
220  case Keyword::ENVMAP:
221  case Keyword::HOOKGROUP:
222  case Keyword::NODECOLLISION:
223  case Keyword::RIGIDIFIERS:
224  return;
225 
226  // Keywords which start new block:
227  default:
228  this->BeginBlock(keyword);
229  return;
230  }
231 
232  // Parse current block, if any
233  m_log_keyword = m_current_block;
234  switch (m_current_block)
235  {
236  case Keyword::AIRBRAKES: this->ParseAirbrakes(); return;
237  case Keyword::ANIMATORS: this->ParseAnimator(); return;
238  case Keyword::ASSETPACKS: this->ParseAssetpacks(); return;
239  case Keyword::AXLES: this->ParseAxles(); return;
240  case Keyword::BEAMS: this->ParseBeams(); return;
241  case Keyword::BRAKES: this->ParseBrakes(); return;
242  case Keyword::CAMERAS: this->ParseCameras(); return;
243  case Keyword::CAB: this->ParseCab(); return;
244  case Keyword::CAMERARAIL: this->ParseCameraRails(); return;
245  case Keyword::CINECAM: this->ParseCinecam(); return;
246  case Keyword::COMMANDS:
247  case Keyword::COMMANDS2: this->ParseCommandsUnified(); return;
248  case Keyword::COLLISIONBOXES: this->ParseCollisionBox(); return;
249  case Keyword::CONTACTERS: this->ParseContacter(); return;
250  case Keyword::DESCRIPTION: this->ParseDescription(); return;
251  case Keyword::ENGINE: this->ParseEngine(); return;
252  case Keyword::ENGOPTION: this->ParseEngoption(); return;
253  case Keyword::ENGTURBO: this->ParseEngturbo(); return;
254  case Keyword::EXHAUSTS: this->ParseExhaust(); return;
255  case Keyword::FIXES: this->ParseFixes(); return;
256  case Keyword::FLARES:
257  case Keyword::FLARES2: this->ParseFlaresUnified(); return;
258  case Keyword::FLARES3: this->ParseFlares3(); return;
259  case Keyword::FLAREGROUPS_NO_IMPORT:this->ParseFlaregroupsNoImport(); return;
260  case Keyword::FLEXBODIES: this->ParseFlexbody(); return;
261  case Keyword::FLEXBODYWHEELS: this->ParseFlexBodyWheel(); return;
262  case Keyword::FUSEDRAG: this->ParseFusedrag(); return;
263  case Keyword::GLOBALS: this->ParseGlobals(); return;
264  case Keyword::GUISETTINGS: this->ParseGuiSettings(); return;
265  case Keyword::HELP: this->ParseHelp(); return;
266  case Keyword::HOOKS: this->ParseHook(); return;
267  case Keyword::HYDROS: this->ParseHydros(); return;
268  case Keyword::INTERAXLES: this->ParseInterAxles(); return;
269  case Keyword::LOCKGROUPS: this->ParseLockgroups(); return;
270  case Keyword::MANAGEDMATERIALS: this->ParseManagedMaterials(); return;
271  case Keyword::MATERIALFLAREBINDINGS:this->ParseMaterialFlareBindings(); return;
272  case Keyword::MESHWHEELS: this->ParseMeshWheel(); return;
273  case Keyword::MESHWHEELS2: this->ParseMeshWheel2(); return;
274  case Keyword::MINIMASS: this->ParseMinimass(); return;
275  case Keyword::NODES:
276  case Keyword::NODES2: this->ParseNodesUnified(); return;
277  case Keyword::PARTICLES: this->ParseParticles(); return;
278  case Keyword::PISTONPROPS: this->ParsePistonprops(); return;
279  case Keyword::PROPS: this->ParseProps(); return;
280  case Keyword::RAILGROUPS: this->ParseRailGroups(); return;
281  case Keyword::ROPABLES: this->ParseRopables(); return;
282  case Keyword::ROPES: this->ParseRopes(); return;
283  case Keyword::ROTATORS:
284  case Keyword::ROTATORS2: this->ParseRotatorsUnified(); return;
285  case Keyword::SCREWPROPS: this->ParseScrewprops(); return;
286  case Keyword::SCRIPTS: this->ParseScripts(); return;
287  case Keyword::SHOCKS: this->ParseShock(); return;
288  case Keyword::SHOCKS2: this->ParseShock2(); return;
289  case Keyword::SHOCKS3: this->ParseShock3(); return;
290  case Keyword::SLIDENODES: this->ParseSlidenodes(); return;
291  case Keyword::SOUNDSOURCES: this->ParseSoundsources(); return;
292  case Keyword::SOUNDSOURCES2: this->ParseSoundsources2(); return;
293  case Keyword::TEXCOORDS: this->ParseTexcoords(); return;
294  case Keyword::TIES: this->ParseTies(); return;
295  case Keyword::TORQUECURVE: this->ParseTorqueCurve(); return;
296  case Keyword::TRANSFERCASE: this->ParseTransferCase(); return;
297  case Keyword::TRIGGERS: this->ParseTriggers(); return;
298  case Keyword::TURBOJETS: this->ParseTurbojets(); return;
299  case Keyword::TURBOPROPS:
300  case Keyword::TURBOPROPS2: this->ParseTurbopropsUnified(); return;
301  case Keyword::VIDEOCAMERA: this->ParseVideoCamera(); return;
302  case Keyword::WHEELDETACHERS: this->ParseWheelDetachers(); return;
303  case Keyword::WHEELS: this->ParseWheel(); return;
304  case Keyword::WHEELS2: this->ParseWheel2(); return;
305  case Keyword::WINGS: this->ParseWing(); return;
306  default:;
307  };
308 }
309 
310 bool Parser::CheckNumArguments(int min_args)
311 {
312  if (min_args > m_num_args)
313  {
314  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
315  fmt::format("Not enough arguments (got {}, {} needed), skipping line", m_num_args, min_args));
316  return false;
317  }
318  return true;
319 }
320 
321 // --------------------------------------------------------------------------
322 // Parsing individual keywords
323 // --------------------------------------------------------------------------
324 
325 void Parser::ParseWing()
326 {
327  if (!this->CheckNumArguments(16)) { return; }
328 
329  Wing wing;
330 
331  for (int i = 0; i < 8; i++) { wing.nodes[i] = this->GetArgNodeRef (i); }
332  for (int i = 8; i < 16; i++) { wing.tex_coords[i-8] = this->GetArgFloat (i); }
333 
334  if (m_num_args > 16) { wing.control_surface = this->GetArgWingSurface (16); }
335  if (m_num_args > 17) { wing.chord_point = this->GetArgFloat (17); }
336  if (m_num_args > 18) { wing.min_deflection = this->GetArgFloat (18); }
337  if (m_num_args > 19) { wing.max_deflection = this->GetArgFloat (19); }
338  if (m_num_args > 20) { wing.airfoil = this->GetArgStr (20); }
339  if (m_num_args > 21) { wing.efficacy_coef = this->GetArgFloat (21); }
340 
341  m_current_module->wings.push_back(wing);
342 }
343 
344 void Parser::ParseSetCollisionRange()
345 {
346  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
347 
348  CollisionRange cr;
349  cr.node_collision_range = this->GetArgFloat(1);
350 
351  m_current_module->set_collision_range.push_back(cr);
352 }
353 
354 void Parser::ParseWheel2()
355 {
356  if (!this->CheckNumArguments(17)) { return; }
357 
358  Wheel2 wheel_2;
359  wheel_2.node_defaults = m_user_node_defaults;
360  wheel_2.beam_defaults = m_user_beam_defaults;
361 
362  wheel_2.rim_radius = this->GetArgFloat ( 0);
363  wheel_2.tyre_radius = this->GetArgFloat ( 1);
364  wheel_2.width = this->GetArgFloat ( 2);
365  wheel_2.num_rays = this->GetArgInt ( 3);
366  wheel_2.nodes[0] = this->GetArgNodeRef ( 4);
367  wheel_2.nodes[1] = this->GetArgNodeRef ( 5);
368  wheel_2.rigidity_node = this->GetArgRigidityNode ( 6);
369  wheel_2.braking = this->GetArgBraking ( 7);
370  wheel_2.propulsion = this->GetArgPropulsion ( 8);
371  wheel_2.reference_arm_node = this->GetArgNodeRef ( 9);
372  wheel_2.mass = this->GetArgFloat (10);
373  wheel_2.rim_springiness = this->GetArgFloat (11);
374  wheel_2.rim_damping = this->GetArgFloat (12);
375  wheel_2.tyre_springiness = this->GetArgFloat (13);
376  wheel_2.tyre_damping = this->GetArgFloat (14);
377  wheel_2.face_material_name = this->GetArgStr (15);
378  wheel_2.band_material_name = this->GetArgStr (16);
379 
380  if (m_sequential_importer.IsEnabled())
381  {
382  m_sequential_importer.GenerateNodesForWheel(Keyword::WHEELS2, wheel_2.num_rays, wheel_2.rigidity_node.IsValidAnyState());
383  }
384 
385  m_current_module->wheels2.push_back(wheel_2);
386 }
387 
388 void Parser::ParseWheel()
389 {
390  if (! this->CheckNumArguments(14)) { return; }
391 
392  Wheel wheel;
393  wheel.node_defaults = m_user_node_defaults;
394  wheel.beam_defaults = m_user_beam_defaults;
395 
396  wheel.radius = this->GetArgFloat ( 0);
397  wheel.width = this->GetArgFloat ( 1);
398  wheel.num_rays = this->GetArgInt ( 2);
399  wheel.nodes[0] = this->GetArgNodeRef ( 3);
400  wheel.nodes[1] = this->GetArgNodeRef ( 4);
401  wheel.rigidity_node = this->GetArgRigidityNode ( 5);
402  wheel.braking = this->GetArgBraking ( 6);
403  wheel.propulsion = this->GetArgPropulsion ( 7);
404  wheel.reference_arm_node = this->GetArgNodeRef ( 8);
405  wheel.mass = this->GetArgFloat ( 9);
406  wheel.springiness = this->GetArgFloat (10);
407  wheel.damping = this->GetArgFloat (11);
408  wheel.face_material_name = this->GetArgStr (12);
409  wheel.band_material_name = this->GetArgStr (13);
410 
411  if (m_sequential_importer.IsEnabled())
412  {
413  m_sequential_importer.GenerateNodesForWheel(Keyword::WHEELS, wheel.num_rays, wheel.rigidity_node.IsValidAnyState());
414  }
415 
416  m_current_module->wheels.push_back(wheel);
417 }
418 
419 void Parser::ParseWheelDetachers()
420 {
421  if (! this->CheckNumArguments(2)) { return; }
422 
423  WheelDetacher wheeldetacher;
424 
425  wheeldetacher.wheel_id = this->GetArgInt(0);
426  wheeldetacher.detacher_group = this->GetArgInt(1);
427 
428  m_current_module->wheeldetachers.push_back(wheeldetacher);
429 }
430 
431 void Parser::ParseTractionControl()
432 {
433  Ogre::StringVector tokens = Ogre::StringUtil::split(m_current_line + 15, ","); // "TractionControl" = 15 characters
434  m_num_args = (int)tokens.size();
435  if (! this->CheckNumArguments(2)) { return; }
436 
437  TractionControl tc;
438  tc.regulation_force = this->ParseArgFloat(tokens[0].c_str());
439  tc.wheel_slip = this->ParseArgFloat(tokens[1].c_str());
440  if (tokens.size() > 2) { tc.fade_speed = this->ParseArgFloat(tokens[2].c_str()); }
441  if (tokens.size() > 3) { tc.pulse_per_sec = this->ParseArgFloat(tokens[3].c_str()); }
442 
443  for (unsigned int i=4; i<tokens.size(); i++)
444  {
445  Ogre::StringVector args2 = Ogre::StringUtil::split(tokens[i], ":");
446  Ogre::StringUtil::trim(args2[0]);
447  Ogre::StringUtil::toLowerCase(args2[0]);
448 
449  if (args2[0] == "mode" && args2.size() == 2)
450  {
451  Ogre::StringVector attrs = Ogre::StringUtil::split(args2[1], "&");
452  auto itor = attrs.begin();
453  auto endi = attrs.end();
454  for (; itor != endi; ++itor)
455  {
456  std::string attr = *itor;
457  Ogre::StringUtil::trim(attr);
458  Ogre::StringUtil::toLowerCase(attr);
459  if (strncmp(attr.c_str(), "nodash", 6) == 0) { tc.attr_no_dashboard = true; }
460  else if (strncmp(attr.c_str(), "notoggle", 8) == 0) { tc.attr_no_toggle = true; }
461  else if (strncmp(attr.c_str(), "on", 2) == 0) { tc.attr_is_on = true; }
462  else if (strncmp(attr.c_str(), "off", 3) == 0) { tc.attr_is_on = false; }
463  }
464  }
465  else
466  {
467  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "missing mode");
468  tc.attr_no_dashboard = false;
469  tc.attr_no_toggle = false;
470  tc.attr_is_on = true;
471  }
472  }
473 
474  m_current_module->tractioncontrol.push_back(tc);
475 }
476 
477 void Parser::ParseTransferCase()
478 {
479  if (! this->CheckNumArguments(2)) { return; }
480 
481  TransferCase tc;
482 
483  tc.a1 = this->GetArgInt(0) - 1;
484  tc.a2 = this->GetArgInt(1) - 1;
485  if (m_num_args > 2) { tc.has_2wd = this->GetArgInt(2); }
486  if (m_num_args > 3) { tc.has_2wd_lo = this->GetArgInt(3); }
487  for (int i = 4; i < m_num_args; i++) { tc.gear_ratios.push_back(this->GetArgFloat(i)); }
488 
489  m_current_module->transfercase.push_back(tc);
490 }
491 
492 void Parser::ParseSubmeshGroundModel()
493 {
494  if (!this->CheckNumArguments(2)) { return; } // Items: keyword, arg
495 
496  m_current_module->submesh_groundmodel.push_back(this->GetArgStr(1));
497 }
498 
499 void Parser::ParseSpeedLimiter()
500 {
501  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
502 
503  SpeedLimiter sl;
504  sl.is_enabled = true;
505  sl.max_speed = this->GetArgFloat(1);
506 
507  m_current_module->speedlimiter.push_back(sl);
508 }
509 
510 void Parser::ParseSetSkeletonSettings()
511 {
512  if (! this->CheckNumArguments(2)) { return; }
513 
514  if (m_current_module->set_skeleton_settings.size() == 0)
515  {
516  m_current_module->set_skeleton_settings.push_back(SkeletonSettings());
517  }
518 
519  SkeletonSettings& skel = m_current_module->set_skeleton_settings[0];
520  skel.visibility_range_meters = this->GetArgFloat(1);
521  if (m_num_args > 2) { skel.beam_thickness_meters = this->GetArgFloat(2); }
522 
523  // Defaults
524  if (skel.visibility_range_meters < 0.f) { skel.visibility_range_meters = 150.f; }
526 }
527 
528 void Parser::ParseDirectiveSetNodeDefaults()
529 {
530  if (!this->CheckNumArguments(2)) { return; }
531 
532  float load_weight = this->GetArgFloat(1);
533  float friction = (m_num_args > 2) ? this->GetArgFloat(2) : -1;
534  float volume = (m_num_args > 3) ? this->GetArgFloat(3) : -1;
535  float surface = (m_num_args > 4) ? this->GetArgFloat(4) : -1;
536 
537  m_user_node_defaults = std::shared_ptr<NodeDefaults>( new NodeDefaults(*m_user_node_defaults) );
538 
539  m_user_node_defaults->load_weight = (load_weight < 0) ? m_ror_node_defaults->load_weight : load_weight;
540  m_user_node_defaults->friction = (friction < 0) ? m_ror_node_defaults->friction : friction;
541  m_user_node_defaults->volume = (volume < 0) ? m_ror_node_defaults->volume : volume;
542  m_user_node_defaults->surface = (surface < 0) ? m_ror_node_defaults->surface : surface;
543 
544  if (m_num_args > 5) m_user_node_defaults->options = this->GetArgNodeOptions(5);
545 }
546 
547 void Parser::ParseDirectiveSetManagedMaterialsOptions()
548 {
549  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
550 
551  // Legacy behavior.
552  m_current_managed_material_options.double_sided = (this->GetArgChar(1) != '0');
553 }
554 
555 void Parser::ParseDirectiveSetBeamDefaultsScale()
556 {
557  if (! this->CheckNumArguments(5)) { return; }
558 
559  BeamDefaults* b = new BeamDefaults(*m_user_beam_defaults);
560  b->scale.springiness = this->GetArgFloat(1);
561 
562  if (m_num_args > 2) { b->scale.damping_constant = this->GetArgFloat(2); }
563  if (m_num_args > 3) { b->scale.deformation_threshold_constant = this->GetArgFloat(3); }
564  if (m_num_args > 4) { b->scale.breaking_threshold_constant = this->GetArgFloat(4); }
565 
566  m_user_beam_defaults = std::shared_ptr<BeamDefaults>(b);
567 }
568 
569 void Parser::ParseDirectiveSetBeamDefaults()
570 {
571  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
572 
573  BeamDefaults d(*m_user_beam_defaults);
574 
575  // What's the state of "enable_advanced_deformation" feature at this point?
576  // Directive "enable_advanced_deformation" alters the effects of BeamDefaults
577  // Since the old parser worked on-the-fly, only BeamDefaults defined after the directive were affected
578 
579  d._enable_advanced_deformation = m_definition->enable_advanced_deformation;
580 
581  d._is_user_defined = true; //The "_enable_advanced_deformation" must only be aplied to user-defined values, not defaults.
582  d.springiness = this->GetArgFloat(1);
583 
584  if (m_num_args > 2) { d.damping_constant = this->GetArgFloat(2); }
585  if (m_num_args > 3) { d.deformation_threshold = this->GetArgFloat(3); }
586  if (m_num_args > 4) { d.breaking_threshold = this->GetArgFloat(4); }
587  if (m_num_args > 5) { d.visual_beam_diameter = this->GetArgFloat(5); }
588  if (m_num_args > 6) { d.beam_material_name = this->GetArgStr (6); }
589  if (m_num_args > 7) { d.plastic_deform_coef = this->GetArgFloat(7); }
590 
591  if (m_num_args > 7 && d.plastic_deform_coef >= 0.0f) { d._is_plastic_deform_coef_user_defined = true; }
592 
593  if (d.springiness < 0.f) { d.springiness = DEFAULT_SPRING; }
594  if (d.damping_constant < 0.f) { d.damping_constant = DEFAULT_DAMP; }
596  if (d.breaking_threshold < 0.f) { d.breaking_threshold = BEAM_BREAK; }
598  if (d.plastic_deform_coef < 0.f) { d.plastic_deform_coef = (*m_user_beam_defaults).plastic_deform_coef; }
599 
600  m_user_beam_defaults = std::shared_ptr<BeamDefaults>( new BeamDefaults(d) );
601  return;
602 }
603 
604 void Parser::ParseDirectivePropCameraMode()
605 {
606  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
607 
608  if (m_current_module->props.size() > 0)
609  {
610  m_current_module->props[m_current_module->props.size() - 1].camera_settings.mode = this->GetArgInt(1);
611  }
612  else
613  {
614  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "This line must come after a prop!");
615  }
616 }
617 
618 void Parser::ParseDirectiveSubmesh()
619 {
620  this->BeginBlock(Keyword::INVALID); // flush the current submesh
621  m_current_submesh = std::shared_ptr<Submesh>( new Submesh() );
622 }
623 
624 void Parser::ParseDirectiveBackmesh()
625 {
626  if (m_current_submesh)
627  {
628  m_current_submesh->backmesh = true;
629  }
630  else
631  {
632  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "must come after 'submesh'");
633  }
634 }
635 
636 void Parser::ProcessGlobalDirective(Keyword keyword) // Directives that should only appear in root module
637 {
638  switch (keyword)
639  {
640  case Keyword::DISABLEDEFAULTSOUNDS: m_definition->disable_default_sounds = true; return;
641  case Keyword::ENABLE_ADVANCED_DEFORMATION: m_definition->enable_advanced_deformation = true; return;
642  case Keyword::FORWARDCOMMANDS: m_definition->forward_commands = true; return;
643  case Keyword::IMPORTCOMMANDS: m_definition->import_commands = true; return;
644  case Keyword::HIDEINCHOOSER: m_definition->hide_in_chooser = true; return;
645  case Keyword::LOCKGROUP_DEFAULT_NOLOCK: m_definition->lockgroup_default_nolock = true; return;
646  case Keyword::RESCUER: m_definition->rescuer = true; return;
647  case Keyword::ROLLON: m_definition->rollon = true; return;
648  case Keyword::SLIDENODE_CONNECT_INSTANTLY: m_definition->slide_nodes_connect_instantly = true; return;
649 
650  default: return;
651  }
652 }
653 
654 void Parser::_ParseBaseMeshWheel(BaseMeshWheel& mesh_wheel)
655 {
656  mesh_wheel.node_defaults = m_user_node_defaults;
657  mesh_wheel.beam_defaults = m_user_beam_defaults;
658 
659  mesh_wheel.tyre_radius = this->GetArgFloat ( 0);
660  mesh_wheel.rim_radius = this->GetArgFloat ( 1);
661  mesh_wheel.width = this->GetArgFloat ( 2);
662  mesh_wheel.num_rays = this->GetArgInt ( 3);
663  mesh_wheel.nodes[0] = this->GetArgNodeRef ( 4);
664  mesh_wheel.nodes[1] = this->GetArgNodeRef ( 5);
665  mesh_wheel.rigidity_node = this->GetArgRigidityNode ( 6);
666  mesh_wheel.braking = this->GetArgBraking ( 7);
667  mesh_wheel.propulsion = this->GetArgPropulsion ( 8);
668  mesh_wheel.reference_arm_node = this->GetArgNodeRef ( 9);
669  mesh_wheel.mass = this->GetArgFloat (10);
670  mesh_wheel.spring = this->GetArgFloat (11);
671  mesh_wheel.damping = this->GetArgFloat (12);
672  mesh_wheel.side = this->GetArgWheelSide (13);
673  mesh_wheel.mesh_name = this->GetArgStr (14);
674  mesh_wheel.material_name = this->GetArgStr (15);
675 }
676 
677 void Parser::ParseMeshWheel()
678 {
679  if (! this->CheckNumArguments(16)) { return; }
680 
681  MeshWheel mesh_wheel;
682  this->_ParseBaseMeshWheel(mesh_wheel);
683 
684  if (m_sequential_importer.IsEnabled())
685  {
686  m_sequential_importer.GenerateNodesForWheel(Keyword::MESHWHEELS, mesh_wheel.num_rays, mesh_wheel.rigidity_node.IsValidAnyState());
687  }
688 
689  m_current_module->meshwheels.push_back(mesh_wheel);
690 }
691 
692 void Parser::ParseMeshWheel2()
693 {
694  if (! this->CheckNumArguments(16)) { return; }
695 
696  MeshWheel2 mesh_wheel;
697  this->_ParseBaseMeshWheel(mesh_wheel);
698 
699  if (m_sequential_importer.IsEnabled())
700  {
701  m_sequential_importer.GenerateNodesForWheel(Keyword::MESHWHEELS2, mesh_wheel.num_rays, mesh_wheel.rigidity_node.IsValidAnyState());
702  }
703 
704  m_current_module->meshwheels2.push_back(mesh_wheel);
705 }
706 
707 void Parser::ParseHook()
708 {
709  if (! this->CheckNumArguments(1)) { return; }
710 
711  Hook hook;
712  hook.node = this->GetArgNodeRef(0);
713 
714  int i = 1;
715  while (i < m_num_args)
716  {
717  std::string attr = this->GetArgStr(i);
718  Ogre::StringUtil::trim(attr);
719  const bool has_value = (i < (m_num_args - 1));
720 
721  // Values
722  if (has_value && (attr == "hookrange") ) { hook.option_hook_range = this->GetArgFloat(++i); }
723  else if (has_value && (attr == "speedcoef") ) { hook.option_speed_coef = this->GetArgFloat(++i); }
724  else if (has_value && (attr == "maxforce") ) { hook.option_max_force = this->GetArgFloat(++i); }
725  else if (has_value && (attr == "timer") ) { hook.option_timer = this->GetArgFloat(++i); }
726  else if (has_value && (attr == "hookgroup" || attr == "hgroup") ) { hook.option_hookgroup = this->GetArgInt (++i); }
727  else if (has_value && (attr == "lockgroup" || attr == "lgroup") ) { hook.option_lockgroup = this->GetArgInt (++i); }
728  else if (has_value && (attr == "shortlimit" || attr == "short_limit")) { hook.option_min_range_meters = this->GetArgFloat(++i); }
729  // Flags
730  else if ((attr == "selflock") ||(attr == "self-lock") ||(attr == "self_lock") ) { hook.flag_self_lock = true; }
731  else if ((attr == "autolock") ||(attr == "auto-lock") ||(attr == "auto_lock") ) { hook.flag_auto_lock = true; }
732  else if ((attr == "nodisable")||(attr == "no-disable")||(attr == "no_disable")) { hook.flag_no_disable = true; }
733  else if ((attr == "norope") ||(attr == "no-rope") ||(attr == "no_rope") ) { hook.flag_no_rope = true; }
734  else if ((attr == "visible") ||(attr == "vis") ) { hook.flag_visible = true; }
735  else
736  {
737  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, fmt::format("ignoring invalid option '{}'", attr));
738  }
739  i++;
740  }
741 
742  m_current_module->hooks.push_back(hook);
743 }
744 
745 void Parser::ParseHelp()
746 {
747  Help h;
748  h.material = m_current_line; // already trimmed
749  m_current_module->help.push_back(h);
750 }
751 
752 void Parser::ParseGuiSettings()
753 {
754  if (! this->CheckNumArguments(2)) { return; }
755 
756  GuiSettings gs;
757  gs.key = this->GetArgStr(0);
758  gs.value = this->GetArgStr(1);
759 
760  m_current_module->guisettings.push_back(gs);
761 }
762 
763 void Parser::ParseGuid()
764 {
765  if (! this->CheckNumArguments(2)) { return; }
766 
767  Guid g;
768  g.guid = this->GetArgStr(1);
769 
770  m_current_module->guid.push_back(g);
771 }
772 
773 void Parser::ParseGlobals()
774 {
775  if (! this->CheckNumArguments(2)) { return; }
776 
777  Globals globals;
778  globals.dry_mass = this->GetArgFloat(0);
779  globals.cargo_mass = this->GetArgFloat(1);
780 
781  if (m_num_args > 2) { globals.material_name = this->GetArgStr(2); }
782 
783  m_current_module->globals.push_back(globals);
784 }
785 
786 void Parser::ParseFusedrag()
787 {
788  if (! this->CheckNumArguments(3)) { return; }
789 
790  Fusedrag fusedrag;
791  fusedrag.front_node = this->GetArgNodeRef(0);
792  fusedrag.rear_node = this->GetArgNodeRef(1);
793 
794  if (this->GetArgStr(2) == "autocalc")
795  {
796  fusedrag.autocalc = true;
797 
798  // Fusedrag autocalculation from truck size
799  if (m_num_args > 3) { fusedrag.area_coefficient = this->GetArgFloat(3); }
800  if (m_num_args > 4) { fusedrag.airfoil_name = this->GetArgStr (4); }
801  }
802  else
803  {
804  // Original calculation
805  fusedrag.approximate_width = this->GetArgFloat(2);
806 
807  if (m_num_args > 3) { fusedrag.airfoil_name = this->GetArgStr(3); }
808  }
809 
810  m_current_module->fusedrag.push_back(fusedrag);
811 }
812 
813 void Parser::ParseDirectiveFlexbodyCameraMode()
814 {
815  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, arg
816 
817  if (m_current_module->flexbodies.size() > 0)
818  {
819  m_current_module->flexbodies[m_current_module->flexbodies.size() - 1].camera_settings.mode = this->GetArgInt(1);
820  }
821  else
822  {
823  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "This line must come after a flexbody!");
824  }
825 }
826 
827 void Parser::ParseCab()
828 {
829  if (! this->CheckNumArguments(3)) { return; }
830 
831  if (!m_current_submesh)
832  {
833  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "must come after 'submesh'");
834  return;
835  }
836 
837  Cab cab;
838  cab.nodes[0] = this->GetArgNodeRef(0);
839  cab.nodes[1] = this->GetArgNodeRef(1);
840  cab.nodes[2] = this->GetArgNodeRef(2);
841  if (m_num_args > 3) cab.options = this->GetArgCabOptions(3);
842 
843  m_current_submesh->cab_triangles.push_back(cab);
844 }
845 
846 void Parser::ParseTexcoords()
847 {
848  if (! this->CheckNumArguments(3)) { return; }
849 
850  if (!m_current_submesh)
851  {
852  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "must come after 'submesh'");
853  return;
854  }
855 
856  Texcoord texcoord;
857  texcoord.node = this->GetArgNodeRef(0);
858  texcoord.u = this->GetArgFloat (1);
859  texcoord.v = this->GetArgFloat (2);
860 
861  m_current_submesh->texcoords.push_back(texcoord);
862 }
863 
864 void Parser::ParseFlexbody()
865 {
866  if (! this->CheckNumArguments(10)) { return; }
867 
868  Flexbody flexbody;
869  flexbody.reference_node = this->GetArgNodeRef (0);
870  flexbody.x_axis_node = this->GetArgNodeRef (1);
871  flexbody.y_axis_node = this->GetArgNodeRef (2);
872  flexbody.offset.x = this->GetArgFloat (3);
873  flexbody.offset.y = this->GetArgFloat (4);
874  flexbody.offset.z = this->GetArgFloat (5);
875  flexbody.rotation.x = this->GetArgFloat (6);
876  flexbody.rotation.y = this->GetArgFloat (7);
877  flexbody.rotation.z = this->GetArgFloat (8);
878  flexbody.mesh_name = this->GetArgStr (9);
879 
880  m_current_module->flexbodies.push_back(flexbody);
881 }
882 
883 void Parser::ParseDirectiveForset()
884 {
885  if (m_current_module->flexbodies.size() == 0)
886  {
887  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, "ignoring 'forset': no matching flexbody!");
888  return;
889  }
890 
891  Parser::ProcessForsetLine(m_current_module->flexbodies.back(), m_current_line, m_current_line_number);
892 }
893 
894 void Parser::ProcessForsetLine(RigDef::Flexbody& def, const std::string& _line, int line_number /*= -1*/)
895 {
896  // --------------------------------------------------------------------------------------------
897  // BEWARE OF QUIRKS in the following code (they must be preserved for backwards compatibility):
898  // - a space between the 'forset' keyword and arguments is optional.
899  // - garbage characters anywhere on the line will silently add node 0 to the set.
900  // - a separator at the end of line will silently add node 0 to the set.
901  // --------------------------------------------------------------------------------------------
902 
903  //parsing set definition
904  std::string line = _line;
905  char* pos = &line[0] + 6; // 'forset' = 6 characters
906  while (*pos == ' ' || *pos == ':' || *pos == ',') { pos++; } // Skip any separators
907  char* end = pos;
908  char endwas = 'G';
909  while (endwas != 0)
910  {
911  int val1, val2; // Node numbers
912  end = pos;
913  while (*end != '-' && *end != ',' && *end != 0) end++;
914  endwas = *end;
915  *end = 0;
916  val1 = strtoul(pos, 0, 10);
917  if (endwas == '-')
918  {
919  pos = end + 1;
920  end = pos;
921  while (*end != ',' && *end != 0) end++;
922  endwas = *end;
923  *end = 0;
924  val2 = strtoul(pos, 0, 10);
925  // Add interval [val1-val2]
926  def.node_list_to_import.push_back(
927  Node::Range(
928  Node::Ref(std::to_string(val1), val1, Node::Ref::IMPORT_STATE_IS_VALID, line_number),
929  Node::Ref(std::to_string(val2), val2, Node::Ref::IMPORT_STATE_IS_VALID, line_number)));
930  }
931  else
932  {
933  // Add interval [val1-val1]
934  Node::Range range_a = Node::Range(Node::Ref(std::to_string(val1), val1, Node::Ref::IMPORT_STATE_IS_VALID, line_number));
935  def.node_list_to_import.push_back(range_a);
936  }
937  pos = end + 1;
938  }
939 }
940 
941 void Parser::ParseFlaresUnified()
942 {
943  const bool is_flares2 = (m_current_block == Keyword::FLARES2);
944  if (! this->CheckNumArguments(is_flares2 ? 6 : 5)) { return; }
945 
946  Flare2 flare2;
947  int pos = 0;
948  flare2.reference_node = this->GetArgNodeRef(pos++);
949  flare2.node_axis_x = this->GetArgNodeRef(pos++);
950  flare2.node_axis_y = this->GetArgNodeRef(pos++);
951  flare2.offset.x = this->GetArgFloat (pos++);
952  flare2.offset.y = this->GetArgFloat (pos++);
953 
954  if (m_current_block == Keyword::FLARES2)
955  {
956  flare2.offset.z = this->GetArgFloat(pos++);
957  }
958 
959  if (m_num_args > pos) { flare2.type = this->GetArgFlareType(pos++); }
960 
961  if (m_num_args > pos)
962  {
963  switch (flare2.type)
964  {
965  case FlareType::USER: flare2.control_number = this->GetArgInt(pos); break;
966  case FlareType::DASHBOARD: flare2.dashboard_link = this->GetArgStr(pos); break;
967  default: break;
968  }
969  pos++;
970  }
971 
972  if (m_num_args > pos) { flare2.blink_delay_milis = this->GetArgInt (pos++); }
973  if (m_num_args > pos) { flare2.size = this->GetArgFloat (pos++); }
974  if (m_num_args > pos) { flare2.material_name = this->GetArgStr (pos++); }
975 
976  m_current_module->flares2.push_back(flare2);
977 }
978 
979 void Parser::ParseFlares3()
980 {
981  const bool is_flares2 = (m_current_block == Keyword::FLARES2);
982  if (! this->CheckNumArguments(is_flares2 ? 6 : 5)) { return; }
983 
984  Flare3 flare3;
985  flare3.inertia_defaults = m_user_default_inertia;
986 
987  flare3.reference_node = this->GetArgNodeRef(0);
988  flare3.node_axis_x = this->GetArgNodeRef(1);
989  flare3.node_axis_y = this->GetArgNodeRef(2);
990  flare3.offset.x = this->GetArgFloat(3);
991  flare3.offset.y = this->GetArgFloat(4);
992  flare3.offset.z = this->GetArgFloat(5);
993  if (m_num_args > 6) { flare3.type = this->GetArgFlareType(6); }
994 
995  if (m_num_args > 7)
996  {
997  switch (flare3.type)
998  {
999  case FlareType::USER: flare3.control_number = this->GetArgInt(7); break;
1000  case FlareType::DASHBOARD: flare3.dashboard_link = this->GetArgStr(7); break;
1001  default: break;
1002  }
1003  }
1004 
1005  if (m_num_args > 8) { flare3.blink_delay_milis = this->GetArgInt (8); }
1006  if (m_num_args > 9) { flare3.size = this->GetArgFloat (9); }
1007  if (m_num_args > 10) { flare3.material_name = this->GetArgStr (10); }
1008 
1009  m_current_module->flares3.push_back(flare3);
1010 }
1011 
1012 void Parser::ParseFixes()
1013 {
1014  m_current_module->fixes.push_back(this->GetArgNodeRef(0));
1015 }
1016 
1017 void Parser::ParseExtCamera()
1018 {
1019  if (! this->CheckNumArguments(2)) { return; }
1020 
1021  ExtCamera extcam;
1022  extcam.mode = this->GetArgExtCameraMode(1);
1023  if (m_num_args > 2) { extcam.node = this->GetArgNodeRef(2); }
1024 
1025  m_current_module->extcamera.push_back(extcam);
1026 }
1027 
1028 void Parser::ParseExhaust()
1029 {
1030  if (! this->CheckNumArguments(2)) { return; }
1031 
1032  Exhaust exhaust;
1033  exhaust.reference_node = this->GetArgNodeRef(0);
1034  exhaust.direction_node = this->GetArgNodeRef(1);
1035 
1036  // Param [2] is unused
1037  if (m_num_args > 3) { exhaust.particle_name = this->GetArgStr(3); }
1038 
1039  m_current_module->exhausts.push_back(exhaust);
1040 }
1041 
1042 void Parser::ParseFileFormatVersion()
1043 {
1044  if (! this->CheckNumArguments(2)) { return; }
1045 
1046  FileFormatVersion ffv;
1047  ffv.version = this->GetArgInt(1);
1048 
1049  m_current_module->fileformatversion.push_back(ffv);
1050  m_current_block = Keyword::INVALID;
1051 }
1052 
1053 void Parser::ParseDirectiveDefaultSkin()
1054 {
1055  if (!this->CheckNumArguments(2)) { return; } // 2 items: keyword, param
1056 
1057  DefaultSkin data;
1058  data.skin_name = this->GetArgStr(1);
1059  std::replace(data.skin_name.begin(), data.skin_name.end(), '_', ' ');
1060 
1061  m_current_module->default_skin.push_back(data);
1062 }
1063 
1064 void Parser::ParseDirectiveDetacherGroup()
1065 {
1066  if (! this->CheckNumArguments(2)) { return; } // 2 items: keyword, param
1067 
1068  if (this->GetArgStr(1) == "end")
1069  {
1070  m_current_detacher_group = 0;
1071  }
1072  else
1073  {
1074  m_current_detacher_group = this->GetArgInt(1);
1075  }
1076 }
1077 
1078 void Parser::ParseCruiseControl()
1079 {
1080  if (! this->CheckNumArguments(3)) { return; } // keyword + 2 params
1081 
1082  CruiseControl cruise_control;
1083  cruise_control.min_speed = this->GetArgFloat(1);
1084  cruise_control.autobrake = this->GetArgInt(2);
1085 
1086  m_current_module->cruisecontrol.push_back(cruise_control);
1087 }
1088 
1089 void Parser::ParseDescription()
1090 {
1091  m_current_module->description.push_back(m_current_line); // Already trimmed
1092 }
1093 
1094 void Parser::ParseDirectiveAddAnimation()
1095 {
1096  Ogre::StringVector tokens = Ogre::StringUtil::split(m_current_line + 14, ","); // "add_animation " = 14 characters
1097  m_num_args = (int)tokens.size();
1098  if (! this->CheckNumArguments(4)) { return; }
1099 
1100  if (m_current_module->props.size() == 0)
1101  {
1102  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, "'add_animation' must come after prop, ignoring...");
1103  return;
1104  }
1105 
1106  Animation animation;
1107  animation.ratio = this->ParseArgFloat(tokens[0].c_str());
1108  animation.lower_limit = this->ParseArgFloat(tokens[1].c_str());
1109  animation.upper_limit = this->ParseArgFloat(tokens[2].c_str());
1110 
1111  for (auto itor = tokens.begin() + 3; itor != tokens.end(); ++itor)
1112  {
1113  Ogre::StringVector entry = Ogre::StringUtil::split(*itor, ":");
1114  Ogre::StringUtil::trim(entry[0]);
1115  if (entry.size() > 1) Ogre::StringUtil::trim(entry[1]);
1116 
1117  const int WARN_LEN = 500;
1118  char warn_msg[WARN_LEN] = "";
1119 
1120  if (entry.size() == 1) // Single keyword
1121  {
1122  if (entry[0] == "autoanimate") { animation.mode |= Animation::MODE_AUTO_ANIMATE; }
1123  else if (entry[0] == "noflip") { animation.mode |= Animation::MODE_NO_FLIP; }
1124  else if (entry[0] == "bounce") { animation.mode |= Animation::MODE_BOUNCE; }
1125  else if (entry[0] == "eventlock") { animation.mode |= Animation::MODE_EVENT_LOCK; }
1126 
1127  else { snprintf(warn_msg, WARN_LEN, "Invalid keyword: %s", entry[0].c_str()); }
1128  }
1129  else if (entry.size() == 2 && (entry[0] == "mode" || entry[0] == "event" || entry[0] == "source" || entry[0] == "link"))
1130  {
1131  Ogre::StringVector values = Ogre::StringUtil::split(entry[1], "|");
1132  if (entry[0] == "mode")
1133  {
1134  for (auto itor = values.begin(); itor != values.end(); ++itor)
1135  {
1136  std::string value = *itor;
1137  Ogre::StringUtil::trim(value);
1138 
1139  if (value == "x-rotation") { animation.mode |= Animation::MODE_ROTATION_X; }
1140  else if (value == "y-rotation") { animation.mode |= Animation::MODE_ROTATION_Y; }
1141  else if (value == "z-rotation") { animation.mode |= Animation::MODE_ROTATION_Z; }
1142  else if (value == "x-offset" ) { animation.mode |= Animation::MODE_OFFSET_X; }
1143  else if (value == "y-offset" ) { animation.mode |= Animation::MODE_OFFSET_Y; }
1144  else if (value == "z-offset" ) { animation.mode |= Animation::MODE_OFFSET_Z; }
1145 
1146  else { snprintf(warn_msg, WARN_LEN, "Invalid 'mode': %s, ignoring...", entry[1].c_str()); }
1147  }
1148  }
1149  else if (entry[0] == "event")
1150  {
1151  animation.event_name = entry[1];
1152  Ogre::StringUtil::trim(animation.event_name);
1153  Ogre::StringUtil::toUpperCase(animation.event_name);
1154  }
1155  else if (entry[0] == "link")
1156  {
1157  animation.dash_link_name = entry[1];
1158  Ogre::StringUtil::trim(animation.dash_link_name);
1159  }
1160  else if (entry[0] == "source")
1161  {
1162  for (auto itor = values.begin(); itor != values.end(); ++itor)
1163  {
1164  std::string value = *itor;
1165  Ogre::StringUtil::trim(value);
1166 
1167  if (value == "airspeed") { animation.source |= Animation::SOURCE_AIRSPEED; }
1168  else if (value == "vvi") { animation.source |= Animation::SOURCE_VERTICAL_VELOCITY; }
1169  else if (value == "altimeter100k") { animation.source |= Animation::SOURCE_ALTIMETER_100K; }
1170  else if (value == "altimeter10k") { animation.source |= Animation::SOURCE_ALTIMETER_10K; }
1171  else if (value == "altimeter1k") { animation.source |= Animation::SOURCE_ALTIMETER_1K; }
1172  else if (value == "aoa") { animation.source |= Animation::SOURCE_ANGLE_OF_ATTACK; }
1173  else if (value == "flap") { animation.source |= Animation::SOURCE_FLAP; }
1174  else if (value == "airbrake") { animation.source |= Animation::SOURCE_AIR_BRAKE; }
1175  else if (value == "roll") { animation.source |= Animation::SOURCE_ROLL; }
1176  else if (value == "pitch") { animation.source |= Animation::SOURCE_PITCH; }
1177  else if (value == "brakes") { animation.source |= Animation::SOURCE_BRAKES; }
1178  else if (value == "accel") { animation.source |= Animation::SOURCE_ACCEL; }
1179  else if (value == "clutch") { animation.source |= Animation::SOURCE_CLUTCH; }
1180  else if (value == "speedo") { animation.source |= Animation::SOURCE_SPEEDO; }
1181  else if (value == "tacho") { animation.source |= Animation::SOURCE_TACHO; }
1182  else if (value == "turbo") { animation.source |= Animation::SOURCE_TURBO; }
1183  else if (value == "parking") { animation.source |= Animation::SOURCE_PARKING; }
1184  else if (value == "shifterman1") { animation.source |= Animation::SOURCE_SHIFT_LEFT_RIGHT; }
1185  else if (value == "shifterman2") { animation.source |= Animation::SOURCE_SHIFT_BACK_FORTH; }
1186  else if (value == "sequential") { animation.source |= Animation::SOURCE_SEQUENTIAL_SHIFT; }
1187  else if (value == "shifterlin") { animation.source |= Animation::SOURCE_SHIFTERLIN; }
1188  else if (value == "autoshifterlin"){ animation.source |= Animation::SOURCE_AUTOSHIFTERLIN; }
1189  else if (value == "torque") { animation.source |= Animation::SOURCE_TORQUE; }
1190  else if (value == "heading") { animation.source |= Animation::SOURCE_HEADING; }
1191  else if (value == "difflock") { animation.source |= Animation::SOURCE_DIFFLOCK; }
1192  else if (value == "rudderboat") { animation.source |= Animation::SOURCE_BOAT_RUDDER; }
1193  else if (value == "throttleboat") { animation.source |= Animation::SOURCE_BOAT_THROTTLE; }
1194  else if (value == "steeringwheel") { animation.source |= Animation::SOURCE_STEERING_WHEEL; }
1195  else if (value == "aileron") { animation.source |= Animation::SOURCE_AILERON; }
1196  else if (value == "elevator") { animation.source |= Animation::SOURCE_ELEVATOR; }
1197  else if (value == "rudderair") { animation.source |= Animation::SOURCE_AIR_RUDDER; }
1198  else if (value == "permanent") { animation.source |= Animation::SOURCE_PERMANENT; }
1199  else if (value == "event") { animation.source |= Animation::SOURCE_EVENT; }
1200  else if (value == "dashboard") { animation.source |= Animation::SOURCE_DASHBOARD; }
1201  else if (value == "signalstalk") { animation.source |= Animation::SOURCE_SIGNALSTALK; }
1202  else if (value == "gearreverse") { animation.source |= Animation::SOURCE_GEAR_REVERSE; }
1203  else if (value == "gearneutral") { animation.source |= Animation::SOURCE_GEAR_NEUTRAL; }
1204 
1205  else
1206  {
1207  Animation::MotorSource motor_source;
1208  // aeroengines...
1209  if (entry[1].compare(0, 8, "throttle") == 0)
1210  {
1211  motor_source.source = Animation::MotorSource::SOURCE_AERO_THROTTLE;
1212  motor_source.motor = this->ParseArgUint(entry[1].substr(8));
1213  }
1214  else if (entry[1].compare(0, 3, "rpm") == 0)
1215  {
1216  motor_source.source = Animation::MotorSource::SOURCE_AERO_RPM;
1217  motor_source.motor = this->ParseArgUint(entry[1].substr(3));
1218  }
1219  else if (entry[1].compare(0, 8, "aerotorq") == 0)
1220  {
1221  motor_source.source = Animation::MotorSource::SOURCE_AERO_TORQUE;
1222  motor_source.motor = this->ParseArgUint(entry[1].substr(8));
1223  }
1224  else if (entry[1].compare(0, 7, "aeropit") == 0)
1225  {
1226  motor_source.source = Animation::MotorSource::SOURCE_AERO_PITCH;
1227  motor_source.motor = this->ParseArgUint(entry[1].substr(7));
1228  }
1229  else if (entry[1].compare(0, 10, "aerostatus") == 0)
1230  {
1231  motor_source.source = Animation::MotorSource::SOURCE_AERO_STATUS;
1232  motor_source.motor = this->ParseArgUint(entry[1].substr(10));
1233  }
1234  // gears... (hack)
1235  else if (entry[1].compare(0, 4, "gear") == 0)
1236  {
1237  motor_source.source = Animation::MotorSource::SOURCE_GEAR_FORWARD;
1238  motor_source.motor = this->ParseArgUint(entry[1].substr(4));
1239  }
1240  else
1241  {
1242  snprintf(warn_msg, WARN_LEN, "Invalid 'source': %s, ignoring...", entry[1].c_str());
1243  continue;
1244  }
1245  animation.motor_sources.push_back(motor_source);
1246  }
1247  }
1248  }
1249  else
1250  {
1251  snprintf(warn_msg, WARN_LEN, "Invalid keyword: %s, ignoring...", entry[0].c_str());
1252  }
1253  }
1254  else
1255  {
1256  snprintf(warn_msg, WARN_LEN, "Invalid item: %s, ignoring...", entry[0].c_str());
1257  }
1258 
1259  if (warn_msg[0] != '\0')
1260  {
1261  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1262  fmt::format("Ignoring invalid token '{}' ({})", itor->c_str(), warn_msg));
1263  }
1264  }
1265 
1266  m_current_module->props.back().animations.push_back(animation);
1267 }
1268 
1269 void Parser::ParseAntiLockBrakes()
1270 {
1271  AntiLockBrakes alb;
1272  Ogre::StringVector tokens = Ogre::StringUtil::split(m_current_line + 15, ","); // "AntiLockBrakes " = 15 characters
1273  m_num_args = (int)tokens.size();
1274  if (! this->CheckNumArguments(2)) { return; }
1275 
1276  alb.regulation_force = this->ParseArgFloat(tokens[0].c_str());
1277  alb.min_speed = this->ParseArgInt (tokens[1].c_str());
1278 
1279  if (tokens.size() > 3) { alb.pulse_per_sec = this->ParseArgFloat(tokens[2].c_str()); }
1280 
1281  for (unsigned int i=3; i<tokens.size(); i++)
1282  {
1283  Ogre::StringVector args2 = Ogre::StringUtil::split(tokens[i], ":");
1284  Ogre::StringUtil::trim(args2[0]);
1285  Ogre::StringUtil::toLowerCase(args2[0]);
1286  if (args2[0] == "mode" && args2.size() == 2)
1287  {
1288  Ogre::StringVector attrs = Ogre::StringUtil::split(args2[1], "&");
1289  auto itor = attrs.begin();
1290  auto endi = attrs.end();
1291  for (; itor != endi; ++itor)
1292  {
1293  std::string attr = *itor;
1294  Ogre::StringUtil::trim(attr);
1295  Ogre::StringUtil::toLowerCase(attr);
1296  if (strncmp(attr.c_str(), "nodash", 6) == 0) { alb.attr_no_dashboard = true; }
1297  else if (strncmp(attr.c_str(), "notoggle", 8) == 0) { alb.attr_no_toggle = true; }
1298  else if (strncmp(attr.c_str(), "on", 2) == 0) { alb.attr_is_on = true; }
1299  else if (strncmp(attr.c_str(), "off", 3) == 0) { alb.attr_is_on = false; }
1300  }
1301  }
1302  else
1303  {
1304  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "missing mode");
1305  alb.attr_no_dashboard = false;
1306  alb.attr_no_toggle = false;
1307  alb.attr_is_on = true;
1308  }
1309  }
1310 
1311  m_current_module->antilockbrakes.push_back(alb);
1312 }
1313 
1314 void Parser::ParseEngoption()
1315 {
1316  if (! this->CheckNumArguments(1)) { return; }
1317 
1318  Engoption engoption;
1319  engoption.inertia = this->GetArgFloat(0);
1320 
1321  if (m_num_args > 1) { engoption.type = this->GetArgEngineType(1); }
1322  if (m_num_args > 2) { engoption.clutch_force = this->GetArgFloat(2); }
1323  if (m_num_args > 3) { engoption.shift_time = this->GetArgFloat(3); }
1324  if (m_num_args > 4) { engoption.clutch_time = this->GetArgFloat(4); }
1325  if (m_num_args > 5) { engoption.post_shift_time = this->GetArgFloat(5); }
1326  if (m_num_args > 6) { engoption.stall_rpm = this->GetArgFloat(6); }
1327  if (m_num_args > 7) { engoption.idle_rpm = this->GetArgFloat(7); }
1328  if (m_num_args > 8) { engoption.max_idle_mixture = this->GetArgFloat(8); }
1329  if (m_num_args > 9) { engoption.min_idle_mixture = this->GetArgFloat(9); }
1330  if (m_num_args > 10){ engoption.braking_torque = this->GetArgFloat(10);}
1331 
1332  m_current_module->engoption.push_back(engoption);
1333 }
1334 
1335 void Parser::ParseEngturbo()
1336 {
1337  if (! this->CheckNumArguments(4)) { return; }
1338 
1339  Engturbo engturbo;
1340  engturbo.version = this->GetArgInt ( 0);
1341  engturbo.tinertiaFactor = this->GetArgFloat( 1);
1342  engturbo.nturbos = this->GetArgInt ( 2);
1343  engturbo.param1 = this->GetArgFloat( 3);
1344 
1345  if (m_num_args > 4) { engturbo.param2 = this->GetArgFloat( 4); }
1346  if (m_num_args > 5) { engturbo.param3 = this->GetArgFloat( 5); }
1347  if (m_num_args > 6) { engturbo.param4 = this->GetArgFloat( 6); }
1348  if (m_num_args > 7) { engturbo.param5 = this->GetArgFloat( 7); }
1349  if (m_num_args > 8) { engturbo.param6 = this->GetArgFloat( 8); }
1350  if (m_num_args > 9) { engturbo.param7 = this->GetArgFloat( 9); }
1351  if (m_num_args > 10) { engturbo.param8 = this->GetArgFloat(10); }
1352  if (m_num_args > 11) { engturbo.param9 = this->GetArgFloat(11); }
1353  if (m_num_args > 12) { engturbo.param10 = this->GetArgFloat(12); }
1354  if (m_num_args > 13) { engturbo.param11 = this->GetArgFloat(13); }
1355 
1356  if (engturbo.nturbos > 4)
1357  {
1358  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, "You cannot have more than 4 turbos. Fallback: using 4 instead.");
1359  engturbo.nturbos = 4;
1360  }
1361 
1362  m_current_module->engturbo.push_back(engturbo);
1363 }
1364 
1365 void Parser::ParseEngine()
1366 {
1367  if (! this->CheckNumArguments(6)) { return; }
1368 
1369  Engine engine;
1370  engine.shift_down_rpm = this->GetArgFloat(0);
1371  engine.shift_up_rpm = this->GetArgFloat(1);
1372  engine.torque = this->GetArgFloat(2);
1373  engine.global_gear_ratio = this->GetArgFloat(3);
1374  engine.reverse_gear_ratio = this->GetArgFloat(4);
1375  engine.neutral_gear_ratio = this->GetArgFloat(5);
1376 
1377  // Forward gears
1378  for (int i = 6; i < m_num_args; i++)
1379  {
1380  float ratio = this->GetArgFloat(i);
1381  if (ratio < 0.f)
1382  {
1383  break; // Optional terminator argument
1384  }
1385  engine.gear_ratios.push_back(ratio);
1386  }
1387 
1388  if (engine.gear_ratios.size() == 0)
1389  {
1390  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "no forward gear");
1391  return;
1392  }
1393 
1394  m_current_module->engine.push_back(engine);
1395 }
1396 
1397 void Parser::ParseContacter()
1398 {
1399  if (! this->CheckNumArguments(1)) { return; }
1400 
1401  m_current_module->contacters.push_back(this->GetArgNodeRef(0));
1402 }
1403 
1404 void Parser::ParseCommandsUnified()
1405 {
1406  const bool is_commands2 = (m_current_block == Keyword::COMMANDS2);
1407  const int max_args = (is_commands2 ? 8 : 7);
1408  if (! this->CheckNumArguments(max_args)) { return; }
1409 
1410  Command2 command2;
1411  command2.beam_defaults = m_user_beam_defaults;
1412  command2.detacher_group = m_current_detacher_group;
1413  command2.inertia_defaults = m_user_default_inertia;
1414 
1415  int pos = 0;
1416  command2.nodes[0] = this->GetArgNodeRef(pos++);
1417  command2.nodes[1] = this->GetArgNodeRef(pos++);
1418  command2.shorten_rate = this->GetArgFloat (pos++);
1419 
1420  if (is_commands2)
1421  {
1422  command2.lengthen_rate = this->GetArgFloat(pos++);
1423  }
1424  else
1425  {
1426  command2.lengthen_rate = command2.shorten_rate;
1427  }
1428 
1429  command2.max_contraction = this->GetArgFloat(pos++);
1430  command2.max_extension = this->GetArgFloat(pos++);
1431  command2.contract_key = this->GetArgInt (pos++);
1432  command2.extend_key = this->GetArgInt (pos++);
1433 
1434  if (m_num_args <= max_args) // No more args?
1435  {
1436  m_current_module->commands2.push_back(command2);
1437  return;
1438  }
1439 
1440  // Parse options
1441  std::string options_str = this->GetArgStr(pos++);
1442  char winner = 0;
1443  for (auto itor = options_str.begin(); itor != options_str.end(); ++itor)
1444  {
1445  const char c = *itor;
1446  if ((winner == 0) && (c == 'o' || c == 'p' || c == 'c')) { winner = c; }
1447 
1448  if (c == 'n') {} // Filler, does nothing
1449  else if (c == 'i') { command2.option_i_invisible = true; }
1450  else if (c == 'r') { command2.option_r_rope = true; }
1451  else if (c == 'f') { command2.option_f_not_faster = true; }
1452  else if (c == 'c') { command2.option_c_auto_center = true; }
1453  else if (c == 'p') { command2.option_p_1press = true; }
1454  else if (c == 'o') { command2.option_o_1press_center = true; }
1455  else
1456  {
1457  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1458  fmt::format("ignoring unknown flag '{}'", c));
1459  }
1460  }
1461 
1462  // Resolve option conflicts
1463  if (command2.option_c_auto_center && winner != 'c' && winner != 0)
1464  {
1465  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, "Command cannot be one-pressed and self centering at the same time, ignoring flag 'c'");
1466  command2.option_c_auto_center = false;
1467  }
1468  char ignored = '\0';
1469  if (command2.option_o_1press_center && winner != 'o' && winner != 0)
1470  {
1471  command2.option_o_1press_center = false;
1472  ignored = 'o';
1473  }
1474  else if (command2.option_p_1press && winner != 'p' && winner != 0)
1475  {
1476  command2.option_p_1press = false;
1477  ignored = 'p';
1478  }
1479 
1480  // Report conflicts
1481  if (ignored != 0 && winner == 'c')
1482  {
1483  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1484  "Command cannot be one-pressed and self centering at the same time, ignoring flag '%c'");
1485  }
1486  else if (ignored != 0 && (winner == 'o' || winner == 'p'))
1487  {
1488  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1489  "Command already has a one-pressed c.mode, ignoring flag '%c'");
1490  }
1491 
1492  if (m_num_args > pos) { command2.description = this->GetArgStr (pos++);}
1493 
1494  if (m_num_args > pos) { ParseOptionalInertia(command2.inertia, pos); pos += 4; }
1495 
1496  if (m_num_args > pos) { command2.affect_engine = this->GetArgFloat(pos++);}
1497  if (m_num_args > pos) { command2.needs_engine = this->GetArgBool (pos++);}
1498  if (m_num_args > pos) { command2.plays_sound = this->GetArgBool (pos++);}
1499 
1500  m_current_module->commands2.push_back(command2);
1501 }
1502 
1503 void Parser::ParseCollisionBox()
1504 {
1505  CollisionBox collisionbox;
1506 
1507  Ogre::StringVector tokens = Ogre::StringUtil::split(m_current_line, ",");
1508  Ogre::StringVector::iterator iter = tokens.begin();
1509  for ( ; iter != tokens.end(); iter++)
1510  {
1511  collisionbox.nodes.push_back( this->_ParseNodeRef(*iter) );
1512  }
1513 
1514  m_current_module->collisionboxes.push_back(collisionbox);
1515 }
1516 
1517 void Parser::ParseCinecam()
1518 {
1519  if (! this->CheckNumArguments(11)) { return; }
1520 
1521  Cinecam cinecam;
1522  cinecam.beam_defaults = m_user_beam_defaults;
1523  cinecam.node_defaults = m_user_node_defaults;
1524 
1525  // Required arguments
1526  cinecam.position.x = this->GetArgFloat ( 0);
1527  cinecam.position.y = this->GetArgFloat ( 1);
1528  cinecam.position.z = this->GetArgFloat ( 2);
1529  cinecam.nodes[0] = this->GetArgNodeRef( 3);
1530  cinecam.nodes[1] = this->GetArgNodeRef( 4);
1531  cinecam.nodes[2] = this->GetArgNodeRef( 5);
1532  cinecam.nodes[3] = this->GetArgNodeRef( 6);
1533  cinecam.nodes[4] = this->GetArgNodeRef( 7);
1534  cinecam.nodes[5] = this->GetArgNodeRef( 8);
1535  cinecam.nodes[6] = this->GetArgNodeRef( 9);
1536  cinecam.nodes[7] = this->GetArgNodeRef(10);
1537 
1538  // Optional arguments
1539  if (m_num_args > 11) { cinecam.spring = this->GetArgFloat(11); }
1540  if (m_num_args > 12) { cinecam.damping = this->GetArgFloat(12); }
1541 
1542  if (m_num_args > 13)
1543  {
1544  float value = this->GetArgFloat(13);
1545  if (value > 0.f) // Invalid input (for example illegal trailing ";pseudo-comment") parses as 0
1546  cinecam.node_mass = value;
1547  }
1548 
1549  if (m_sequential_importer.IsEnabled())
1550  {
1551  m_sequential_importer.AddGeneratedNode(Keyword::CINECAM);
1552  }
1553 
1554  m_current_module->cinecam.push_back(cinecam);
1555 }
1556 
1557 void Parser::ParseCameraRails()
1558 {
1559  m_current_camera_rail->nodes.push_back( this->GetArgNodeRef(0) );
1560 }
1561 
1562 void Parser::ParseBrakes()
1563 {
1564  if (!this->CheckNumArguments(1)) { return; }
1565 
1566  Brakes brakes;
1567  brakes.default_braking_force = this->GetArgFloat(0);
1568  if (m_num_args > 1)
1569  {
1570  brakes.parking_brake_force = this->GetArgFloat(1);
1571  }
1572  m_current_module->brakes.push_back(brakes);
1573 }
1574 
1575 void Parser::ParseAxles()
1576 {
1577  Axle axle;
1578 
1579  Ogre::StringVector tokens = Ogre::StringUtil::split(m_current_line, ",");
1580  Ogre::StringVector::iterator iter = tokens.begin();
1581  for ( ; iter != tokens.end(); iter++)
1582  {
1583  std::smatch results;
1584  if (! std::regex_search(*iter, results, Regexes::SECTION_AXLES_PROPERTY))
1585  {
1586  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "Invalid property, ignoring whole line...");
1587  return;
1588  }
1589  // NOTE: Positions in 'results' array match E_CAPTURE*() positions (starting with 1) in the respective regex.
1590 
1591  if (results[1].matched)
1592  {
1593  unsigned int wheel_index = PARSEINT(results[2]) - 1;
1594  axle.wheels[wheel_index][0] = _ParseNodeRef(results[3]);
1595  axle.wheels[wheel_index][1] = _ParseNodeRef(results[4]);
1596  }
1597  else if (results[5].matched)
1598  {
1599  this->_ParseDifferentialTypes(axle.options, results[6].str());
1600  }
1601  }
1602 
1603  m_current_module->axles.push_back(axle);
1604 }
1605 
1606 void Parser::ParseInterAxles()
1607 {
1608  auto args = Ogre::StringUtil::split(m_current_line, ",");
1609  if (args.size() < 2) { return; }
1610 
1611  InterAxle interaxle;
1612 
1613  interaxle.a1 = this->ParseArgInt(args[0].c_str()) - 1;
1614  interaxle.a2 = this->ParseArgInt(args[1].c_str()) - 1;
1615 
1616  std::smatch results;
1617  if (! std::regex_search(args[2], results, Regexes::SECTION_AXLES_PROPERTY))
1618  {
1619  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "Invalid property, ignoring whole line...");
1620  return;
1621  }
1622  // NOTE: Positions in 'results' array match E_CAPTURE*() positions (starting with 1) in the respective regex.
1623 
1624  if (results[5].matched)
1625  {
1626  this->_ParseDifferentialTypes(interaxle.options, results[6].str());
1627  }
1628 
1629  m_current_module->interaxles.push_back(interaxle);
1630 }
1631 
1632 void Parser::ParseAirbrakes()
1633 {
1634  if (! this->CheckNumArguments(14)) { return; }
1635 
1636  Airbrake airbrake;
1637  airbrake.reference_node = this->GetArgNodeRef( 0);
1638  airbrake.x_axis_node = this->GetArgNodeRef( 1);
1639  airbrake.y_axis_node = this->GetArgNodeRef( 2);
1640  airbrake.aditional_node = this->GetArgNodeRef( 3);
1641  airbrake.offset.x = this->GetArgFloat ( 4);
1642  airbrake.offset.y = this->GetArgFloat ( 5);
1643  airbrake.offset.z = this->GetArgFloat ( 6);
1644  airbrake.width = this->GetArgFloat ( 7);
1645  airbrake.height = this->GetArgFloat ( 8);
1646  airbrake.max_inclination_angle = this->GetArgFloat ( 9);
1647  airbrake.texcoord_x1 = this->GetArgFloat (10);
1648  airbrake.texcoord_y1 = this->GetArgFloat (11);
1649  airbrake.texcoord_x2 = this->GetArgFloat (12);
1650  airbrake.texcoord_y2 = this->GetArgFloat (13);
1651 
1652  m_current_module->airbrakes.push_back(airbrake);
1653 }
1654 
1655 void Parser::ParseAssetpacks()
1656 {
1657  if (! this->CheckNumArguments(1)) { return; }
1658 
1659  Assetpack assetpack;
1660 
1661  assetpack.filename = this->GetArgStr(0);
1662 
1663  m_current_module->assetpacks.push_back(assetpack);
1664 }
1665 
1666 void Parser::ParseVideoCamera()
1667 {
1668  if (! this->CheckNumArguments(19)) { return; }
1669 
1670  VideoCamera videocamera;
1671 
1672  videocamera.reference_node = this->GetArgNodeRef ( 0);
1673  videocamera.left_node = this->GetArgNodeRef ( 1);
1674  videocamera.bottom_node = this->GetArgNodeRef ( 2);
1675  videocamera.alt_reference_node = this->GetArgNullableNode( 3);
1676  videocamera.alt_orientation_node = this->GetArgNullableNode( 4);
1677  videocamera.offset.x = this->GetArgFloat ( 5);
1678  videocamera.offset.y = this->GetArgFloat ( 6);
1679  videocamera.offset.z = this->GetArgFloat ( 7);
1680  videocamera.rotation.x = this->GetArgFloat ( 8);
1681  videocamera.rotation.y = this->GetArgFloat ( 9);
1682  videocamera.rotation.z = this->GetArgFloat (10);
1683  videocamera.field_of_view = this->GetArgFloat (11);
1684  videocamera.texture_width = this->GetArgInt (12);
1685  videocamera.texture_height = this->GetArgInt (13);
1686  videocamera.min_clip_distance = this->GetArgFloat (14);
1687  videocamera.max_clip_distance = this->GetArgFloat (15);
1688  videocamera.camera_role = this->GetArgVideoCamRole(16);
1689  videocamera.camera_mode = this->GetArgInt (17);
1690  videocamera.material_name = this->GetArgStr (18);
1691 
1692  if (m_num_args > 19) { videocamera.camera_name = this->GetArgStr(19); }
1693 
1694  m_current_module->videocameras.push_back(videocamera);
1695 }
1696 
1697 void Parser::ParseCameras()
1698 {
1699  if (! this->CheckNumArguments(3)) { return; }
1700 
1701  Camera camera;
1702  camera.center_node = this->GetArgNodeRef(0);
1703  camera.back_node = this->GetArgNodeRef(1);
1704  camera.left_node = this->GetArgNodeRef(2);
1705 
1706  m_current_module->cameras.push_back(camera);
1707 }
1708 
1709 void Parser::ParseTurbopropsUnified()
1710 {
1711  bool is_turboprop_2 = m_current_block == Keyword::TURBOPROPS2;
1712 
1713  if (! this->CheckNumArguments(is_turboprop_2 ? 9 : 8)) { return; }
1714 
1715  Turboprop2 turboprop;
1716 
1717  turboprop.reference_node = this->GetArgNodeRef(0);
1718  turboprop.axis_node = this->GetArgNodeRef(1);
1719  turboprop.blade_tip_nodes[0] = this->GetArgNodeRef(2);
1720  turboprop.blade_tip_nodes[1] = this->GetArgNodeRef(3);
1721  turboprop.blade_tip_nodes[2] = this->GetArgNullableNode(4);
1722  turboprop.blade_tip_nodes[3] = this->GetArgNullableNode(5);
1723 
1724  int offset = 0;
1725 
1726  if (is_turboprop_2)
1727  {
1728  turboprop.couple_node = this->GetArgNullableNode(6);
1729 
1730  offset = 1;
1731  }
1732 
1733  turboprop.turbine_power_kW = this->GetArgFloat (6 + offset);
1734  turboprop.airfoil = this->GetArgStr (7 + offset);
1735 
1736  m_current_module->turboprops2.push_back(turboprop);
1737 }
1738 
1739 void Parser::ParseTurbojets()
1740 {
1741  if (! this->CheckNumArguments(9)) { return; }
1742 
1743  Turbojet turbojet;
1744  turbojet.front_node = this->GetArgNodeRef(0);
1745  turbojet.back_node = this->GetArgNodeRef(1);
1746  turbojet.side_node = this->GetArgNodeRef(2);
1747  turbojet.is_reversable = this->GetArgInt (3);
1748  turbojet.dry_thrust = this->GetArgFloat (4);
1749  turbojet.wet_thrust = this->GetArgFloat (5);
1750  turbojet.front_diameter = this->GetArgFloat (6);
1751  turbojet.back_diameter = this->GetArgFloat (7);
1752  turbojet.nozzle_length = this->GetArgFloat (8);
1753 
1754  m_current_module->turbojets.push_back(turbojet);
1755 }
1756 
1757 void Parser::ParseTriggers()
1758 {
1759  if (! this->CheckNumArguments(6)) { return; }
1760 
1761  Trigger trigger;
1762  trigger.beam_defaults = m_user_beam_defaults;
1763  trigger.detacher_group = m_current_detacher_group;
1764  trigger.nodes[0] = this->GetArgNodeRef(0);
1765  trigger.nodes[1] = this->GetArgNodeRef(1);
1766  trigger.contraction_trigger_limit = this->GetArgFloat (2);
1767  trigger.expansion_trigger_limit = this->GetArgFloat (3);
1768  trigger.shortbound_trigger_action = this->GetArgInt (4);
1769  trigger.longbound_trigger_action = this->GetArgInt (5);
1770  if (m_num_args > 6) trigger.options = this->GetArgTriggerOptions(6);
1771  if (m_num_args > 7) trigger.boundary_timer = this->GetArgFloat(7);
1772 
1773  m_current_module->triggers.push_back(trigger);
1774 }
1775 
1776 void Parser::ParseTorqueCurve()
1777 {
1778  if (m_current_module->torquecurve.size() == 0)
1779  {
1780  m_current_module->torquecurve.push_back(TorqueCurve());
1781  }
1782 
1783  Ogre::StringVector args = Ogre::StringUtil::split(m_current_line, ",");
1784 
1785  if (args.size() == 1u)
1786  {
1787  m_current_module->torquecurve[0].predefined_func_name = args[0];
1788  }
1789  else if (args.size() == 2u)
1790  {
1791  TorqueCurve::Sample sample;
1792  sample.power = this->ParseArgFloat(args[0].c_str());
1793  sample.torque_percent = this->ParseArgFloat(args[1].c_str());
1794  m_current_module->torquecurve[0].samples.push_back(sample);
1795  }
1796  else
1797  {
1798  // Consistent with 0.38's parser.
1799  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "too many arguments, skipping");
1800  }
1801 }
1802 
1803 void Parser::ParseTies()
1804 {
1805  if (! this->CheckNumArguments(5)) { return; }
1806 
1807  Tie tie;
1808  tie.beam_defaults = m_user_beam_defaults;
1809  tie.detacher_group = m_current_detacher_group;
1810 
1811  tie.root_node = this->GetArgNodeRef(0);
1812  tie.max_reach_length = this->GetArgFloat (1);
1813  tie.auto_shorten_rate = this->GetArgFloat (2);
1814  tie.min_length = this->GetArgFloat (3);
1815  tie.max_length = this->GetArgFloat (4);
1816 
1817  if (m_num_args > 5)
1818  {
1819  for (char c: this->GetArgStr(5))
1820  {
1821  switch (c)
1822  {
1823  case (char)TieOption::n_DUMMY:
1824  case (char)TieOption::v_DUMMY:
1825  break;
1826 
1827  case (char)TieOption::i_INVISIBLE:
1828  tie.options |= Tie::OPTION_i_INVISIBLE;
1829  break;
1830 
1831  case (char)TieOption::s_NO_SELF_LOCK:
1832  tie.options |= Tie::OPTION_s_DISABLE_SELF_LOCK;
1833  break;
1834 
1835  default:
1836  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1837  fmt::format("ignoring invalid option '{}'", c));
1838  break;
1839  }
1840  }
1841  }
1842 
1843  if (m_num_args > 6) { tie.max_stress = this->GetArgFloat (6); }
1844  if (m_num_args > 7) { tie.group = this->GetArgInt (7); }
1845 
1846  m_current_module->ties.push_back(tie);
1847 }
1848 
1849 void Parser::ParseSoundsources()
1850 {
1851  if (! this->CheckNumArguments(2)) { return; }
1852 
1853  SoundSource soundsource;
1854  soundsource.node = this->GetArgNodeRef(0);
1855  soundsource.sound_script_name = this->GetArgStr(1);
1856 
1857  m_current_module->soundsources.push_back(soundsource);
1858 }
1859 
1860 void Parser::ParseSoundsources2()
1861 {
1862  if (! this->CheckNumArguments(3)) { return; }
1863 
1864  SoundSource2 soundsource2;
1865  soundsource2.node = this->GetArgNodeRef(0);
1866  soundsource2.mode = this->GetArgInt(1);
1867  soundsource2.sound_script_name = this->GetArgStr(2);
1868 
1869  if (soundsource2.mode < -2)
1870  {
1871  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
1872  fmt::format("invalid mode {}, falling back to default -2", soundsource2.mode));
1873  soundsource2.mode = -2;
1874  }
1875 
1876  m_current_module->soundsources2.push_back(soundsource2);
1877 }
1878 
1879 void Parser::ParseSlidenodes()
1880 {
1881  Ogre::StringVector args = Ogre::StringUtil::split(m_current_line, ", ");
1882  m_num_args = (int)args.size();
1883  if (! this->CheckNumArguments(2)) { return; }
1884 
1885  SlideNode slidenode;
1886  slidenode.slide_node = this->_ParseNodeRef(args[0]);
1887 
1888  bool in_rail_node_list = true;
1889 
1890  for (auto itor = args.begin() + 1; itor != args.end(); ++itor)
1891  {
1892  char c = toupper(itor->at(0));
1893  switch (c)
1894  {
1895  case 'S':
1896  slidenode.spring_rate = this->ParseArgFloat(itor->substr(1));
1897  slidenode._spring_rate_set = true;
1898  in_rail_node_list = false;
1899  break;
1900  case 'B':
1901  slidenode.break_force = this->ParseArgFloat(itor->substr(1));
1902  slidenode._break_force_set = true;
1903  in_rail_node_list = false;
1904  break;
1905  case 'T':
1906  slidenode.tolerance = this->ParseArgFloat(itor->substr(1));
1907  slidenode._tolerance_set = true;
1908  in_rail_node_list = false;
1909  break;
1910  case 'R':
1911  slidenode.attachment_rate = this->ParseArgFloat(itor->substr(1));
1912  slidenode._attachment_rate_set = true;
1913  in_rail_node_list = false;
1914  break;
1915  case 'G':
1916  slidenode.railgroup_id = this->ParseArgFloat(itor->substr(1));
1917  slidenode._railgroup_id_set = true;
1918  in_rail_node_list = false;
1919  break;
1920  case 'D':
1921  slidenode.max_attach_dist = this->ParseArgFloat(itor->substr(1));
1922  slidenode._max_attach_dist_set = true;
1923  in_rail_node_list = false;
1924  break;
1925  case 'C':
1926  switch (itor->at(1))
1927  {
1928  case 'a':
1929  BITMASK_SET_1(slidenode.constraint_flags, SlideNode::CONSTRAINT_ATTACH_ALL);
1930  break;
1931  case 'f':
1932  BITMASK_SET_1(slidenode.constraint_flags, SlideNode::CONSTRAINT_ATTACH_FOREIGN);
1933  break;
1934  case 's':
1935  BITMASK_SET_1(slidenode.constraint_flags, SlideNode::CONSTRAINT_ATTACH_SELF);
1936  break;
1937  case 'n':
1938  BITMASK_SET_1(slidenode.constraint_flags, SlideNode::CONSTRAINT_ATTACH_NONE);
1939  break;
1940  default:
1941  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
1942  fmt::format("Ignoring invalid option '{}'", itor->at(1)));
1943  break;
1944  }
1945  in_rail_node_list = false;
1946  break;
1947  default:
1948  if (in_rail_node_list)
1949  slidenode.rail_node_ranges.push_back( _ParseNodeRef(*itor));
1950  break;
1951  }
1952  }
1953 
1954  m_current_module->slidenodes.push_back(slidenode);
1955 }
1956 
1957 void Parser::ParseShock3()
1958 {
1959  if (! this->CheckNumArguments(15)) { return; }
1960 
1961  Shock3 shock_3;
1962  shock_3.beam_defaults = m_user_beam_defaults;
1963  shock_3.detacher_group = m_current_detacher_group;
1964 
1965  shock_3.nodes[0] = this->GetArgNodeRef( 0);
1966  shock_3.nodes[1] = this->GetArgNodeRef( 1);
1967  shock_3.spring_in = this->GetArgFloat ( 2);
1968  shock_3.damp_in = this->GetArgFloat ( 3);
1969  shock_3.damp_in_slow = this->GetArgFloat ( 4);
1970  shock_3.split_vel_in = this->GetArgFloat ( 5);
1971  shock_3.damp_in_fast = this->GetArgFloat ( 6);
1972  shock_3.spring_out = this->GetArgFloat ( 7);
1973  shock_3.damp_out = this->GetArgFloat ( 8);
1974  shock_3.damp_out_slow = this->GetArgFloat ( 9);
1975  shock_3.split_vel_out = this->GetArgFloat (10);
1976  shock_3.damp_out_fast = this->GetArgFloat (11);
1977  shock_3.short_bound = this->GetArgFloat (12);
1978  shock_3.long_bound = this->GetArgFloat (13);
1979  shock_3.precompression = this->GetArgFloat (14);
1980 
1981  if (m_num_args > 15) shock_3.options = this->GetArgShock3Options(15);
1982 
1983  m_current_module->shocks3.push_back(shock_3);
1984 }
1985 
1986 void Parser::ParseShock2()
1987 {
1988  if (! this->CheckNumArguments(13)) { return; }
1989 
1990  Shock2 shock_2;
1991  shock_2.beam_defaults = m_user_beam_defaults;
1992  shock_2.detacher_group = m_current_detacher_group;
1993 
1994  shock_2.nodes[0] = this->GetArgNodeRef( 0);
1995  shock_2.nodes[1] = this->GetArgNodeRef( 1);
1996  shock_2.spring_in = this->GetArgFloat ( 2);
1997  shock_2.damp_in = this->GetArgFloat ( 3);
1998  shock_2.progress_factor_spring_in = this->GetArgFloat ( 4);
1999  shock_2.progress_factor_damp_in = this->GetArgFloat ( 5);
2000  shock_2.spring_out = this->GetArgFloat ( 6);
2001  shock_2.damp_out = this->GetArgFloat ( 7);
2002  shock_2.progress_factor_spring_out = this->GetArgFloat ( 8);
2003  shock_2.progress_factor_damp_out = this->GetArgFloat ( 9);
2004  shock_2.short_bound = this->GetArgFloat (10);
2005  shock_2.long_bound = this->GetArgFloat (11);
2006  shock_2.precompression = this->GetArgFloat (12);
2007 
2008  if (m_num_args > 13) shock_2.options = this->GetArgShock2Options(13);
2009 
2010  m_current_module->shocks2.push_back(shock_2);
2011 }
2012 
2013 void Parser::ParseShock()
2014 {
2015  if (! this->CheckNumArguments(7)) { return; }
2016 
2017  Shock shock;
2018  shock.beam_defaults = m_user_beam_defaults;
2019  shock.detacher_group = m_current_detacher_group;
2020 
2021  shock.nodes[0] = this->GetArgNodeRef(0);
2022  shock.nodes[1] = this->GetArgNodeRef(1);
2023  shock.spring_rate = this->GetArgFloat (2);
2024  shock.damping = this->GetArgFloat (3);
2025  shock.short_bound = this->GetArgFloat (4);
2026  shock.long_bound = this->GetArgFloat (5);
2027  shock.precompression = this->GetArgFloat (6);
2028  if (m_num_args > 7) shock.options = this->GetArgShockOptions(7);
2029 
2030  m_current_module->shocks.push_back(shock);
2031 }
2032 
2033 Node::Ref Parser::_ParseNodeRef(std::string const & node_id_str)
2034 {
2035  if (m_sequential_importer.IsEnabled())
2036  {
2037  // Import of legacy fileformatversion
2038  int node_id_num = PARSEINT(node_id_str);
2039  if (node_id_num < 0)
2040  {
2041  node_id_num *= -1;
2042  }
2043  // Since fileformatversion is not known from the beginning of parsing, 2 states must be kept
2044  // at the same time: IMPORT_STATE and REGULAR_STATE. The outer logic must make the right pick.
2045  unsigned int flags = Node::Ref::IMPORT_STATE_IS_VALID | // Import state
2046  Node::Ref::REGULAR_STATE_IS_VALID | Node::Ref::REGULAR_STATE_IS_NAMED; // Regular state (fileformatversion >= 450)
2047  if (m_any_named_node_defined)
2048  {
2049  flags |= Node::Ref::IMPORT_STATE_MUST_CHECK_NAMED_FIRST;
2050  }
2051  return Node::Ref(node_id_str, node_id_num, flags, m_current_line_number);
2052  }
2053  else
2054  {
2055  // fileformatversion >= 450, use named-only nodes
2056  return Node::Ref(node_id_str, 0, Node::Ref::REGULAR_STATE_IS_VALID | Node::Ref::REGULAR_STATE_IS_NAMED, m_current_line_number);
2057  }
2058 }
2059 
2060 void Parser::ParseDirectiveSetDefaultMinimass()
2061 {
2062  if (! this->CheckNumArguments(2)) { return; } // Directive name + parameter
2063 
2064  m_set_default_minimass = std::shared_ptr<DefaultMinimass>(new DefaultMinimass());
2065  m_set_default_minimass->min_mass_Kg = this->GetArgFloat(1);
2066 }
2067 
2068 void Parser::ParseFlaregroupsNoImport()
2069 {
2070  // ;flare_groups_no_import
2071  // ; <flaretype> <controlnumber 1-10>
2072  if (!this->CheckNumArguments(1)) { return; }
2073 
2074  FlaregroupNoImport fni;
2075  fni.type = this->GetArgFlareType(0);
2076  if (m_num_args > 1)
2077  {
2078  fni.control_number = this->GetArgInt(1);
2079  if (fni.control_number < 1)
2080  {
2081  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, fmt::format("flaregroup_no_import: parameter <control_number> must be 1-10; got {}, clamping to 1", fni.control_number));
2082  }
2083  if (fni.control_number > 10)
2084  {
2085  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, fmt::format("flaregroup_no_import: parameter <control_number> must be 1-10; got {}, clamping to 10", fni.control_number));
2086  }
2087  }
2088 
2089  LOG(fmt::format("[RoR|RigDef::Parser] parsed FlaregroupNoImport ({} {})", (char)fni.type, fni.control_number));
2090  m_current_module->flaregroups_no_import.push_back(fni);
2091 }
2092 
2093 void Parser::ParseDirectiveSetInertiaDefaults()
2094 {
2095  if (! this->CheckNumArguments(2)) { return; }
2096 
2097  float start_delay = this->GetArgFloat(1);
2098  float stop_delay = 0;
2099  if (m_num_args > 2) { stop_delay = this->GetArgFloat(2); }
2100 
2101  if (start_delay < 0 || stop_delay < 0)
2102  {
2103  m_user_default_inertia = m_ror_default_inertia; // Reset and return
2104  return;
2105  }
2106 
2107  // Create
2108  Inertia* i = new Inertia(*m_user_default_inertia.get());
2109  i->start_delay_factor = start_delay;
2110  i->stop_delay_factor = stop_delay;
2111 
2112  if (m_num_args > 3) { i->start_function = this->GetArgStr(3); }
2113  if (m_num_args > 4) { i->stop_function = this->GetArgStr(4); }
2114 
2115  m_user_default_inertia = std::shared_ptr<Inertia>(i);
2116 }
2117 
2118 void Parser::ParseScrewprops()
2119 {
2120  if (! this->CheckNumArguments(4)) { return; }
2121 
2122  Screwprop screwprop;
2123 
2124  screwprop.prop_node = this->GetArgNodeRef(0);
2125  screwprop.back_node = this->GetArgNodeRef(1);
2126  screwprop.top_node = this->GetArgNodeRef(2);
2127  screwprop.power = this->GetArgFloat (3);
2128 
2129  m_current_module->screwprops.push_back(screwprop);
2130 }
2131 
2132 void Parser::ParseScripts()
2133 {
2134  if (!this->CheckNumArguments(1)) { return; }
2135 
2136  Script script;
2137 
2138  script.filename = this->GetArgStr(0);
2139 
2140  m_current_module->scripts.push_back(script);
2141 }
2142 
2143 void Parser::ParseRotatorsUnified()
2144 {
2145  if (! this->CheckNumArguments(13)) { return; }
2146 
2147  Rotator2 rotator;
2148  rotator.inertia_defaults = m_user_default_inertia;
2149 
2150  rotator.axis_nodes[0] = this->GetArgNodeRef( 0);
2151  rotator.axis_nodes[1] = this->GetArgNodeRef( 1);
2152  rotator.base_plate_nodes[0] = this->GetArgNodeRef( 2);
2153  rotator.base_plate_nodes[1] = this->GetArgNodeRef( 3);
2154  rotator.base_plate_nodes[2] = this->GetArgNodeRef( 4);
2155  rotator.base_plate_nodes[3] = this->GetArgNodeRef( 5);
2156  rotator.rotating_plate_nodes[0] = this->GetArgNodeRef( 6);
2157  rotator.rotating_plate_nodes[1] = this->GetArgNodeRef( 7);
2158  rotator.rotating_plate_nodes[2] = this->GetArgNodeRef( 8);
2159  rotator.rotating_plate_nodes[3] = this->GetArgNodeRef( 9);
2160  rotator.rate = this->GetArgFloat (10);
2161  rotator.spin_left_key = this->GetArgInt (11);
2162  rotator.spin_right_key = this->GetArgInt (12);
2163 
2164  int offset = 0;
2165 
2166  if (m_current_block == Keyword::ROTATORS2)
2167  {
2168  if (! this->CheckNumArguments(16)) { return; }
2169  if (m_num_args > 13) { rotator.rotating_force = this->GetArgFloat(13); }
2170  if (m_num_args > 14) { rotator.tolerance = this->GetArgFloat(14); }
2171  if (m_num_args > 15) { rotator.description = this->GetArgStr (15); }
2172 
2173  offset = 3;
2174  }
2175 
2176  this->ParseOptionalInertia(rotator.inertia, 13 + offset);
2177  if (m_num_args > 17 + offset) { rotator.engine_coupling = this->GetArgFloat(17 + offset); }
2178  if (m_num_args > 18 + offset) { rotator.needs_engine = this->GetArgBool (18 + offset); }
2179 
2180  if (m_current_block == Keyword::ROTATORS2)
2181  {
2182  m_current_module->rotators2.push_back(rotator);
2183  }
2184  else
2185  {
2186  m_current_module->rotators.push_back(rotator);
2187  }
2188 }
2189 
2190 void Parser::ParseFileinfo()
2191 {
2192  if (! this->CheckNumArguments(2)) { return; }
2193 
2194  Fileinfo fileinfo;
2195 
2196  fileinfo.unique_id = this->GetArgStr(1);
2197  Ogre::StringUtil::trim(fileinfo.unique_id);
2198 
2199  if (m_num_args > 2) { fileinfo.category_id = this->GetArgInt(2); }
2200  if (m_num_args > 3) { fileinfo.file_version = this->GetArgInt(3); }
2201 
2202  m_current_module->fileinfo.push_back(fileinfo);
2203 
2204  m_current_block = Keyword::INVALID;
2205 }
2206 
2207 void Parser::ParseRopes()
2208 {
2209  if (! this->CheckNumArguments(2)) { return; }
2210 
2211  Rope rope;
2212  rope.beam_defaults = m_user_beam_defaults;
2213  rope.detacher_group = m_current_detacher_group;
2214  rope.root_node = this->GetArgNodeRef(0);
2215  rope.end_node = this->GetArgNodeRef(1);
2216 
2217  if (m_num_args > 2) { rope.invisible = (this->GetArgChar(2) == 'i'); }
2218 
2219  m_current_module->ropes.push_back(rope);
2220 }
2221 
2222 void Parser::ParseRopables()
2223 {
2224  if (! this->CheckNumArguments(1)) { return; }
2225 
2226  Ropable ropable;
2227  ropable.node = this->GetArgNodeRef(0);
2228 
2229  if (m_num_args > 1) { ropable.group = this->GetArgInt(1); }
2230  if (m_num_args > 2) { ropable.has_multilock = (this->GetArgInt(2) == 1); }
2231 
2232  m_current_module->ropables.push_back(ropable);
2233 }
2234 
2235 void Parser::ParseRailGroups()
2236 {
2237  Ogre::StringVector args = Ogre::StringUtil::split(m_current_line, ",");
2238  m_num_args = (int)args.size();
2239  if (! this->CheckNumArguments(3)) { return; }
2240 
2241  RailGroup railgroup;
2242  railgroup.id = this->ParseArgInt(args[0].c_str());
2243 
2244  for (auto itor = args.begin() + 1; itor != args.end(); itor++)
2245  {
2246  railgroup.node_list.push_back( this->_ParseNodeRef(*itor));
2247  }
2248 
2249  m_current_module->railgroups.push_back(railgroup);
2250 }
2251 
2252 void Parser::ParseProps()
2253 {
2254  if (! this->CheckNumArguments(10)) { return; }
2255 
2256  Prop prop;
2257  prop.reference_node = this->GetArgNodeRef(0);
2258  prop.x_axis_node = this->GetArgNodeRef(1);
2259  prop.y_axis_node = this->GetArgNodeRef(2);
2260  prop.offset.x = this->GetArgFloat (3);
2261  prop.offset.y = this->GetArgFloat (4);
2262  prop.offset.z = this->GetArgFloat (5);
2263  prop.rotation.x = this->GetArgFloat (6);
2264  prop.rotation.y = this->GetArgFloat (7);
2265  prop.rotation.z = this->GetArgFloat (8);
2266  prop.mesh_name = this->GetArgStr(9);
2267  prop.special = Parser::IdentifySpecialProp(prop.mesh_name);
2268 
2269  if ((prop.special == SpecialProp::BEACON) && (m_num_args >= 14))
2270  {
2271  prop.special_prop_beacon.flare_material_name = this->GetArgStr(10);
2272  Ogre::StringUtil::trim(prop.special_prop_beacon.flare_material_name);
2273 
2274  prop.special_prop_beacon.color = Ogre::ColourValue(
2275  this->GetArgFloat(11), this->GetArgFloat(12), this->GetArgFloat(13));
2276  }
2277  else if (prop.special == SpecialProp::DASHBOARD_LEFT ||
2278  prop.special == SpecialProp::DASHBOARD_RIGHT)
2279  {
2280  if (m_num_args > 10) prop.special_prop_dashboard.mesh_name = this->GetArgStr(10);
2281  if (m_num_args > 13)
2282  {
2283  prop.special_prop_dashboard.offset = Ogre::Vector3(this->GetArgFloat(11), this->GetArgFloat(12), this->GetArgFloat(13));
2285  }
2286  if (m_num_args > 14) prop.special_prop_dashboard.rotation_angle = this->GetArgFloat(14);
2287  }
2288 
2289  m_current_module->props.push_back(prop);
2290 }
2291 
2292 void Parser::ParsePistonprops()
2293 {
2294  if (!this->CheckNumArguments(10)) { return; }
2295 
2296  Pistonprop pistonprop;
2297  pistonprop.reference_node = this->GetArgNodeRef (0);
2298  pistonprop.axis_node = this->GetArgNodeRef (1);
2299  pistonprop.blade_tip_nodes[0] = this->GetArgNodeRef (2);
2300  pistonprop.blade_tip_nodes[1] = this->GetArgNodeRef (3);
2301  pistonprop.blade_tip_nodes[2] = this->GetArgNullableNode(4);
2302  pistonprop.blade_tip_nodes[3] = this->GetArgNullableNode(5);
2303  pistonprop.couple_node = this->GetArgNullableNode(6);
2304  pistonprop.turbine_power_kW = this->GetArgFloat (7);
2305  pistonprop.pitch = this->GetArgFloat (8);
2306  pistonprop.airfoil = this->GetArgStr (9);
2307 
2308  m_current_module->pistonprops.push_back(pistonprop);
2309 
2310 }
2311 
2312 void Parser::ParseParticles()
2313 {
2314  if (!this->CheckNumArguments(3)) { return; }
2315 
2316  Particle particle;
2317  particle.emitter_node = this->GetArgNodeRef(0);
2318  particle.reference_node = this->GetArgNodeRef(1);
2319  particle.particle_system_name = this->GetArgStr (2);
2320 
2321  m_current_module->particles.push_back(particle);
2322 }
2323 
2324 // Static
2325 void Parser::_TrimTrailingComments(std::string const & line_in, std::string & line_out)
2326 {
2327  // Trim trailing comment
2328  // We need to handle a case of lines as [keyword 1, 2, 3 ;;///// Comment!]
2329  int comment_start = static_cast<int>(line_in.find_first_of(";"));
2330  if (comment_start != Ogre::String::npos)
2331  {
2332  line_out = line_in.substr(0, comment_start);
2333  return;
2334  }
2335  // The [//Comment] is harder - the '/' character may also be present in DESCRIPTION arguments!
2336  comment_start = static_cast<int>(line_in.find_last_of("/"));
2337  if (comment_start != Ogre::String::npos)
2338  {
2339  while (comment_start >= 0)
2340  {
2341  char c = line_in[comment_start - 1];
2342  if (c != '/' && c != ' ' && c != '\t')
2343  {
2344  break; // Start of comment found
2345  }
2346  --comment_start;
2347  }
2348  line_out = line_in.substr(0, comment_start);
2349  return;
2350  }
2351  // No comment found
2352  line_out = line_in;
2353 }
2354 
2355 void Parser::ParseNodesUnified()
2356 {
2357  if (! this->CheckNumArguments(4)) { return; }
2358 
2359  Node node;
2360  node.node_defaults = m_user_node_defaults;
2361  node.beam_defaults = m_user_beam_defaults;
2362  node.default_minimass = m_set_default_minimass;
2363  node.detacher_group = m_current_detacher_group;
2364 
2365  if (m_current_block == Keyword::NODES2)
2366  {
2367  std::string node_name = this->GetArgStr(0);
2368  node.id.setStr(node_name);
2369  if (m_sequential_importer.IsEnabled())
2370  {
2371  m_sequential_importer.AddNamedNode(node_name);
2372  }
2373  m_any_named_node_defined = true; // For import logic
2374  }
2375  else
2376  {
2377  const unsigned int node_num = this->GetArgUint(0);
2378  node.id.SetNum(node_num);
2379  if (m_sequential_importer.IsEnabled())
2380  {
2381  m_sequential_importer.AddNumberedNode(node_num);
2382  }
2383  }
2384 
2385  node.position.x = this->GetArgFloat(1);
2386  node.position.y = this->GetArgFloat(2);
2387  node.position.z = this->GetArgFloat(3);
2388  if (m_num_args > 4)
2389  {
2390  node.options = this->GetArgNodeOptions(4);
2391  }
2392  if (m_num_args > 5)
2393  {
2394  if (node.options & Node::OPTION_l_LOAD_WEIGHT)
2395  {
2396  node.load_weight_override = this->GetArgFloat(5);
2397  node._has_load_weight_override = true;
2398  }
2399  else
2400  {
2401  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
2402  "Node has load-weight-override value specified, but option 'l' is not present. Ignoring value...");
2403  }
2404  }
2405 
2406  m_current_module->nodes.push_back(node);
2407 }
2408 
2409 void Parser::ParseMinimass()
2410 {
2411  if (! this->CheckNumArguments(1)) { return; }
2412 
2413  Minimass mm;
2414  mm.global_min_mass_Kg = this->GetArgFloat(0);
2415  if (m_num_args > 1) { mm.option = this->GetArgMinimassOption(1); }
2416 
2417  m_current_module->minimass.push_back(mm);
2418  m_current_block = Keyword::INVALID;
2419 }
2420 
2421 void Parser::ParseFlexBodyWheel()
2422 {
2423  if (! this->CheckNumArguments(16)) { return; }
2424 
2425  FlexBodyWheel flexbody_wheel;
2426  flexbody_wheel.node_defaults = m_user_node_defaults;
2427  flexbody_wheel.beam_defaults = m_user_beam_defaults;
2428 
2429  flexbody_wheel.tyre_radius = this->GetArgFloat ( 0);
2430  flexbody_wheel.rim_radius = this->GetArgFloat ( 1);
2431  flexbody_wheel.width = this->GetArgFloat ( 2);
2432  flexbody_wheel.num_rays = this->GetArgInt ( 3);
2433  flexbody_wheel.nodes[0] = this->GetArgNodeRef ( 4);
2434  flexbody_wheel.nodes[1] = this->GetArgNodeRef ( 5);
2435  flexbody_wheel.rigidity_node = this->GetArgRigidityNode ( 6);
2436  flexbody_wheel.braking = this->GetArgBraking ( 7);
2437  flexbody_wheel.propulsion = this->GetArgPropulsion ( 8);
2438  flexbody_wheel.reference_arm_node = this->GetArgNodeRef ( 9);
2439  flexbody_wheel.mass = this->GetArgFloat (10);
2440  flexbody_wheel.tyre_springiness = this->GetArgFloat (11);
2441  flexbody_wheel.tyre_damping = this->GetArgFloat (12);
2442  flexbody_wheel.rim_springiness = this->GetArgFloat (13);
2443  flexbody_wheel.rim_damping = this->GetArgFloat (14);
2444  flexbody_wheel.side = this->GetArgWheelSide (15);
2445 
2446  if (m_num_args > 16) { flexbody_wheel.rim_mesh_name = this->GetArgStr(16); }
2447  if (m_num_args > 17) { flexbody_wheel.tyre_mesh_name = this->GetArgStr(17); }
2448 
2449  if (m_sequential_importer.IsEnabled())
2450  {
2451  m_sequential_importer.GenerateNodesForWheel(Keyword::FLEXBODYWHEELS, flexbody_wheel.num_rays, flexbody_wheel.rigidity_node.IsValidAnyState());
2452  }
2453 
2454  m_current_module->flexbodywheels.push_back(flexbody_wheel);
2455 }
2456 
2457 void Parser::ParseMaterialFlareBindings()
2458 {
2459  if (! this->CheckNumArguments(2)) { return; }
2460 
2461  MaterialFlareBinding binding;
2462  binding.flare_number = this->GetArgInt(0);
2463  binding.material_name = this->GetArgStr(1);
2464 
2465  m_current_module->materialflarebindings.push_back(binding);
2466 }
2467 
2468 void Parser::ParseManagedMaterials()
2469 {
2470  if (! this->CheckNumArguments(2)) { return; }
2471 
2472  ManagedMaterial managed_mat;
2473  managed_mat.options = m_current_managed_material_options;
2474  managed_mat.name = this->GetArgStr(0);
2475  managed_mat.type = this->GetArgManagedMatType(1);
2476 
2477  if (managed_mat.type != ManagedMaterialType::INVALID)
2478  {
2479  if (! this->CheckNumArguments(3)) { return; }
2480 
2481  managed_mat.diffuse_map = this->GetArgStr(2);
2482 
2483  if (managed_mat.type == ManagedMaterialType::MESH_STANDARD ||
2484  managed_mat.type == ManagedMaterialType::MESH_TRANSPARENT)
2485  {
2486  if (m_num_args > 3) { managed_mat.specular_map = this->GetArgManagedTex(3); }
2487  }
2488  else if (managed_mat.type == ManagedMaterialType::FLEXMESH_STANDARD ||
2489  managed_mat.type == ManagedMaterialType::FLEXMESH_TRANSPARENT)
2490  {
2491  if (m_num_args > 3) { managed_mat.damaged_diffuse_map = this->GetArgManagedTex(3); }
2492  if (m_num_args > 4) { managed_mat.specular_map = this->GetArgManagedTex(4); }
2493  }
2494 
2495  m_current_module->managedmaterials.push_back(managed_mat);
2496  }
2497 }
2498 
2499 void Parser::ParseLockgroups()
2500 {
2501  if (! this->CheckNumArguments(2)) { return; } // Lockgroup num. + at least 1 node...
2502 
2503  Lockgroup lockgroup;
2504  lockgroup.number = this->GetArgInt(0);
2505 
2506  for (int i = 1; i < m_num_args; ++i)
2507  {
2508  lockgroup.nodes.push_back(this->GetArgNodeRef(i));
2509  }
2510 
2511  m_current_module->lockgroups.push_back(lockgroup);
2512 }
2513 
2514 void Parser::ParseHydros()
2515 {
2516  if (! this->CheckNumArguments(3)) { return; }
2517 
2518  Hydro hydro;
2519  hydro.inertia_defaults = m_user_default_inertia;
2520  hydro.detacher_group = m_current_detacher_group;
2521  hydro.beam_defaults = m_user_beam_defaults;
2522 
2523  hydro.nodes[0] = this->GetArgNodeRef(0);
2524  hydro.nodes[1] = this->GetArgNodeRef(1);
2525  hydro.lenghtening_factor = this->GetArgFloat (2);
2526 
2527  if (m_num_args > 3) { hydro.options = this->GetArgHydroOptions(3); }
2528 
2529  if (!hydro.options)
2530  {
2531  hydro.options |= Hydro::OPTION_n_INPUT_NORMAL;
2532  }
2533 
2534  this->ParseOptionalInertia(hydro.inertia, 4);
2535 
2536  m_current_module->hydros.push_back(hydro);
2537 }
2538 
2539 void Parser::ParseOptionalInertia(Inertia & inertia, int index)
2540 {
2541  if (m_num_args > index) { inertia.start_delay_factor = this->GetArgFloat(index++); }
2542  if (m_num_args > index) { inertia.stop_delay_factor = this->GetArgFloat(index++); }
2543  if (m_num_args > index) { inertia.start_function = this->GetArgStr (index++); }
2544  if (m_num_args > index) { inertia.stop_function = this->GetArgStr (index++); }
2545 }
2546 
2547 void Parser::_ParseDifferentialTypes(DifferentialTypeVec& diff_types, std::string const& options_str)
2548 {
2549  for (char c: options_str)
2550  {
2551  switch(c)
2552  {
2553  case (char)DifferentialType::o_OPEN:
2554  case (char)DifferentialType::l_LOCKED:
2555  case (char)DifferentialType::s_SPLIT:
2556  case (char)DifferentialType::v_VISCOUS:
2557  diff_types.push_back(DifferentialType(c));
2558  break;
2559 
2560  default:
2561  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
2562  fmt::format("ignoring invalid differential type '{}'", c));
2563  break;
2564  }
2565  }
2566 }
2567 
2568 void Parser::ParseBeams()
2569 {
2570  if (! this->CheckNumArguments(2)) { return; }
2571 
2572  Beam beam;
2573  beam.defaults = m_user_beam_defaults;
2574  beam.detacher_group = m_current_detacher_group;
2575 
2576  beam.nodes[0] = this->GetArgNodeRef(0);
2577  beam.nodes[1] = this->GetArgNodeRef(1);
2578  if (m_num_args > 2) beam.options = this->GetArgBeamOptions(2);
2579 
2580  if ((m_num_args > 3) && BITMASK_IS_1(beam.options, Beam::OPTION_s_SUPPORT))
2581  {
2582  float support_break_limit = 0.0f;
2583  float support_break_factor = this->GetArgInt(3);
2584  if (support_break_factor > 0.0f)
2585  {
2586  support_break_limit = support_break_factor;
2587  }
2588  beam.extension_break_limit = support_break_limit;
2589  beam._has_extension_break_limit = true;
2590  }
2591 
2592  m_current_module->beams.push_back(beam);
2593 }
2594 
2595 void Parser::ParseAnimator()
2596 {
2597  auto args = Ogre::StringUtil::split(m_current_line, ",");
2598  if (args.size() < 4) { return; }
2599 
2600  Animator animator;
2601  animator.inertia_defaults = m_user_default_inertia;
2602  animator.beam_defaults = m_user_beam_defaults;
2603  animator.detacher_group = m_current_detacher_group;
2604 
2605  animator.nodes[0] = this->_ParseNodeRef(args[0]);
2606  animator.nodes[1] = this->_ParseNodeRef(args[1]);
2607  animator.lenghtening_factor = this->ParseArgFloat(args[2]);
2608 
2609  // Parse options; Just use the split/trim/compare method
2610  Ogre::StringVector attrs = Ogre::StringUtil::split(args[3], "|");
2611 
2612  auto itor = attrs.begin();
2613  auto endi = attrs.end();
2614  for (; itor != endi; ++itor)
2615  {
2616  Ogre::String token = *itor;
2617  Ogre::StringUtil::trim(token);
2618  std::smatch results;
2619  bool is_shortlimit = false;
2620 
2621  // Numbered keywords
2622  if (std::regex_search(token, results, Regexes::PARSE_ANIMATORS_NUMBERED_KEYWORD))
2623  {
2624  if (results[1] == "throttle") animator.aero_animator.flags |= AeroAnimator::OPTION_THROTTLE;
2625  else if (results[1] == "rpm") animator.aero_animator.flags |= AeroAnimator::OPTION_RPM;
2626  else if (results[1] == "aerotorq") animator.aero_animator.flags |= AeroAnimator::OPTION_TORQUE;
2627  else if (results[1] == "aeropit") animator.aero_animator.flags |= AeroAnimator::OPTION_PITCH;
2628  else if (results[1] == "aerostatus") animator.aero_animator.flags |= AeroAnimator::OPTION_STATUS;
2629 
2630  animator.aero_animator.engine_idx = this->ParseArgUint(results[2].str().c_str()) - 1;
2631  }
2632  else if ((is_shortlimit = (token.compare(0, 10, "shortlimit") == 0)) || (token.compare(0, 9, "longlimit") == 0))
2633  {
2634  Ogre::StringVector fields = Ogre::StringUtil::split(token, ":");
2635  if (fields.size() > 1)
2636  {
2637  if (is_shortlimit)
2638  {
2639  animator.short_limit = std::strtod(fields[1].c_str(), nullptr);
2640  animator.flags |= Animator::OPTION_SHORT_LIMIT;
2641  }
2642  else
2643  {
2644  animator.long_limit = std::strtod(fields[1].c_str(), nullptr);
2645  animator.flags |= Animator::OPTION_LONG_LIMIT;
2646  }
2647  }
2648  }
2649  else
2650  {
2651  // Standalone keywords
2652  if (token == "vis") animator.flags |= Animator::OPTION_VISIBLE;
2653  else if (token == "inv") animator.flags |= Animator::OPTION_INVISIBLE;
2654  else if (token == "airspeed") animator.flags |= Animator::OPTION_AIRSPEED;
2655  else if (token == "vvi") animator.flags |= Animator::OPTION_VERTICAL_VELOCITY;
2656  else if (token == "altimeter100k") animator.flags |= Animator::OPTION_ALTIMETER_100K;
2657  else if (token == "altimeter10k") animator.flags |= Animator::OPTION_ALTIMETER_10K;
2658  else if (token == "altimeter1k") animator.flags |= Animator::OPTION_ALTIMETER_1K;
2659  else if (token == "aoa") animator.flags |= Animator::OPTION_ANGLE_OF_ATTACK;
2660  else if (token == "flap") animator.flags |= Animator::OPTION_FLAP;
2661  else if (token == "airbrake") animator.flags |= Animator::OPTION_AIR_BRAKE;
2662  else if (token == "roll") animator.flags |= Animator::OPTION_ROLL;
2663  else if (token == "pitch") animator.flags |= Animator::OPTION_PITCH;
2664  else if (token == "brakes") animator.flags |= Animator::OPTION_BRAKES;
2665  else if (token == "accel") animator.flags |= Animator::OPTION_ACCEL;
2666  else if (token == "clutch") animator.flags |= Animator::OPTION_CLUTCH;
2667  else if (token == "speedo") animator.flags |= Animator::OPTION_SPEEDO;
2668  else if (token == "tacho") animator.flags |= Animator::OPTION_TACHO;
2669  else if (token == "turbo") animator.flags |= Animator::OPTION_TURBO;
2670  else if (token == "parking") animator.flags |= Animator::OPTION_PARKING;
2671  else if (token == "shifterman1") animator.flags |= Animator::OPTION_SHIFT_LEFT_RIGHT;
2672  else if (token == "shifterman2") animator.flags |= Animator::OPTION_SHIFT_BACK_FORTH;
2673  else if (token == "sequential") animator.flags |= Animator::OPTION_SEQUENTIAL_SHIFT;
2674  else if (token == "shifterlin") animator.flags |= Animator::OPTION_GEAR_SELECT;
2675  else if (token == "torque") animator.flags |= Animator::OPTION_TORQUE;
2676  else if (token == "difflock") animator.flags |= Animator::OPTION_DIFFLOCK;
2677  else if (token == "rudderboat") animator.flags |= Animator::OPTION_BOAT_RUDDER;
2678  else if (token == "throttleboat") animator.flags |= Animator::OPTION_BOAT_THROTTLE;
2679  }
2680  }
2681 
2682  m_current_module->animators.push_back(animator);
2683 }
2684 
2685 void Parser::ParseAuthor()
2686 {
2687  if (! this->CheckNumArguments(2)) { return; }
2688 
2689  Author author;
2690  if (m_num_args > 1) { author.type = this->GetArgStr(1); }
2691  if (m_num_args > 2) { author.forum_account_id = this->GetArgInt(2); author._has_forum_account = true; }
2692  if (m_num_args > 3) { author.name = this->GetArgStr(3); }
2693  if (m_num_args > 4) { author.email = this->GetArgStr(4); }
2694 
2695  m_current_module->author.push_back(author);
2696  m_current_block = Keyword::INVALID;
2697 }
2698 
2699 // --------------------------------------------------------------------------
2700 // Utilities
2701 // --------------------------------------------------------------------------
2702 
2703 void Parser::LogMessage(Console::MessageType type, std::string const& msg)
2704 {
2707  type,
2708  fmt::format("{}:{} ({}): {}",
2709  m_filename, m_current_line_number, KeywordToString(m_log_keyword), msg));
2710 }
2711 
2712 Keyword Parser::IdentifyKeyword(const std::string& line)
2713 {
2714  // Search and ignore lettercase
2715  std::smatch results;
2716  std::regex_search(line, results, Regexes::IDENTIFY_KEYWORD_IGNORE_CASE); // Always returns true.
2717 
2718  // The 'results' array contains a complete match at positon [0] and sub-matches starting with [1],
2719  // so we get exact positions in Regexes::IDENTIFY_KEYWORD, which again match Keyword enum members
2720  for (unsigned int i = 1; i < results.size(); i++)
2721  {
2722  std::ssub_match sub = results[i];
2723  if (sub.matched)
2724  {
2725  // Build enum value directly from result offset
2726  return Keyword(i);
2727  }
2728  }
2729 
2730  return Keyword::INVALID;
2731 }
2732 
2733 void Parser::Prepare()
2734 {
2735  m_current_block = Keyword::INVALID;
2736  m_current_line_number = 1;
2737  m_definition = RigDef::DocumentPtr(new Document());
2738  m_any_named_node_defined = false;
2739  m_current_detacher_group = 0; // Global detacher group
2740 
2741  m_user_default_inertia = m_ror_default_inertia;
2742  m_user_node_defaults = m_ror_node_defaults;
2743  m_current_managed_material_options = ManagedMaterialsOptions();
2744 
2745  m_user_beam_defaults = std::shared_ptr<BeamDefaults>(new BeamDefaults);
2746  m_user_beam_defaults->springiness = DEFAULT_SPRING;
2747  m_user_beam_defaults->damping_constant = DEFAULT_DAMP;
2748  m_user_beam_defaults->deformation_threshold = BEAM_DEFORM;
2749  m_user_beam_defaults->breaking_threshold = BEAM_BREAK;
2750  m_user_beam_defaults->visual_beam_diameter = DEFAULT_BEAM_DIAMETER;
2751 
2752  m_root_module = m_definition->root_module;
2753  m_current_module = m_definition->root_module;
2754 
2755  m_sequential_importer.Init(true); // Enabled=true
2756 }
2757 
2758 void Parser::BeginBlock(Keyword keyword)
2759 {
2760  if (keyword == Keyword::INVALID) // also means 'end'
2761  {
2762  // flush staged submesh, if any
2763  if (m_current_submesh != nullptr)
2764  {
2765  m_current_module->submeshes.push_back(*m_current_submesh);
2766  m_current_submesh.reset(); // Set to nullptr
2767  }
2768 
2769  // flush staged camerarail, if any
2770  if (m_current_camera_rail != nullptr)
2771  {
2772  if (m_current_camera_rail->nodes.size() == 0)
2773  {
2774  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING, "Empty section 'camerarail', ignoring...");
2775  }
2776  else
2777  {
2778  m_current_module->camerarail.push_back(*m_current_camera_rail);
2779  m_current_camera_rail.reset();
2780  }
2781  }
2782  }
2783  else if (keyword == Keyword::CAMERARAIL)
2784  {
2785  this->BeginBlock(Keyword::INVALID); // flush staged rail
2786  m_current_camera_rail = std::shared_ptr<CameraRail>( new CameraRail() );
2787  }
2788  m_current_block = keyword;
2789 }
2790 
2791 void Parser::ProcessChangeModuleLine(Keyword keyword)
2792 {
2793  // Determine and verify new module
2794  std::string new_module_name;
2795  if (keyword == Keyword::END_SECTION)
2796  {
2797  if (m_current_module == m_root_module)
2798  {
2799  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "Misplaced keyword 'end_section' (already in root module), ignoring...");
2800  return;
2801  }
2802  new_module_name = ROOT_MODULE_NAME;
2803  }
2804  else if (keyword == Keyword::SECTION)
2805  {
2806  if (!this->CheckNumArguments(3)) // Syntax: "section VERSION NAME"; VERSION is unused
2807  {
2808  return; // Error already reported
2809  }
2810 
2811  new_module_name = this->GetArgStr(2);
2812  if (new_module_name == m_current_module->name)
2813  {
2814  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR, "Attempt to re-enter current module, ignoring...");
2815  return;
2816  }
2817  }
2818 
2819  // Perform the switch
2820  this->BeginBlock(Keyword::INVALID);
2821 
2822  if (new_module_name == ROOT_MODULE_NAME)
2823  {
2824  m_current_module = m_root_module;
2825  return;
2826  }
2827 
2828  auto search_itor = m_definition->user_modules.find(new_module_name);
2829  if (search_itor != m_definition->user_modules.end())
2830  {
2831  m_current_module = search_itor->second;
2832  }
2833  else
2834  {
2835  m_current_module = std::make_shared<Document::Module>(new_module_name);
2836  m_definition->user_modules.insert(std::make_pair(new_module_name, m_current_module));
2837  }
2838 }
2839 
2840 void Parser::ParseDirectiveSection()
2841 {
2842  this->ProcessChangeModuleLine(Keyword::SECTION);
2843 }
2844 
2845 void Parser::ParseDirectiveSectionConfig()
2846 {
2847  // FIXME: restore this, see branch 'retro-0407'
2848 }
2849 
2850 void Parser::Finalize()
2851 {
2852  this->BeginBlock(Keyword::INVALID);
2853 
2854  if (m_sequential_importer.IsEnabled())
2855  {
2856  m_sequential_importer.Process( m_definition );
2857  }
2858 }
2859 
2860 std::string Parser::GetArgStr(int index)
2861 {
2862  return std::string(m_args[index].start, m_args[index].length);
2863 }
2864 
2865 char Parser::GetArgChar(int index)
2866 {
2867  return *(m_args[index].start);
2868 }
2869 
2870 WheelSide Parser::GetArgWheelSide(int index)
2871 {
2872  char c = this->GetArgChar(index);
2873  switch (c)
2874  {
2875  case (char)WheelSide::RIGHT:
2876  case (char)WheelSide::LEFT:
2877  return WheelSide(c);
2878 
2879  default:
2880  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
2881  fmt::format("Bad arg~{} 'side' (value: {}), parsing as 'l' for backwards compatibility.", index + 1, c));
2882  return WheelSide::LEFT;
2883  }
2884 }
2885 
2886 long Parser::GetArgLong(int index)
2887 {
2888  errno = 0;
2889  char* out_end = nullptr;
2890  long res = std::strtol(m_args[index].start, &out_end, 10);
2891  if (errno != 0)
2892  {
2893  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
2894  fmt::format("Cannot parse argument [{}] as integer, errno: {}", index + 1, errno));
2895  return 0; // Compatibility
2896  }
2897  if (out_end == m_args[index].start)
2898  {
2899  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
2900  fmt::format("Argument [{}] is not valid integer", index + 1));
2901  return 0; // Compatibility
2902  }
2903  else if (out_end != (m_args[index].start + m_args[index].length))
2904  {;
2905  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
2906  fmt::format("Integer argument [{}] has invalid trailing characters", index + 1));
2907  }
2908  return res;
2909 }
2910 
2911 int Parser::GetArgInt(int index)
2912 {
2913  return static_cast<int>(this->GetArgLong(index));
2914 }
2915 
2916 Node::Ref Parser::GetArgRigidityNode(int index)
2917 {
2918  std::string rigidity_node = this->GetArgStr(index);
2919  if (rigidity_node != "9999") // Special null value
2920  {
2921  return this->GetArgNodeRef(index);
2922  }
2923  return Node::Ref(); // Defaults to invalid ref
2924 }
2925 
2926 WheelPropulsion Parser::GetArgPropulsion(int index)
2927 {
2928  int p = this->GetArgInt(index);
2929  switch (p)
2930  {
2931  case (int)WheelPropulsion::NONE:
2932  case (int)WheelPropulsion::FORWARD:
2933  case (int)WheelPropulsion::BACKWARD:
2934  return WheelPropulsion(p);
2935 
2936  default:
2937  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
2938  fmt::format("Bad value of param ~{} (propulsion), using 0 (no propulsion)", index + 1));
2939  return WheelPropulsion::NONE;
2940  }
2941 }
2942 
2943 WheelBraking Parser::GetArgBraking(int index)
2944 {
2945  int b = this->GetArgInt(index);
2946  switch (b)
2947  {
2948  case (int)WheelBraking::NONE:
2949  case (int)WheelBraking::FOOT_HAND:
2950  case (int)WheelBraking::FOOT_HAND_SKID_LEFT:
2951  case (int)WheelBraking::FOOT_HAND_SKID_RIGHT:
2952  case (int)WheelBraking::FOOT_ONLY:
2953  return WheelBraking(b);
2954 
2955  default:
2956  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
2957  fmt::format("Bad value of param ~{} (braking), using 0 (not braked)", index + 1));
2958  return WheelBraking::NONE;
2959  }
2960 }
2961 
2962 Node::Ref Parser::GetArgNodeRef(int index)
2963 {
2964  return this->_ParseNodeRef(this->GetArgStr(index));
2965 }
2966 
2967 Node::Ref Parser::GetArgNullableNode(int index)
2968 {
2969  if (! (Ogre::StringConverter::parseReal(this->GetArgStr(index)) == -1.f))
2970  {
2971  return this->GetArgNodeRef(index);
2972  }
2973  return Node::Ref(); // Defaults to empty ref.
2974 }
2975 
2976 unsigned Parser::GetArgUint(int index)
2977 {
2978  return static_cast<unsigned>(this->GetArgLong(index));
2979 }
2980 
2981 FlareType Parser::GetArgFlareType(int index)
2982 {
2983  char in = this->GetArgChar(index);
2984  switch (in)
2985  {
2986  // Front lights
2987  case (char)FlareType::HEADLIGHT:
2988  case (char)FlareType::HIGH_BEAM:
2989  case (char)FlareType::FOG_LIGHT:
2990  // Rear lighs
2991  case (char)FlareType::TAIL_LIGHT:
2992  case (char)FlareType::BRAKE_LIGHT:
2993  case (char)FlareType::REVERSE_LIGHT:
2994  // Special lights
2995  case (char)FlareType::SIDELIGHT:
2996  case (char)FlareType::BLINKER_LEFT:
2997  case (char)FlareType::BLINKER_RIGHT:
2998  case (char)FlareType::USER:
2999  case (char)FlareType::DASHBOARD:
3000  return FlareType(in);
3001 
3002  default:
3003  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3004  fmt::format("Invalid flare type '{}', falling back to type 'f' (front light)...", in));
3005  return FlareType::HEADLIGHT;
3006  }
3007 }
3008 
3009 ExtCameraMode Parser::GetArgExtCameraMode(int index)
3010 {
3011  std::string str = this->GetArgStr(index);
3012  if (str == "classic") return ExtCameraMode::CLASSIC;
3013  if (str == "cinecam") return ExtCameraMode::CINECAM;
3014  if (str == "node") return ExtCameraMode::NODE;
3015 
3016  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3017  fmt::format("Invalid ExtCameraMode '{}', falling back to type 'classic'...", str));
3018  return ExtCameraMode::CLASSIC;
3019 }
3020 
3021 float Parser::GetArgFloat(int index)
3022 {
3023  return (float) Ogre::StringConverter::parseReal(this->GetArgStr(index), 0.f);
3024 }
3025 
3026 float Parser::ParseArgFloat(const char* str)
3027 {
3028  return (float) Ogre::StringConverter::parseReal(str, 0.f);
3029 }
3030 
3031 float Parser::ParseArgFloat(std::string const & str)
3032 {
3033  return this->ParseArgFloat(str.c_str());
3034 }
3035 
3036 unsigned Parser::ParseArgUint(const char* str)
3037 {
3038  errno = 0;
3039  long res = std::strtol(str, nullptr, 10);
3040  if (errno != 0)
3041  {
3042  this->LogMessage(Console::CONSOLE_SYSTEM_ERROR,
3043  fmt::format("Cannot parse argument '{}' as int, errno: {}", str, errno));
3044  return 0.f; // Compatibility
3045  }
3046  return static_cast<unsigned>(res);
3047 }
3048 
3049 unsigned Parser::ParseArgUint(std::string const & str)
3050 {
3051  return this->ParseArgUint(str.c_str());
3052 }
3053 
3054 int Parser::ParseArgInt(const char* str)
3055 {
3056  return static_cast<int>(this->ParseArgUint(str));
3057 }
3058 
3059 bool Parser::GetArgBool(int index)
3060 {
3061  return Ogre::StringConverter::parseBool(this->GetArgStr(index));
3062 }
3063 
3064 WingControlSurface Parser::GetArgWingSurface(int index)
3065 {
3066  char c = this->GetArgChar(index);
3067  switch (c)
3068  {
3069  case (char)WingControlSurface::n_NONE:
3070  case (char)WingControlSurface::a_RIGHT_AILERON:
3071  case (char)WingControlSurface::b_LEFT_AILERON:
3072  case (char)WingControlSurface::f_FLAP:
3073  case (char)WingControlSurface::e_ELEVATOR:
3074  case (char)WingControlSurface::r_RUDDER:
3075  case (char)WingControlSurface::S_RIGHT_HAND_STABILATOR:
3076  case (char)WingControlSurface::T_LEFT_HAND_STABILATOR:
3077  case (char)WingControlSurface::c_RIGHT_ELEVON:
3078  case (char)WingControlSurface::d_LEFT_ELEVON:
3079  case (char)WingControlSurface::g_RIGHT_FLAPERON:
3080  case (char)WingControlSurface::h_LEFT_FLAPERON:
3081  case (char)WingControlSurface::U_RIGHT_HAND_TAILERON:
3082  case (char)WingControlSurface::V_LEFT_HAND_TAILERON:
3083  case (char)WingControlSurface::i_RIGHT_RUDDERVATOR:
3084  case (char)WingControlSurface::j_LEFT_RUDDERVATOR:
3085  return WingControlSurface(c);
3086 
3087  default:
3088  fmt::format("invalid WingControlSurface '{}', falling back to 'n' (none)", c);
3089  return WingControlSurface::n_NONE;
3090  }
3091 }
3092 
3093 std::string Parser::GetArgManagedTex(int index)
3094 {
3095  std::string tex_name = this->GetArgStr(index);
3096  return (tex_name.at(0) != '-') ? tex_name : "";
3097 }
3098 
3099 MinimassOption Parser::GetArgMinimassOption(int index)
3100 {
3101  switch (this->GetArgStr(index)[0])
3102  {
3103  case (char)MinimassOption::l_SKIP_LOADED:
3104  return MinimassOption::l_SKIP_LOADED;
3105 
3106  case (char)MinimassOption::n_DUMMY:
3107  return MinimassOption::n_DUMMY;
3108 
3109  default:
3110  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3111  fmt::format("Not a valid minimass option: {}, falling back to 'n' (dummy)", this->GetArgStr(index)));
3112  return MinimassOption::n_DUMMY;
3113  }
3114 }
3115 
3116 BitMask_t Parser::GetArgCabOptions(int index)
3117 {
3118  BitMask_t ret = 0;
3119  for (char c: this->GetArgStr(index))
3120  {
3121  switch (c)
3122  {
3123  case (char)CabOption::c_CONTACT: ret |= Cab::OPTION_c_CONTACT ; break;
3124  case (char)CabOption::b_BUOYANT: ret |= Cab::OPTION_b_BUOYANT ; break;
3125  case (char)CabOption::p_10xTOUGHER: ret |= Cab::OPTION_p_10xTOUGHER ; break;
3126  case (char)CabOption::u_INVULNERABLE: ret |= Cab::OPTION_u_INVULNERABLE ; break;
3127  case (char)CabOption::s_BUOYANT_NO_DRAG: ret |= Cab::OPTION_s_BUOYANT_NO_DRAG ; break;
3128  case (char)CabOption::r_BUOYANT_ONLY_DRAG: ret |= Cab::OPTION_r_BUOYANT_ONLY_DRAG ; break;
3129  case (char)CabOption::D_CONTACT_BUOYANT: ret |= Cab::OPTION_D_CONTACT_BUOYANT ; break;
3130  case (char)CabOption::F_10xTOUGHER_BUOYANT: ret |= Cab::OPTION_F_10xTOUGHER_BUOYANT ; break;
3131  case (char)CabOption::S_INVULNERABLE_BUOYANT: ret |= Cab::OPTION_S_INVULNERABLE_BUOYANT; break;
3132 
3133  default:
3134  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3135  fmt::format("ignoring invalid flag '{}'", c));
3136  }
3137  }
3138  return ret;
3139 }
3140 
3141 BitMask_t Parser::GetArgTriggerOptions(int index)
3142 {
3143  BitMask_t ret = 0;
3144  for (char c: this->GetArgStr(index))
3145  {
3146  switch(c)
3147  {
3148  case (char)TriggerOption::i_INVISIBLE : ret |= Trigger::OPTION_i_INVISIBLE; break;
3149  case (char)TriggerOption::c_COMMAND_STYLE : ret |= Trigger::OPTION_c_COMMAND_STYLE; break;
3150  case (char)TriggerOption::x_START_DISABLED : ret |= Trigger::OPTION_x_START_DISABLED; break;
3151  case (char)TriggerOption::b_KEY_BLOCKER : ret |= Trigger::OPTION_b_KEY_BLOCKER; break;
3152  case (char)TriggerOption::B_TRIGGER_BLOCKER : ret |= Trigger::OPTION_B_TRIGGER_BLOCKER; break;
3153  case (char)TriggerOption::A_INV_TRIGGER_BLOCKER: ret |= Trigger::OPTION_A_INV_TRIGGER_BLOCKER; break;
3154  case (char)TriggerOption::s_CMD_NUM_SWITCH : ret |= Trigger::OPTION_s_CMD_NUM_SWITCH; break;
3155  case (char)TriggerOption::h_UNLOCKS_HOOK_GROUP : ret |= Trigger::OPTION_h_UNLOCKS_HOOK_GROUP; break;
3156  case (char)TriggerOption::H_LOCKS_HOOK_GROUP : ret |= Trigger::OPTION_H_LOCKS_HOOK_GROUP; break;
3157  case (char)TriggerOption::t_CONTINUOUS : ret |= Trigger::OPTION_t_CONTINUOUS; break;
3158  case (char)TriggerOption::E_ENGINE_TRIGGER : ret |= Trigger::OPTION_E_ENGINE_TRIGGER; break;
3159 
3160  default:
3161  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3162  fmt::format("ignoring invalid option '{}'", c));
3163  }
3164  }
3165  return ret;
3166 }
3167 
3168 BitMask_t Parser::GetArgBeamOptions(int index)
3169 {
3170  BitMask_t ret = 0;
3171  for (char c: this->GetArgStr(index))
3172  {
3173  switch (c)
3174  {
3175  case (char)BeamOption::i_INVISIBLE: ret |= Beam::OPTION_i_INVISIBLE; break;
3176  case (char)BeamOption::r_ROPE : ret |= Beam::OPTION_r_ROPE ; break;
3177  case (char)BeamOption::s_SUPPORT : ret |= Beam::OPTION_s_SUPPORT ; break;
3178 
3179  case (char)BeamOption::v_DUMMY: break;
3180 
3181  default:
3182  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3183  fmt::format("ignoring invalid option '{}'", c));
3184  }
3185  }
3186  return ret;
3187 }
3188 
3189 BitMask_t Parser::GetArgHydroOptions (int index)
3190 {
3191  BitMask_t ret = 0;
3192  for (char c: this->GetArgStr(index))
3193  {
3194  switch (c)
3195  {
3196  case (char)HydroOption::j_INVISIBLE : ret |= Hydro::OPTION_j_INVISIBLE ; break;
3197  case (char)HydroOption::s_DISABLE_ON_HIGH_SPEED : ret |= Hydro::OPTION_s_DISABLE_ON_HIGH_SPEED ; break;
3198  case (char)HydroOption::a_INPUT_AILERON : ret |= Hydro::OPTION_a_INPUT_AILERON ; break;
3199  case (char)HydroOption::r_INPUT_RUDDER : ret |= Hydro::OPTION_r_INPUT_RUDDER ; break;
3200  case (char)HydroOption::e_INPUT_ELEVATOR : ret |= Hydro::OPTION_e_INPUT_ELEVATOR ; break;
3201  case (char)HydroOption::u_INPUT_AILERON_ELEVATOR : ret |= Hydro::OPTION_u_INPUT_AILERON_ELEVATOR ; break;
3202  case (char)HydroOption::v_INPUT_InvAILERON_ELEVATOR: ret |= Hydro::OPTION_v_INPUT_InvAILERON_ELEVATOR; break;
3203  case (char)HydroOption::x_INPUT_AILERON_RUDDER : ret |= Hydro::OPTION_x_INPUT_AILERON_RUDDER ; break;
3204  case (char)HydroOption::y_INPUT_InvAILERON_RUDDER : ret |= Hydro::OPTION_y_INPUT_InvAILERON_RUDDER ; break;
3205  case (char)HydroOption::g_INPUT_ELEVATOR_RUDDER : ret |= Hydro::OPTION_g_INPUT_ELEVATOR_RUDDER ; break;
3206  case (char)HydroOption::h_INPUT_InvELEVATOR_RUDDER : ret |= Hydro::OPTION_h_INPUT_InvELEVATOR_RUDDER ; break;
3207  case (char)HydroOption::n_INPUT_NORMAL : ret |= Hydro::OPTION_n_INPUT_NORMAL ; break;
3208 
3209  case (char)HydroOption::i_INVISIBLE_INPUT_NORMAL:
3210  if (ret == 0)
3211  {
3212  // Original intent: when using 'i' flag alone, also force 'n' (steering input).
3213  // For backward compatibility, do it every time 'i' comes first, even if not alone.
3214  ret |= Hydro::OPTION_n_INPUT_NORMAL;
3215  }
3216  ret |= Hydro::OPTION_j_INVISIBLE;
3217  break;
3218 
3219  default:
3220  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3221  fmt::format("ignoring invalid option '{}'", c));
3222  }
3223  }
3224 
3225  return ret;
3226 }
3227 
3228 BitMask_t Parser::GetArgShockOptions(int index)
3229 {
3230  BitMask_t ret = 0;
3231  for (char c: this->GetArgStr(index))
3232  {
3233  switch (c)
3234  {
3235  case (char)ShockOption::i_INVISIBLE : ret |= Shock::OPTION_i_INVISIBLE ; break;
3236  case (char)ShockOption::L_ACTIVE_LEFT : ret |= Shock::OPTION_L_ACTIVE_LEFT ; break;
3237  case (char)ShockOption::R_ACTIVE_RIGHT : ret |= Shock::OPTION_R_ACTIVE_RIGHT; break;
3238  case (char)ShockOption::m_METRIC : ret |= Shock::OPTION_m_METRIC ; break;
3239 
3240  case (char)ShockOption::n_DUMMY: break;
3241  case (char)ShockOption::v_DUMMY: break;
3242 
3243  default:
3244  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3245  fmt::format("ignoring invalid option '{}'", c));
3246  }
3247  }
3248  return ret;
3249 }
3250 
3251 BitMask_t Parser::GetArgShock2Options(int index)
3252 {
3253  BitMask_t ret = 0;
3254  for (char c: this->GetArgStr(index))
3255  {
3256  switch (c)
3257  {
3258  case (char)Shock2Option::i_INVISIBLE: ret |= Shock2::OPTION_i_INVISIBLE; break;
3259  case (char)Shock2Option::s_SOFT_BUMP_BOUNDS: ret |= Shock2::OPTION_s_SOFT_BUMP_BOUNDS; break;
3260  case (char)Shock2Option::m_METRIC: ret |= Shock2::OPTION_m_METRIC; break;
3261  case (char)Shock2Option::M_ABSOLUTE_METRIC: ret |= Shock2::OPTION_M_ABSOLUTE_METRIC; break;
3262 
3263  case (char)Shock2Option::n_DUMMY: break;
3264  case (char)Shock2Option::v_DUMMY: break;
3265 
3266  default:
3267  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3268  fmt::format("ignoring invalid option '{}'", c));
3269  }
3270  }
3271  return ret;
3272 }
3273 
3274 BitMask_t Parser::GetArgShock3Options(int index)
3275 {
3276  BitMask_t ret = 0;
3277  for (char c: this->GetArgStr(index))
3278  {
3279  switch (c)
3280  {
3281  case (char)Shock3Option::i_INVISIBLE: ret |= Shock3::OPTION_i_INVISIBLE; break;
3282  case (char)Shock3Option::m_METRIC: ret |= Shock3::OPTION_m_METRIC; break;
3283  case (char)Shock3Option::M_ABSOLUTE_METRIC: ret |= Shock3::OPTION_M_ABSOLUTE_METRIC; break;
3284 
3285  case (char)Shock3Option::n_DUMMY: break;
3286  case (char)Shock3Option::v_DUMMY: break;
3287 
3288  default:
3289  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3290  fmt::format("ignoring invalid option '{}'", c));
3291  }
3292  }
3293  return ret;
3294 }
3295 
3296 BitMask_t Parser::GetArgNodeOptions(int index)
3297 {
3298  BitMask_t ret = 0;
3299  for (char c: this->GetArgStr(index))
3300  {
3301  switch (c)
3302  {
3303  case (char)NodeOption::m_NO_MOUSE_GRAB : ret |= Node::OPTION_m_NO_MOUSE_GRAB ; break;
3304  case (char)NodeOption::f_NO_SPARKS : ret |= Node::OPTION_f_NO_SPARKS ; break;
3305  case (char)NodeOption::x_EXHAUST_POINT : ret |= Node::OPTION_x_EXHAUST_POINT ; break;
3306  case (char)NodeOption::y_EXHAUST_DIRECTION : ret |= Node::OPTION_y_EXHAUST_DIRECTION ; break;
3307  case (char)NodeOption::c_NO_GROUND_CONTACT : ret |= Node::OPTION_c_NO_GROUND_CONTACT ; break;
3308  case (char)NodeOption::h_HOOK_POINT : ret |= Node::OPTION_h_HOOK_POINT ; break;
3309  case (char)NodeOption::e_TERRAIN_EDIT_POINT: ret |= Node::OPTION_e_TERRAIN_EDIT_POINT; break;
3310  case (char)NodeOption::b_EXTRA_BUOYANCY : ret |= Node::OPTION_b_EXTRA_BUOYANCY ; break;
3311  case (char)NodeOption::p_NO_PARTICLES : ret |= Node::OPTION_p_NO_PARTICLES ; break;
3312  case (char)NodeOption::L_LOG : ret |= Node::OPTION_L_LOG ; break;
3313  case (char)NodeOption::l_LOAD_WEIGHT : ret |= Node::OPTION_l_LOAD_WEIGHT ; break;
3314 
3315  case (char)NodeOption::n_DUMMY: break;
3316 
3317  default:
3318  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3319  fmt::format("ignoring invalid option '{}'", c));
3320  }
3321  }
3322  return ret;
3323 }
3324 
3325 SpecialProp Parser::IdentifySpecialProp(const std::string& str)
3326 {
3327  if (str.find("leftmirror" ) != std::string::npos) { return SpecialProp::MIRROR_LEFT; }
3328  if (str.find("rightmirror" ) != std::string::npos) { return SpecialProp::MIRROR_RIGHT; }
3329  if (str.find("dashboard-rh") != std::string::npos) { return SpecialProp::DASHBOARD_RIGHT; }
3330  if (str.find("dashboard" ) != std::string::npos) { return SpecialProp::DASHBOARD_LEFT; }
3331  if (Ogre::StringUtil::startsWith(str, "spinprop", false) ) { return SpecialProp::AERO_PROP_SPIN; }
3332  if (Ogre::StringUtil::startsWith(str, "pale", false) ) { return SpecialProp::AERO_PROP_BLADE; }
3333  if (Ogre::StringUtil::startsWith(str, "seat", false) ) { return SpecialProp::DRIVER_SEAT; }
3334  if (Ogre::StringUtil::startsWith(str, "seat2", false) ) { return SpecialProp::DRIVER_SEAT_2; }
3335  if (Ogre::StringUtil::startsWith(str, "beacon", false) ) { return SpecialProp::BEACON; }
3336  if (Ogre::StringUtil::startsWith(str, "redbeacon", false)) { return SpecialProp::REDBEACON; }
3337  if (Ogre::StringUtil::startsWith(str, "lightb", false) ) { return SpecialProp::LIGHTBAR; }
3338  return SpecialProp::NONE;
3339 }
3340 
3341 EngineType Parser::GetArgEngineType(int index)
3342 {
3343  char c = this->GetArgChar(index);
3344  switch (c)
3345  {
3346  case (char)EngineType::t_TRUCK:
3347  case (char)EngineType::c_CAR:
3348  case (char)EngineType::e_ECAR:
3349  return EngineType(c);
3350 
3351  default:
3352  fmt::format("invalid EngineType '{}', falling back to 't' (truck)", c);
3353  return EngineType::t_TRUCK;
3354  }
3355 }
3356 
3357 ManagedMaterialType Parser::GetArgManagedMatType(int index)
3358 {
3359  std::string str = this->GetArgStr(index);
3360  if (str == "mesh_standard") return ManagedMaterialType::MESH_STANDARD;
3361  if (str == "mesh_transparent") return ManagedMaterialType::MESH_TRANSPARENT;
3362  if (str == "flexmesh_standard") return ManagedMaterialType::FLEXMESH_STANDARD;
3363  if (str == "flexmesh_transparent") return ManagedMaterialType::FLEXMESH_TRANSPARENT;
3364 
3365  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3366  fmt::format("Not a valid ManagedMaterialType: '{}'", str));
3367  return ManagedMaterialType::INVALID;
3368 }
3369 
3370 VideoCamRole Parser::GetArgVideoCamRole(int index)
3371 {
3372  int role = this->GetArgInt(index);
3373  switch (role)
3374  {
3377  case VCAM_ROLE_MIRROR: return VCAM_ROLE_MIRROR;
3379  default:
3380  this->LogMessage(Console::CONSOLE_SYSTEM_WARNING,
3381  fmt::format("Invalid value of 'camera role' ({}), videocamera will not work", role));
3382  return VCAM_ROLE_INVALID;
3383  }
3384 }
3385 
3386 int Parser::TokenizeCurrentLine()
3387 {
3388  int cur_arg = 0;
3389  const char* cur_char = m_current_line;
3390  int arg_len = 0;
3391  while ((*cur_char != '\0') && (cur_arg < Parser::LINE_MAX_ARGS))
3392  {
3393  const bool is_arg = !IsSeparator(*cur_char);
3394  if ((arg_len == 0) && is_arg)
3395  {
3396  m_args[cur_arg].start = cur_char;
3397  arg_len = 1;
3398  }
3399  else if ((arg_len > 0) && !is_arg)
3400  {
3401  m_args[cur_arg].length = arg_len;
3402  arg_len = 0;
3403  ++cur_arg;
3404  }
3405  else if (is_arg)
3406  {
3407  ++arg_len;
3408  }
3409  ++cur_char;
3410  }
3411  if (arg_len > 0)
3412  {
3413  m_args[cur_arg].length = arg_len;
3414  ++cur_arg;
3415  }
3416 
3417  m_num_args = cur_arg;
3418  return cur_arg;
3419 }
3420 
3421 void Parser::ProcessOgreStream(Ogre::DataStream* stream, Ogre::String resource_group)
3422 {
3423  m_resource_group = resource_group;
3424  m_filename = stream->getName();
3425 
3426  char raw_line_buf[LINE_BUFFER_LENGTH];
3427  while (!stream->eof())
3428  {
3429  try
3430  {
3431  stream->readLine(raw_line_buf, LINE_BUFFER_LENGTH);
3432  }
3433  catch (Ogre::Exception &ex)
3434  {
3436  fmt::format("Could not read truck file: {}", ex.getFullDescription()));
3437  break;
3438  }
3439 
3440  this->ProcessRawLine(raw_line_buf);
3441  }
3442 }
3443 
3444 void Parser::ProcessRawLine(const char* raw_line_buf)
3445 {
3446  const char* raw_start = raw_line_buf;
3447  const char* raw_end = raw_line_buf + strnlen(raw_line_buf, LINE_BUFFER_LENGTH);
3448 
3449  // Trim leading whitespace
3450  while (IsWhitespace(*raw_start) && (raw_start != raw_end))
3451  {
3452  ++raw_start;
3453  }
3454 
3455  // Skip empty/comment lines
3456  if ((raw_start == raw_end) || (*raw_start == ';') || (*raw_start == '/'))
3457  {
3458  ++m_current_line_number;
3459  return;
3460  }
3461 
3462  // Sanitize UTF-8
3463  memset(m_current_line, 0, LINE_BUFFER_LENGTH);
3464  char* out_start = m_current_line;
3465  utf8::replace_invalid(raw_start, raw_end, out_start, '?');
3466 
3467  // Process
3468  this->ProcessCurrentLine();
3469  ++m_current_line_number;
3470 }
3471 
3472 } // namespace RigDef
RigDef::Command2::contract_key
unsigned int contract_key
Definition: RigDef_File.h:771
RigDef::Command2::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:778
RigDef::InterAxle::options
DifferentialTypeVec options
Order matters!
Definition: RigDef_File.h:1010
RigDef::Engine::shift_up_rpm
float shift_up_rpm
Definition: RigDef_File.h:809
RigDef::Command2::extend_key
unsigned int extend_key
Definition: RigDef_File.h:772
RigDef::Shock2::spring_out
float spring_out
spring value applied when shock extending
Definition: RigDef_File.h:1219
RigDef::FlexBodyWheel::rim_springiness
float rim_springiness
Definition: RigDef_File.h:919
RigDef::Shock3::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1254
RigDef::Airbrake::texcoord_x1
float texcoord_x1
Definition: RigDef_File.h:470
RigDef::SlideNode::constraint_flags
BitMask_t constraint_flags
Definition: RigDef_File.h:1273
RigDef::Engoption::min_idle_mixture
float min_idle_mixture
Definition: RigDef_File.h:828
RigDef::Wing::tex_coords
float tex_coords[8]
Definition: RigDef_File.h:1470
RigDef::MinimassOption
MinimassOption
Definition: RigDef_File.h:236
RigDef::MaterialFlareBinding::material_name
Ogre::String material_name
Definition: RigDef_File.h:1041
RigDef::Shock3::damp_out_fast
float damp_out_fast
Damping value applied when shock is commpressing faster than split out velocity.
Definition: RigDef_File.h:1249
RigDef::Prop::DashboardSpecial::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:1096
RigDef::Animation::MotorSource
Definition: RigDef_File.h:479
RigDef::Beam::extension_break_limit
float extension_break_limit
Definition: RigDef_File.h:635
RigDef::VideoCamera::left_node
Node::Ref left_node
Definition: RigDef_File.h:1427
RigDef::Turboprop2::blade_tip_nodes
Node::Ref blade_tip_nodes[4]
Definition: RigDef_File.h:1418
RigDef::BaseWheel2::rim_radius
float rim_radius
Definition: RigDef_File.h:436
RigDef::Wheel
Definition: RigDef_File.h:1444
RigDef::SlideNode::_railgroup_id_set
bool _railgroup_id_set
Definition: RigDef_File.h:1280
RigDef::GuiSettings
Definition: RigDef_File.h:949
RigDef::SlideNode::tolerance
float tolerance
Definition: RigDef_File.h:1278
RigDef::Turboprop2::axis_node
Node::Ref axis_node
Definition: RigDef_File.h:1417
BEAM_SKELETON_DIAMETER
static const float BEAM_SKELETON_DIAMETER
Definition: SimConstants.h:57
RoR::FlareType::REVERSE_LIGHT
@ REVERSE_LIGHT
RigDef::Rope::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1144
RigDef::ExtCamera::node
Node::Ref node
Definition: RigDef_File.h:862
RigDef::InterAxle::a2
int a2
Definition: RigDef_File.h:1009
RigDef::Node::Ref
Legacy parser resolved references on-the-fly and the condition to check named nodes was "are there an...
Definition: RigDef_Node.h:77
RigDef::Command2::inertia_defaults
std::shared_ptr< Inertia > inertia_defaults
Definition: RigDef_File.h:779
RigDef::Hydro::inertia
Inertia inertia
Definition: RigDef_File.h:1000
RigDef::Command2::option_f_not_faster
bool option_f_not_faster
Definition: RigDef_File.h:785
RigDef::Prop
Definition: RigDef_File.h:1085
RigDef::WheelDetacher::wheel_id
int wheel_id
Definition: RigDef_File.h:1463
Keyword
Keyword
Definition: Bench_TruckParser_IdentifyKeyword.cpp:6
RoR::VCAM_ROLE_TRACKING_VIDEOCAM
@ VCAM_ROLE_TRACKING_VIDEOCAM
Definition: Application.h:378
RigDef::BeamDefaultsScale::breaking_threshold_constant
float breaking_threshold_constant
Definition: RigDef_File.h:653
RigDef::Tie::group
int group
Definition: RigDef_File.h:1334
RigDef::TractionControl::fade_speed
float fade_speed
Definition: RigDef_File.h:1360
RigDef::Hydro::inertia_defaults
std::shared_ptr< Inertia > inertia_defaults
Definition: RigDef_File.h:1001
RigDef::Prop::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:1116
RigDef::Camera::left_node
Node::Ref left_node
Definition: RigDef_File.h:730
RigDef::Engine::neutral_gear_ratio
float neutral_gear_ratio
Definition: RigDef_File.h:813
RigDef::Brakes::default_braking_force
float default_braking_force
Definition: RigDef_File.h:706
RigDef::Command2::option_o_1press_center
bool option_o_1press_center
Definition: RigDef_File.h:787
RigDef::SpecialProp
SpecialProp
Definition: RigDef_File.h:191
RigDef::Cinecam::position
Ogre::Vector3 position
Definition: RigDef_File.h:745
RoR::FlareType::BLINKER_LEFT
@ BLINKER_LEFT
RigDef::Cinecam::node_defaults
std::shared_ptr< NodeDefaults > node_defaults
Definition: RigDef_File.h:751
RigDef::FlaregroupNoImport::control_number
int control_number
Only 'u' type flares.
Definition: RigDef_File.h:899
RigDef::VideoCamera::min_clip_distance
float min_clip_distance
Definition: RigDef_File.h:1436
RigDef::Animation::lower_limit
float lower_limit
Definition: RigDef_File.h:546
RigDef::Wing::control_surface
WingControlSurface control_surface
Definition: RigDef_File.h:1471
RigDef::Rotator::needs_engine
bool needs_engine
Definition: RigDef_File.h:1159
RigDef::CollisionRange
Definition: RigDef_File.h:759
RigDef::ManagedMaterial::diffuse_map
Ogre::String diffuse_map
Definition: RigDef_File.h:1033
RigDef::Hook::option_hookgroup
int option_hookgroup
Definition: RigDef_File.h:968
RigDef::TractionControl::attr_no_dashboard
bool attr_no_dashboard
Definition: RigDef_File.h:1363
RigDef::Animation::MotorSource::motor
unsigned int motor
Definition: RigDef_File.h:491
RigDef::Inertia::start_function
Ogre::String start_function
Definition: RigDef_File.h:451
RigDef::IsSeparator
bool IsSeparator(char c)
Definition: RigDef_Parser.cpp:54
RigDef::Engturbo
Definition: RigDef_File.h:832
RigDef::VideoCamera::texture_width
unsigned int texture_width
Definition: RigDef_File.h:1434
RigDef::Flare2::type
RoR::FlareType type
Definition: RigDef_File.h:883
RigDef::Animator::long_limit
float long_limit
Definition: RigDef_File.h:593
RigDef::Node::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_Node.h:165
RoR::VCAM_ROLE_INVALID
@ VCAM_ROLE_INVALID
Definition: Application.h:389
RoR::FlareType::FOG_LIGHT
@ FOG_LIGHT
RigDef::Author
Definition: RigDef_File.h:612
RigDef::Engoption::post_shift_time
float post_shift_time
Seconds.
Definition: RigDef_File.h:824
RigDef::Shock::long_bound
float long_bound
Maximum extension. The longest length a shock can be, as a proportion of its original length....
Definition: RigDef_File.h:1198
RigDef::SoundSource2::mode
int mode
A special constant or cinecam index.
Definition: RigDef_File.h:1295
RigDef::Airbrake::texcoord_y1
float texcoord_y1
Definition: RigDef_File.h:472
RigDef::Engoption::shift_time
float shift_time
Seconds.
Definition: RigDef_File.h:822
RigDef::Animation
Definition: RigDef_File.h:477
BEAM_DEFORM
static const float BEAM_DEFORM
Definition: SimConstants.h:61
RigDef::AntiLockBrakes::attr_no_dashboard
bool attr_no_dashboard
Definition: RigDef_File.h:608
RigDef::Command2::max_extension
float max_extension
Definition: RigDef_File.h:770
RigDef::Animator::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:589
RigDef::Engine::gear_ratios
std::vector< float > gear_ratios
Definition: RigDef_File.h:814
RigDef::Tie::auto_shorten_rate
float auto_shorten_rate
Definition: RigDef_File.h:1327
RigDef::MaterialFlareBinding::flare_number
unsigned int flare_number
Definition: RigDef_File.h:1040
RigDef::Tie::min_length
float min_length
Definition: RigDef_File.h:1328
RigDef::Node::Id::SetNum
void SetNum(unsigned int id_num)
Definition: RigDef_Node.cpp:55
RigDef::Turboprop2::airfoil
Ogre::String airfoil
Definition: RigDef_File.h:1420
RigDef::Flare3
Definition: RigDef_File.h:891
RigDef::Prop::DashboardSpecial::mesh_name
Ogre::String mesh_name
Definition: RigDef_File.h:1099
RigDef::FlaregroupNoImport::type
RoR::FlareType type
Definition: RigDef_File.h:898
RigDef::SkeletonSettings
Definition: RigDef_File.h:1258
RigDef::SpeedLimiter::is_enabled
bool is_enabled
Definition: RigDef_File.h:1301
RigDef::Engine::global_gear_ratio
float global_gear_ratio
Definition: RigDef_File.h:811
RigDef::Texcoord::node
Node::Ref node
Definition: RigDef_File.h:1315
RigDef::RailGroup
Definition: RigDef_File.h:1126
RigDef::Shock3::split_vel_out
float split_vel_out
Split velocity in (m/s) - threshold for slow / fast damping during extension.
Definition: RigDef_File.h:1248
RigDef::VideoCamera::camera_mode
int camera_mode
Definition: RigDef_File.h:1439
RigDef::Hook::flag_auto_lock
bool flag_auto_lock
Definition: RigDef_File.h:973
RigDef::Engturbo::param3
float param3
Definition: RigDef_File.h:841
RigDef::Assetpack::filename
std::string filename
Definition: RigDef_File.h:406
RigDef::Fusedrag::approximate_width
float approximate_width
Definition: RigDef_File.h:932
RigDef::Command2::detacher_group
int detacher_group
Definition: RigDef_File.h:780
RigDef::Prop::rotation
Ogre::Vector3 rotation
Definition: RigDef_File.h:1117
RigDef::Fusedrag::autocalc
bool autocalc
Definition: RigDef_File.h:929
RigDef::Engturbo::version
int version
Definition: RigDef_File.h:836
RigDef::ManagedMaterial::damaged_diffuse_map
Ogre::String damaged_diffuse_map
Definition: RigDef_File.h:1034
RigDef::Engoption::type
EngineType type
Definition: RigDef_File.h:820
RigDef::Rotator::rate
float rate
Definition: RigDef_File.h:1153
RigDef::Wheel::radius
float radius
Definition: RigDef_File.h:1446
format
Truck file format(technical spec)
RigDef::BeamDefaults::deformation_threshold
float deformation_threshold
Definition: RigDef_File.h:693
RigDef::Screwprop::back_node
Node::Ref back_node
Definition: RigDef_File.h:1172
RigDef::Particle::particle_system_name
Ogre::String particle_system_name
Definition: RigDef_File.h:1071
RigDef::Camera
Definition: RigDef_File.h:726
RigDef::Hydro
Definition: RigDef_File.h:979
RigDef::Shock::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1201
RigDef::CollisionBox
Definition: RigDef_File.h:754
RigDef::SlideNode::break_force
float break_force
Definition: RigDef_File.h:1277
RigDef::Command2::shorten_rate
float shorten_rate
Definition: RigDef_File.h:767
RigDef::Ropable
Definition: RigDef_File.h:1132
RigDef::Trigger::longbound_trigger_action
int longbound_trigger_action
Definition: RigDef_File.h:1398
RigDef::SlideNode::attachment_rate
float attachment_rate
Definition: RigDef_File.h:1279
RigDef::BaseMeshWheel::side
RoR::WheelSide side
Definition: RigDef_File.h:425
RigDef::SoundSource::node
Node::Ref node
Definition: RigDef_File.h:1286
RigDef::Wing::chord_point
float chord_point
Definition: RigDef_File.h:1472
RigDef::Shock2::spring_in
float spring_in
Spring value applied when the shock is compressing.
Definition: RigDef_File.h:1215
RigDef::BaseWheel::width
float width
Definition: RigDef_File.h:411
RigDef::Flexbody::rotation
Ogre::Vector3 rotation
Definition: RigDef_File.h:908
RigDef::Airbrake
Definition: RigDef_File.h:458
SimConstants.h
RigDef::KeywordToString
const char * KeywordToString(Keyword keyword)
Definition: RigDef_File.cpp:162
RigDef::Flexbody::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:907
RigDef::Tie::options
BitMask_t options
Definition: RigDef_File.h:1330
RoR::FlareType::USER
@ USER
RigDef::Flare2
Definition: RigDef_File.h:877
RigDef::Help
Definition: RigDef_File.h:955
RigDef::DifferentialType
DifferentialType
Definition: RigDef_File.h:226
RigDef::SlideNode::rail_node_ranges
std::vector< Node::Range > rail_node_ranges
Definition: RigDef_File.h:1272
RigDef::GuiSettings::key
std::string key
Definition: RigDef_File.h:951
RoR::FlareType::SIDELIGHT
@ SIDELIGHT
RigDef::Cinecam::spring
float spring
Definition: RigDef_File.h:747
RigDef::Trigger
Definition: RigDef_File.h:1376
RigDef::CollisionBox::nodes
std::vector< Node::Ref > nodes
Definition: RigDef_File.h:756
RigDef::Shock3::precompression
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
Definition: RigDef_File.h:1252
RigDef::Engturbo::param8
float param8
Definition: RigDef_File.h:846
RigDef::BeamDefaults::springiness
float springiness
Definition: RigDef_File.h:691
Console.h
RoR::FlareType::HEADLIGHT
@ HEADLIGHT
RigDef::Command2::description
Ogre::String description
Definition: RigDef_File.h:773
RoR::Console::putMessage
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition: Console.cpp:103
RoR::FlareType::BLINKER_RIGHT
@ BLINKER_RIGHT
DEFAULT_BEAM_DIAMETER
static const float DEFAULT_BEAM_DIAMETER
5 centimeters default beam width
Definition: SimConstants.h:52
RigDef::Flare2::node_axis_y
Node::Ref node_axis_y
Definition: RigDef_File.h:881
RigDef::Pistonprop
Definition: RigDef_File.h:1074
RoR::VCAM_ROLE_VIDEOCAM
@ VCAM_ROLE_VIDEOCAM
Definition: Application.h:377
RigDef::Trigger::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:1390
RigDef::Axle::wheels
Node::Ref wheels[2][2]
Definition: RigDef_File.h:623
RigDef::Rotator::spin_right_key
unsigned int spin_right_key
Definition: RigDef_File.h:1155
RigDef::Screwprop::power
float power
Definition: RigDef_File.h:1174
RigDef::BeamDefaults
Definition: RigDef_File.h:656
RigDef::Flare2::blink_delay_milis
int blink_delay_milis
Definition: RigDef_File.h:886
RigDef::Shock::detacher_group
int detacher_group
Definition: RigDef_File.h:1202
RigDef::FlaregroupNoImport
Definition: RigDef_File.h:896
RoR::FlareType
FlareType
Definition: SimData.h:228
RigDef::VideoCamera::rotation
Ogre::Vector3 rotation
Definition: RigDef_File.h:1432
RigDef::Airbrake::y_axis_node
Node::Ref y_axis_node
Definition: RigDef_File.h:464
RigDef::FlexBodyWheel
Definition: RigDef_File.h:916
RigDef::Turboprop2::turbine_power_kW
float turbine_power_kW
Definition: RigDef_File.h:1419
RigDef::SlideNode::_spring_rate_set
bool _spring_rate_set
Definition: RigDef_File.h:1276
RigDef::Tie::detacher_group
int detacher_group
Definition: RigDef_File.h:1333
RigDef::Engturbo::param6
float param6
Definition: RigDef_File.h:844
RigDef::Particle
Definition: RigDef_File.h:1067
RigDef::Engine::shift_down_rpm
float shift_down_rpm
Definition: RigDef_File.h:808
RigDef::Texcoord::v
float v
Definition: RigDef_File.h:1317
RigDef::Node::Range
Definition: RigDef_Node.h:114
RigDef::CameraRail
Definition: RigDef_File.h:733
RigDef::Animator::detacher_group
int detacher_group
Definition: RigDef_File.h:597
RigDef::Engoption::braking_torque
float braking_torque
Definition: RigDef_File.h:829
RigDef::SkeletonSettings::beam_thickness_meters
float beam_thickness_meters
Definition: RigDef_File.h:1261
Utils.h
RigDef::Flexbody::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:904
RigDef::WheelPropulsion
WheelPropulsion
Definition: RigDef_File.h:251
RigDef::BeamDefaults::breaking_threshold
float breaking_threshold
Definition: RigDef_File.h:694
RigDef::ManagedMaterialType
ManagedMaterialType
Definition: RigDef_File.h:207
RoR::FlareType::TAIL_LIGHT
@ TAIL_LIGHT
RigDef::Animator::short_limit
float short_limit
Definition: RigDef_File.h:592
RigDef::SlideNode::_tolerance_set
bool _tolerance_set
Definition: RigDef_File.h:1278
RigDef::Wheel::damping
float damping
Definition: RigDef_File.h:1448
RigDef::Shock2::long_bound
float long_bound
Maximum extension limit, in percentage ( 1.00 = 100% )
Definition: RigDef_File.h:1224
RigDef::WingControlSurface
WingControlSurface
Definition: RigDef_File.h:258
RigDef::Node::Ref::IsValidAnyState
bool IsValidAnyState() const
Definition: RigDef_Node.h:101
RigDef::BeamDefaults::_is_plastic_deform_coef_user_defined
bool _is_plastic_deform_coef_user_defined
Definition: RigDef_File.h:699
RigDef::Rotator::axis_nodes
Node::Ref axis_nodes[2]
Definition: RigDef_File.h:1150
RigDef::Trigger::options
BitMask_t options
Definition: RigDef_File.h:1393
RigDef::NodeDefaults
Definition: RigDef_File.h:1056
RigDef::Hook
Definition: RigDef_File.h:960
RigDef::Hydro::options
BitMask_t options
Definition: RigDef_File.h:999
RigDef::Pistonprop::airfoil
Ogre::String airfoil
Definition: RigDef_File.h:1082
RoR::Console::CONSOLE_SYSTEM_ERROR
@ CONSOLE_SYSTEM_ERROR
Definition: Console.h:52
RigDef::AntiLockBrakes
Definition: RigDef_File.h:600
RigDef::InterAxle::a1
int a1
Definition: RigDef_File.h:1008
RigDef::Tie::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1332
RigDef::TorqueCurve::Sample
Definition: RigDef_File.h:1339
RigDef::Ropable::has_multilock
bool has_multilock
Definition: RigDef_File.h:1136
Actor.h
RigDef::Ropable::node
Node::Ref node
Definition: RigDef_File.h:1134
RigDef::TractionControl::pulse_per_sec
float pulse_per_sec
Definition: RigDef_File.h:1361
RigDef::Shock3::damp_in
float damp_in
Damping value applied when the shock is compressing.
Definition: RigDef_File.h:1241
RigDef::Shock2::damp_out
float damp_out
damping value applied when shock extending
Definition: RigDef_File.h:1220
RigDef::Engoption::clutch_time
float clutch_time
Seconds.
Definition: RigDef_File.h:823
RigDef::Shock3::spring_out
float spring_out
Spring value applied when shock extending.
Definition: RigDef_File.h:1242
RigDef::Brakes
Definition: RigDef_File.h:704
RigDef::Command2
Definition: RigDef_File.h:764
RigDef::Tie::max_stress
float max_stress
Definition: RigDef_File.h:1331
RigDef::SlideNode::max_attach_dist
float max_attach_dist
Definition: RigDef_File.h:1281
RigDef::Engturbo::param9
float param9
Definition: RigDef_File.h:847
RigDef::SoundSource2
Definition: RigDef_File.h:1290
BEAM_BREAK
static const float BEAM_BREAK
Definition: SimConstants.h:60
RigDef::Particle::emitter_node
Node::Ref emitter_node
Definition: RigDef_File.h:1069
RigDef::Author::name
Ogre::String name
Definition: RigDef_File.h:616
RigDef::Command2::option_r_rope
bool option_r_rope
Definition: RigDef_File.h:783
RigDef::VideoCamera::camera_role
RoR::VideoCamRole camera_role
Definition: RigDef_File.h:1438
RigDef::BaseMeshWheel::rim_radius
float rim_radius
Definition: RigDef_File.h:428
RigDef::FlexBodyWheel::tyre_mesh_name
Ogre::String tyre_mesh_name
Definition: RigDef_File.h:922
RigDef::Node::position
Ogre::Vector3 position
Definition: RigDef_Node.h:159
RigDef::Fileinfo
Definition: RigDef_File.h:870
RigDef::Shock::short_bound
float short_bound
Maximum contraction. The shortest length the shock can be, as a proportion of its original length....
Definition: RigDef_File.h:1197
RigDef::BeamDefaults::damping_constant
float damping_constant
Definition: RigDef_File.h:692
RigDef::BaseMeshWheel::spring
float spring
Definition: RigDef_File.h:430
RigDef::VideoCamera::alt_reference_node
Node::Ref alt_reference_node
Definition: RigDef_File.h:1429
RigDef::AeroAnimator::flags
BitMask_t flags
Definition: RigDef_File.h:400
RigDef::Shock::spring_rate
float spring_rate
The 'stiffness' of the shock. The higher the value, the less the shock will move for a given bump.
Definition: RigDef_File.h:1195
RigDef::Fileinfo::unique_id
Ogre::String unique_id
Definition: RigDef_File.h:872
RigDef::Engoption::max_idle_mixture
float max_idle_mixture
Definition: RigDef_File.h:827
RigDef::BeamDefaults::_enable_advanced_deformation
bool _enable_advanced_deformation
Informs whether "enable_advanced_deformation" directive preceded these defaults.
Definition: RigDef_File.h:698
RoR::ExtCameraMode::CINECAM
@ CINECAM
RigDef::Command2::option_p_1press
bool option_p_1press
Definition: RigDef_File.h:786
RigDef::Command2::inertia
Inertia inertia
Definition: RigDef_File.h:774
RigDef::ManagedMaterial::type
ManagedMaterialType type
Definition: RigDef_File.h:1031
RigDef::Command2::plays_sound
bool plays_sound
Definition: RigDef_File.h:777
PARSEINT
#define PARSEINT(x)
Definition: Application.h:57
RigDef::ManagedMaterial::options
ManagedMaterialsOptions options
Definition: RigDef_File.h:1032
RigDef::ManagedMaterial::name
Ogre::String name
Definition: RigDef_File.h:1030
RigDef::Prop::special
SpecialProp special
Definition: RigDef_File.h:1121
RigDef::Screwprop::top_node
Node::Ref top_node
Definition: RigDef_File.h:1173
RoR::FlareType::DASHBOARD
@ DASHBOARD
RigDef::AntiLockBrakes::attr_no_toggle
bool attr_no_toggle
Definition: RigDef_File.h:609
RigDef::Flare2::material_name
Ogre::String material_name
Definition: RigDef_File.h:888
RigDef::Wheel2::rim_damping
float rim_damping
Definition: RigDef_File.h:1456
RigDef::BaseWheel::mass
float mass
Definition: RigDef_File.h:418
RigDef::Camera::back_node
Node::Ref back_node
Definition: RigDef_File.h:729
RigDef::Pistonprop::couple_node
Node::Ref couple_node
Definition: RigDef_File.h:1079
RoR::FlareType::BRAKE_LIGHT
@ BRAKE_LIGHT
RigDef::Node::_has_load_weight_override
bool _has_load_weight_override
Definition: RigDef_Node.h:162
RigDef::Flare2::dashboard_link
std::string dashboard_link
Only 'd' type flares.
Definition: RigDef_File.h:885
RigDef::Engoption
Definition: RigDef_File.h:817
RigDef::Turbojet::front_node
Node::Ref front_node
Definition: RigDef_File.h:1403
RigDef::Turbojet::back_diameter
float back_diameter
Definition: RigDef_File.h:1410
RigDef::Beam::_has_extension_break_limit
bool _has_extension_break_limit
Definition: RigDef_File.h:636
RigDef::CruiseControl::min_speed
float min_speed
Definition: RigDef_File.h:792
RigDef::Author::_has_forum_account
bool _has_forum_account
Definition: RigDef_File.h:618
RigDef::Turboprop2::couple_node
Node::Ref couple_node
Definition: RigDef_File.h:1421
RigDef::AntiLockBrakes::min_speed
unsigned int min_speed
Definition: RigDef_File.h:605
RigDef::VideoCamera::texture_height
unsigned int texture_height
Definition: RigDef_File.h:1435
RigDef::SlideNode::spring_rate
float spring_rate
Definition: RigDef_File.h:1276
RigDef::Globals::dry_mass
float dry_mass
Definition: RigDef_File.h:939
RigDef::Wheel2::band_material_name
Ogre::String band_material_name
Definition: RigDef_File.h:1458
RigDef::Shock2
Definition: RigDef_File.h:1205
RigDef::BaseWheel::num_rays
unsigned int num_rays
Definition: RigDef_File.h:412
RoR::ExtCameraMode
ExtCameraMode
Definition: SimData.h:56
RigDef::Inertia::stop_delay_factor
float stop_delay_factor
Definition: RigDef_File.h:450
RigDef::DefaultMinimass
Definition: RigDef_File.h:796
RigDef::Pistonprop::axis_node
Node::Ref axis_node
Definition: RigDef_File.h:1077
RigDef::BaseWheel::rigidity_node
Node::Ref rigidity_node
Definition: RigDef_File.h:414
CacheSystem.h
A database of user-installed content alias 'mods' (vehicles, terrains...)
RigDef::Hydro::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:997
RigDef::FileFormatVersion
Definition: RigDef_File.h:865
keyword
static int keyword
Definition: Bench_TruckParser_IdentifyKeyword.cpp:1448
RigDef
Definition: ForwardDeclarations.h:276
RigDef::Keyword
Keyword
Definition: RigDef_File.h:65
RigDef::Node::load_weight_override
float load_weight_override
Definition: RigDef_Node.h:161
RigDef::Wheel2
Definition: RigDef_File.h:1453
RigDef::CruiseControl::autobrake
int autobrake
Definition: RigDef_File.h:793
RigDef::TractionControl
Definition: RigDef_File.h:1354
BITMASK_IS_1
#define BITMASK_IS_1(VAR, FLAGS)
Definition: BitFlags.h:14
RigDef::SlideNode::slide_node
Node::Ref slide_node
Definition: RigDef_File.h:1271
RigDef::Rotator::inertia_defaults
std::shared_ptr< Inertia > inertia_defaults
Definition: RigDef_File.h:1157
RigDef::Help::material
std::string material
Definition: RigDef_File.h:957
RigDef::Wing::max_deflection
float max_deflection
Definition: RigDef_File.h:1474
RigDef::Node::id
Id id
Definition: RigDef_Node.h:158
RigDef::VideoCamera::alt_orientation_node
Node::Ref alt_orientation_node
Definition: RigDef_File.h:1430
RigDef::VideoCamera::bottom_node
Node::Ref bottom_node
Definition: RigDef_File.h:1428
RigDef::Engine::torque
float torque
Definition: RigDef_File.h:810
BITMASK_SET_1
#define BITMASK_SET_1(VAR, FLAGS)
Definition: BitFlags.h:17
strnlen
#define strnlen(str, len)
Definition: InputEngine.cpp:400
RigDef::Flare2::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:879
RigDef::Trigger::detacher_group
int detacher_group
Definition: RigDef_File.h:1396
RigDef::Tie::max_length
float max_length
Definition: RigDef_File.h:1329
RigDef::Guid::guid
std::string guid
Definition: RigDef_File.h:946
RigDef::ManagedMaterial::specular_map
Ogre::String specular_map
Definition: RigDef_File.h:1035
RigDef::Cinecam
Definition: RigDef_File.h:743
RigDef::Flare2::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:882
RigDef::Fileinfo::file_version
int file_version
Definition: RigDef_File.h:874
RigDef::SlideNode::railgroup_id
int railgroup_id
Definition: RigDef_File.h:1280
RigDef::IsWhitespace
bool IsWhitespace(char c)
Definition: RigDef_Parser.cpp:49
RigDef::Shock2::short_bound
float short_bound
Maximum contraction limit, in percentage ( 1.00 = 100% )
Definition: RigDef_File.h:1223
RoR::Console::MessageType
MessageType
Definition: Console.h:46
RigDef::FlexBodyWheel::rim_mesh_name
Ogre::String rim_mesh_name
Definition: RigDef_File.h:921
RigDef::Airbrake::height
float height
Definition: RigDef_File.h:468
RigDef::Shock3::spring_in
float spring_in
Spring value applied when the shock is compressing.
Definition: RigDef_File.h:1240
RigDef::Shock2::precompression
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
Definition: RigDef_File.h:1225
RigDef::Tie::max_reach_length
float max_reach_length
Definition: RigDef_File.h:1326
RigDef::Prop::DashboardSpecial::_offset_is_set
bool _offset_is_set
Definition: RigDef_File.h:1097
RigDef::Document
Definition: RigDef_File.h:1482
RigDef::Wing::nodes
Node::Ref nodes[8]
Definition: RigDef_File.h:1469
RoR::ExtCameraMode::NODE
@ NODE
RigDef::Engturbo::param11
float param11
Definition: RigDef_File.h:849
RigDef::Airbrake::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:462
RigDef::Engturbo::param1
float param1
Definition: RigDef_File.h:839
RigDef::Flexbody::node_list_to_import
std::vector< Node::Range > node_list_to_import
Definition: RigDef_File.h:911
RigDef::Guid
Definition: RigDef_File.h:944
RigDef::MeshWheel
Definition: RigDef_File.h:1050
RoR::WheelSide::RIGHT
@ RIGHT
RigDef::ManagedMaterial
Definition: RigDef_File.h:1028
RigDef::TransferCase::a1
int a1
Definition: RigDef_File.h:1369
RoR::WheelSide::LEFT
@ LEFT
RigDef::Airbrake::aditional_node
Node::Ref aditional_node
Definition: RigDef_File.h:465
RigDef::BeamDefaults::scale
BeamDefaultsScale scale
Definition: RigDef_File.h:701
RoR::VCAM_ROLE_MIRROR_NOFLIP
@ VCAM_ROLE_MIRROR_NOFLIP
Same as VCAM_ROLE_MIRROR, but without flipping the texture horizontally (expects texcoords to be alre...
Definition: Application.h:380
RigDef::InterAxle
Definition: RigDef_File.h:1006
RigDef::Node::detacher_group
int detacher_group
Definition: RigDef_Node.h:166
RigDef::Shock2::progress_factor_spring_out
float progress_factor_spring_out
Progression factor springout, 0 = disabled, 1...x as multipliers, example:maximum springrate == sprin...
Definition: RigDef_File.h:1221
RigDef::Engoption::clutch_force
float clutch_force
Definition: RigDef_File.h:821
RigDef::Trigger::expansion_trigger_limit
float expansion_trigger_limit
Definition: RigDef_File.h:1392
Application.h
Central state/object manager and communications hub.
RigDef::Shock3::long_bound
float long_bound
Maximum extension limit, in percentage ( 1.00 = 100% )
Definition: RigDef_File.h:1251
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:274
RigDef::SlideNode::_break_force_set
bool _break_force_set
Definition: RigDef_File.h:1277
RigDef::Turbojet::wet_thrust
float wet_thrust
Definition: RigDef_File.h:1408
RigDef::Animator::flags
BitMask_t flags
Definition: RigDef_File.h:591
RoR::VCAM_ROLE_MIRROR
@ VCAM_ROLE_MIRROR
Flips the video output and when not in driver cam, acts like a natural mirror, not a screen.
Definition: Application.h:379
RigDef::Flexbody::mesh_name
Ogre::String mesh_name
Definition: RigDef_File.h:909
RigDef::TorqueCurve::Sample::torque_percent
float torque_percent
Definition: RigDef_File.h:1347
RigDef::Screwprop::prop_node
Node::Ref prop_node
Definition: RigDef_File.h:1171
RigDef::AntiLockBrakes::attr_is_on
bool attr_is_on
Definition: RigDef_File.h:607
RigDef::Engturbo::param4
float param4
Definition: RigDef_File.h:842
RigDef::DefaultSkin
Definition: RigDef_File.h:801
RigDef::Shock3::detacher_group
int detacher_group
Definition: RigDef_File.h:1255
RigDef::Engturbo::param2
float param2
Definition: RigDef_File.h:840
RigDef::Flare3::inertia_defaults
std::shared_ptr< Inertia > inertia_defaults
Definition: RigDef_File.h:893
RigDef::TransferCase::gear_ratios
std::vector< float > gear_ratios
Definition: RigDef_File.h:1373
RigDef::Submesh
Definition: RigDef_File.h:1306
RigDef::Shock3::damp_in_fast
float damp_in_fast
Damping value applied when shock is commpressing faster than split in velocity.
Definition: RigDef_File.h:1246
RigDef::Shock3::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:1239
RigDef::Hook::flag_no_disable
bool flag_no_disable
Definition: RigDef_File.h:974
RigDef::Animation::ratio
float ratio
Definition: RigDef_File.h:545
RigDef::Airbrake::max_inclination_angle
float max_inclination_angle
Definition: RigDef_File.h:469
RigDef::Wing::min_deflection
float min_deflection
Definition: RigDef_File.h:1473
RigDef::Inertia
Definition: RigDef_File.h:442
RigDef::Animator::aero_animator
AeroAnimator aero_animator
Definition: RigDef_File.h:594
RigDef::Animation::mode
BitMask_t mode
Definition: RigDef_File.h:550
RigDef::Axle::options
DifferentialTypeVec options
Order matters!
Definition: RigDef_File.h:624
RigDef::BaseMeshWheel::damping
float damping
Definition: RigDef_File.h:431
RigDef::Shock
Definition: RigDef_File.h:1187
RigDef::Command2::lengthen_rate
float lengthen_rate
Definition: RigDef_File.h:768
RigDef::BaseMeshWheel
Definition: RigDef_File.h:423
RigDef::TorqueCurve::Sample::power
float power
Definition: RigDef_File.h:1346
RigDef::Shock2::progress_factor_spring_in
float progress_factor_spring_in
Progression factor for springin. A value of 0 disables this option. 1...x as multipliers,...
Definition: RigDef_File.h:1217
RigDef::Trigger::shortbound_trigger_action
int shortbound_trigger_action
Definition: RigDef_File.h:1397
RigDef::Rotator::engine_coupling
float engine_coupling
Definition: RigDef_File.h:1158
RigDef::Hook::option_hook_range
float option_hook_range
Definition: RigDef_File.h:965
RigDef::Hydro::detacher_group
int detacher_group
Definition: RigDef_File.h:1003
RigDef::BeamDefaults::visual_beam_diameter
float visual_beam_diameter
Definition: RigDef_File.h:695
RigDef::BaseWheel2::tyre_damping
float tyre_damping
Definition: RigDef_File.h:439
RigDef::Shock3::options
BitMask_t options
Definition: RigDef_File.h:1253
RigDef::Engturbo::param5
float param5
Definition: RigDef_File.h:843
RigDef::Engoption::stall_rpm
float stall_rpm
Definition: RigDef_File.h:826
RigDef::Cab
Definition: RigDef_File.h:710
RigDef::Engturbo::tinertiaFactor
float tinertiaFactor
Definition: RigDef_File.h:837
RigDef::MaterialFlareBinding
Definition: RigDef_File.h:1038
RigDef::Prop::mesh_name
Ogre::String mesh_name
Definition: RigDef_File.h:1118
RigDef::VideoCamera::field_of_view
float field_of_view
Definition: RigDef_File.h:1433
RigDef::Rope::invisible
bool invisible
Definition: RigDef_File.h:1143
RigDef::SpeedLimiter
Definition: RigDef_File.h:1298
RigDef::Beam::defaults
std::shared_ptr< BeamDefaults > defaults
Definition: RigDef_File.h:638
RigDef::Lockgroup
Definition: RigDef_File.h:1013
RigDef::Hook::option_max_force
float option_max_force
Definition: RigDef_File.h:967
RigDef::BaseWheel2::tyre_springiness
float tyre_springiness
Definition: RigDef_File.h:438
RigDef::Flare2::node_axis_x
Node::Ref node_axis_x
Definition: RigDef_File.h:880
RigDef::Animator
Definition: RigDef_File.h:557
RigDef::FileFormatVersion::version
int version
Definition: RigDef_File.h:867
RigDef::Turbojet::front_diameter
float front_diameter
Definition: RigDef_File.h:1409
RigDef::Wheel2::face_material_name
Ogre::String face_material_name
Definition: RigDef_File.h:1457
RigDef::SpeedLimiter::max_speed
float max_speed
Definition: RigDef_File.h:1300
RigDef::DefaultSkin::skin_name
std::string skin_name
Definition: RigDef_File.h:803
RigDef::Brakes::parking_brake_force
float parking_brake_force
Definition: RigDef_File.h:707
RigDef::Author::email
Ogre::String email
Definition: RigDef_File.h:617
RigDef::Rope::end_node
Node::Ref end_node
Definition: RigDef_File.h:1142
RigDef::Flexbody
Definition: RigDef_File.h:902
RigDef::Hook::option_min_range_meters
float option_min_range_meters
Definition: RigDef_File.h:971
RigDef::Engturbo::param10
float param10
Definition: RigDef_File.h:848
RigDef::Command2::option_i_invisible
bool option_i_invisible
Definition: RigDef_File.h:782
RigDef::VideoCamera
Definition: RigDef_File.h:1424
RigDef::Shock2::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:1214
RigDef::TransferCase
Definition: RigDef_File.h:1367
RigDef::Rotator::inertia
Inertia inertia
Definition: RigDef_File.h:1156
RigDef::BeamDefaults::_is_user_defined
bool _is_user_defined
Informs whether these data were read from "set_beam_defaults" directive or filled in by the parser on...
Definition: RigDef_File.h:700
RoR::ExtCameraMode::CLASSIC
@ CLASSIC
RigDef::FlexBodyWheel::side
RoR::WheelSide side
Definition: RigDef_File.h:918
RigDef::Wing::airfoil
Ogre::String airfoil
Definition: RigDef_File.h:1475
RigDef::Shock::precompression
float precompression
Changes compression or extension of the suspension when the truck spawns. This can be used to "level"...
Definition: RigDef_File.h:1199
RigDef::Shock2::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1227
RigDef::Turboprop2
Definition: RigDef_File.h:1414
RigDef::TractionControl::wheel_slip
float wheel_slip
Definition: RigDef_File.h:1359
RigDef::Cinecam::node_mass
float node_mass
Definition: RigDef_File.h:749
RigDef::Inertia::start_delay_factor
float start_delay_factor
Definition: RigDef_File.h:449
RigDef::Prop::y_axis_node
Node::Ref y_axis_node
Definition: RigDef_File.h:1115
RigDef::TractionControl::attr_is_on
bool attr_is_on
Definition: RigDef_File.h:1362
RigDef::Fusedrag::front_node
Node::Ref front_node
Definition: RigDef_File.h:930
RigDef::BaseMeshWheel::tyre_radius
float tyre_radius
Definition: RigDef_File.h:429
RigDef::Script
Definition: RigDef_File.h:1177
RigDef::Minimass::global_min_mass_Kg
float global_min_mass_Kg
minimum node mass in Kg - only effective where DefaultMinimass was not set.
Definition: RigDef_File.h:1046
RigDef::VideoCamera::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:1431
RigDef::Engine::reverse_gear_ratio
float reverse_gear_ratio
Definition: RigDef_File.h:812
RigDef::BeamDefaultsScale::deformation_threshold_constant
float deformation_threshold_constant
Definition: RigDef_File.h:652
RigDef::Engturbo::nturbos
int nturbos
Definition: RigDef_File.h:838
RigDef::Exhaust::direction_node
Node::Ref direction_node
Definition: RigDef_File.h:855
RigDef::Rotator::rotating_plate_nodes
Node::Ref rotating_plate_nodes[4]
Definition: RigDef_File.h:1152
RigDef::Script::filename
std::string filename
Definition: RigDef_File.h:1179
RigDef::Globals::material_name
Ogre::String material_name
Definition: RigDef_File.h:941
RigDef::BaseWheel::propulsion
WheelPropulsion propulsion
Definition: RigDef_File.h:416
RigDef::SkeletonSettings::visibility_range_meters
float visibility_range_meters
Definition: RigDef_File.h:1260
RigDef::Exhaust::particle_name
Ogre::String particle_name
Definition: RigDef_File.h:856
RigDef::Exhaust::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:854
RigDef::Tie
Definition: RigDef_File.h:1320
RigDef::Animator::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:596
RigDef::Flare2::size
float size
Definition: RigDef_File.h:887
RoR::VideoCamRole
VideoCamRole
Definition: Application.h:373
RigDef::WheelDetacher
Definition: RigDef_File.h:1461
RigDef::BeamDefaults::plastic_deform_coef
float plastic_deform_coef
Definition: RigDef_File.h:697
RigDef::Rotator2::tolerance
float tolerance
Definition: RigDef_File.h:1165
RigDef::Turbojet::side_node
Node::Ref side_node
Definition: RigDef_File.h:1405
RigDef::Minimass::option
MinimassOption option
Definition: RigDef_File.h:1047
RigDef::TransferCase::has_2wd
bool has_2wd
Definition: RigDef_File.h:1371
RigDef::Fusedrag
Definition: RigDef_File.h:925
RigDef::Trigger::boundary_timer
float boundary_timer
Definition: RigDef_File.h:1394
RigDef::Globals::cargo_mass
float cargo_mass
Definition: RigDef_File.h:940
RigDef::Wheel::springiness
float springiness
Definition: RigDef_File.h:1447
RigDef::Airbrake::texcoord_y2
float texcoord_y2
Definition: RigDef_File.h:473
RigDef::Engine
Definition: RigDef_File.h:806
RigDef::Trigger::contraction_trigger_limit
float contraction_trigger_limit
Definition: RigDef_File.h:1391
RigDef::Node::node_defaults
std::shared_ptr< NodeDefaults > node_defaults
Definition: RigDef_Node.h:163
RigDef::Texcoord
Definition: RigDef_File.h:1313
RigDef::StrEqualsNocase
bool StrEqualsNocase(std::string const &s1, std::string const &s2)
Definition: RigDef_Parser.cpp:59
RigDef::Author::type
Ogre::String type
Definition: RigDef_File.h:614
RigDef::Shock3::split_vel_in
float split_vel_in
Split velocity in (m/s) - threshold for slow / fast damping during compression.
Definition: RigDef_File.h:1245
RigDef::Beam::detacher_group
int detacher_group
Definition: RigDef_File.h:637
RigDef_Parser.h
Checks the rig-def file syntax and loads data to memory.
RigDef::TractionControl::attr_no_toggle
bool attr_no_toggle
Definition: RigDef_File.h:1364
RigDef::Wheel::band_material_name
Ogre::String band_material_name
Definition: RigDef_File.h:1450
RigDef::Turbojet::dry_thrust
float dry_thrust
Definition: RigDef_File.h:1407
RigDef::Beam::options
BitMask_t options
Definition: RigDef_File.h:634
DEFAULT_SPRING
static const float DEFAULT_SPRING
Definition: SimConstants.h:48
RigDef::Camera::center_node
Node::Ref center_node
Definition: RigDef_File.h:728
RigDef::Animation::source
BitMask64_t source
Definition: RigDef_File.h:548
RigDef::Texcoord::u
float u
Definition: RigDef_File.h:1316
RigDef::FlexBodyWheel::rim_damping
float rim_damping
Definition: RigDef_File.h:920
RigDef::Shock3::short_bound
float short_bound
Maximum contraction limit, in percentage ( 1.00 = 100% )
Definition: RigDef_File.h:1250
RigDef::Engturbo::param7
float param7
Definition: RigDef_File.h:845
RigDef::Engoption::idle_rpm
float idle_rpm
Definition: RigDef_File.h:825
RigDef::AeroAnimator::engine_idx
unsigned int engine_idx
Definition: RigDef_File.h:401
RigDef::BaseWheel2::tyre_radius
float tyre_radius
Definition: RigDef_File.h:437
RigDef::Shock2::progress_factor_damp_in
float progress_factor_damp_in
Progression factor for dampin. 0 = disabled, 1...x as multipliers, example:maximum dampingrate == spr...
Definition: RigDef_File.h:1218
RigDef::Animator::inertia_defaults
std::shared_ptr< Inertia > inertia_defaults
Definition: RigDef_File.h:595
RigDef::Node::options
BitMask_t options
Definition: RigDef_Node.h:160
RigDef::BeamDefaultsScale::springiness
float springiness
Definition: RigDef_File.h:650
RigDef::BaseWheel::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:420
RigDef::Rotator2
Definition: RigDef_File.h:1162
RigDef::Shock::options
BitMask_t options
Definition: RigDef_File.h:1200
RigDef::Command2::affect_engine
float affect_engine
Definition: RigDef_File.h:775
RigDef::Shock2::detacher_group
int detacher_group
Definition: RigDef_File.h:1228
RigDef::Author::forum_account_id
unsigned int forum_account_id
Definition: RigDef_File.h:615
RigDef::BaseMeshWheel::mesh_name
Ogre::String mesh_name
Definition: RigDef_File.h:426
RigDef::Shock3
Definition: RigDef_File.h:1231
RigDef::Exhaust
Definition: RigDef_File.h:852
RigDef::Airbrake::width
float width
Definition: RigDef_File.h:467
RigDef::Node::Id::setStr
void setStr(std::string const &id_str)
Definition: RigDef_Node.cpp:63
RigDef::VideoCamera::max_clip_distance
float max_clip_distance
Definition: RigDef_File.h:1437
DEFAULT_DAMP
static const float DEFAULT_DAMP
Definition: SimConstants.h:49
RigDef::Animation::motor_sources
std::list< MotorSource > motor_sources
Definition: RigDef_File.h:549
RigDef::Prop::special_prop_dashboard
DashboardSpecial special_prop_dashboard
Definition: RigDef_File.h:1123
RigDef::CollisionRange::node_collision_range
float node_collision_range
Definition: RigDef_File.h:761
RigDef::Pistonprop::blade_tip_nodes
Node::Ref blade_tip_nodes[4]
Definition: RigDef_File.h:1078
RigDef::Fusedrag::airfoil_name
Ogre::String airfoil_name
Definition: RigDef_File.h:933
RigDef::BeamDefaultsScale::damping_constant
float damping_constant
Definition: RigDef_File.h:651
RigDef::Flare2::control_number
int control_number
Only 'u' type flares.
Definition: RigDef_File.h:884
RigDef::Shock2::damp_in
float damp_in
Damping value applied when the shock is compressing.
Definition: RigDef_File.h:1216
RigDef::Turbojet::nozzle_length
float nozzle_length
Definition: RigDef_File.h:1411
RigDef::Lockgroup::nodes
std::vector< Node::Ref > nodes
Definition: RigDef_File.h:1020
BitMask_t
uint32_t BitMask_t
Definition: BitFlags.h:7
RigDef::Axle
Definition: RigDef_File.h:621
RigDef::Shock2::options
BitMask_t options
Definition: RigDef_File.h:1226
RigDef::Fusedrag::area_coefficient
float area_coefficient
Definition: RigDef_File.h:934
RigDef::SlideNode::_attachment_rate_set
bool _attachment_rate_set
Definition: RigDef_File.h:1279
RigDef::Shock::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:1194
RigDef::Cinecam::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:750
RigDef::Tie::root_node
Node::Ref root_node
Definition: RigDef_File.h:1325
RoR::Console::CONSOLE_MSGTYPE_ACTOR
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition: Console.h:63
RigDef::TransferCase::has_2wd_lo
bool has_2wd_lo
Definition: RigDef_File.h:1372
RigDef::EngineType
EngineType
Definition: RigDef_File.h:219
RigDef::Lockgroup::number
int number
Definition: RigDef_File.h:1019
RigDef::Node
Definition: RigDef_Node.h:39
RigDef::DocumentPtr
std::shared_ptr< Document > DocumentPtr
Definition: ForwardDeclarations.h:278
RoR::Console::CONSOLE_SYSTEM_WARNING
@ CONSOLE_SYSTEM_WARNING
Definition: Console.h:53
RigDef::BaseWheel::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:413
RigDef::VideoCamera::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:1426
RigDef::SoundSource
Definition: RigDef_File.h:1284
RigDef::RailGroup::node_list
std::vector< Node::Range > node_list
Definition: RigDef_File.h:1129
RigDef::BaseWheel::braking
WheelBraking braking
Definition: RigDef_File.h:415
RoR::WheelSide
WheelSide
Used by rig-def/addonpart/tuneup formats to specify wheel rim mesh orientation.
Definition: GfxData.h:115
RigDef::Turbojet::is_reversable
int is_reversable
Definition: RigDef_File.h:1406
RigDef::BaseWheel::node_defaults
std::shared_ptr< NodeDefaults > node_defaults
Definition: RigDef_File.h:419
RigDef::Airbrake::texcoord_x2
float texcoord_x2
Definition: RigDef_File.h:471
RigDef::Hook::option_lockgroup
int option_lockgroup
Definition: RigDef_File.h:969
RigDef::Hook::flag_no_rope
bool flag_no_rope
Definition: RigDef_File.h:975
RigDef::AntiLockBrakes::regulation_force
float regulation_force
Definition: RigDef_File.h:604
RigDef::Assetpack
Definition: RigDef_File.h:404
RigDef::Shock::damping
float damping
The 'resistance to motion' of the shock. The best value is given by this equation: 2 * sqrt(suspended...
Definition: RigDef_File.h:1196
RigDef::Animator::lenghtening_factor
float lenghtening_factor
Definition: RigDef_File.h:590
RigDef::Rotator2::description
Ogre::String description
Definition: RigDef_File.h:1166
RigDef::Prop::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:1113
RigDef::SlideNode::_max_attach_dist_set
bool _max_attach_dist_set
Definition: RigDef_File.h:1281
RigDef::Hydro::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1002
RigDef::Rotator::base_plate_nodes
Node::Ref base_plate_nodes[4]
Definition: RigDef_File.h:1151
RigDef::Flexbody::x_axis_node
Node::Ref x_axis_node
Definition: RigDef_File.h:905
RigDef::Airbrake::x_axis_node
Node::Ref x_axis_node
Definition: RigDef_File.h:463
RigDef::Animation::event_name
Ogre::String event_name
Definition: RigDef_File.h:551
ignored
or anywhere else will not be considered a but parsed as regular data ! Each line is treated as values separated by separators Possible i e animators Multiline description Single does not affect it Directive usualy set global attributes or change behavior of the parsing Directive may appear in any block section Modularity The elements can be grouped into modules Each module must belong to one or more configurations Directives sectionconfig specify truck configurations the user can choose from Exactly one must be selected If the first defined is used lettercase matches original docs(parsing is insensitive). NAME TYPE NOTES advdrag BLOCK add_animation DIRECTIVE Special syntax airbrakes BLOCK animators BLOCK Special syntax IF(values[0]=="") bad trailing chars are silently ignored no space at the end Items delimited On each side of there is max item Empty invalid string parses as node num items Acceptable item the node is the others ignored
Definition: ReadMe.txt:302
RigDef::Particle::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:1070
RigDef::Pistonprop::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:1076
RigDef::Shock3::damp_out
float damp_out
Damping value applied when shock extending.
Definition: RigDef_File.h:1243
RigDef::Command2::option_c_auto_center
bool option_c_auto_center
Definition: RigDef_File.h:784
RigDef::Rotator::spin_left_key
unsigned int spin_left_key
Definition: RigDef_File.h:1154
RigDef::Minimass
Definition: RigDef_File.h:1044
RigDef::Shock3::damp_in_slow
float damp_in_slow
Damping value applied when shock is commpressing slower than split in velocity.
Definition: RigDef_File.h:1244
RigDef::BeamDefaults::beam_material_name
Ogre::String beam_material_name
Definition: RigDef_File.h:696
RoR::FlareType::HIGH_BEAM
@ HIGH_BEAM
RigDef::Turbojet
Definition: RigDef_File.h:1401
RigDef::Hydro::lenghtening_factor
float lenghtening_factor
Definition: RigDef_File.h:998
RigDef::VideoCamera::material_name
Ogre::String material_name
Definition: RigDef_File.h:1440
RigDef::Command2::max_contraction
float max_contraction
Definition: RigDef_File.h:769
RigDef::Command2::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:766
RigDef::ROOT_MODULE_NAME
const char * ROOT_MODULE_NAME
Definition: RigDef_File.cpp:35
RigDef::AntiLockBrakes::pulse_per_sec
float pulse_per_sec
Definition: RigDef_File.h:606
RigDef::Beam
Definition: RigDef_File.h:627
RigDef::Rotator2::rotating_force
float rotating_force
Definition: RigDef_File.h:1164
RigDef::Screwprop
Definition: RigDef_File.h:1169
RigDef::Hook::option_timer
float option_timer
Definition: RigDef_File.h:970
RigDef::GuiSettings::value
std::string value
Definition: RigDef_File.h:952
RigDef::Prop::x_axis_node
Node::Ref x_axis_node
Definition: RigDef_File.h:1114
RigDef::Animation::upper_limit
float upper_limit
Definition: RigDef_File.h:547
RigDef::Cab::options
BitMask_t options
Definition: RigDef_File.h:723
RigDef::ExtCamera::mode
RoR::ExtCameraMode mode
Definition: RigDef_File.h:861
RigDef::Inertia::stop_function
Ogre::String stop_function
Definition: RigDef_File.h:452
RigDef::Prop::DashboardSpecial::rotation_angle
float rotation_angle
Definition: RigDef_File.h:1098
RigDef::Engoption::inertia
float inertia
Definition: RigDef_File.h:819
RigDef::CruiseControl
Definition: RigDef_File.h:790
RigDef::WheelBraking
WheelBraking
Definition: RigDef_File.h:242
RigDef::BaseMeshWheel::material_name
Ogre::String material_name
Definition: RigDef_File.h:427
RigDef::Rope::detacher_group
int detacher_group
Definition: RigDef_File.h:1145
RigDef::Wing
Definition: RigDef_File.h:1467
RigDef::BaseWheel::reference_arm_node
Node::Ref reference_arm_node
Definition: RigDef_File.h:417
RigDef::Node::default_minimass
std::shared_ptr< DefaultMinimass > default_minimass
Definition: RigDef_Node.h:164
RigDef_File.h
Data structures representing 'truck' file format, see https://docs.rigsofrods.org/vehicle-creation/fi...
RigDef::Animation::dash_link_name
Ogre::String dash_link_name
Definition: RigDef_File.h:552
RoR
Definition: AppContext.h:36
RigDef::ManagedMaterialsOptions
Definition: RigDef_File.h:1023
RigDef::Hook::flag_visible
bool flag_visible
Definition: RigDef_File.h:976
RigDef::Fusedrag::rear_node
Node::Ref rear_node
Definition: RigDef_File.h:931
RigDef::Globals
Definition: RigDef_File.h:937
RigDef::Prop::special_prop_beacon
BeaconSpecial special_prop_beacon
Definition: RigDef_File.h:1122
RigDef::RailGroup::id
unsigned int id
Definition: RigDef_File.h:1128
RigDef::Command2::needs_engine
bool needs_engine
Definition: RigDef_File.h:776
RigDef::Pistonprop::pitch
float pitch
Definition: RigDef_File.h:1081
RigDef::ExtCamera
Definition: RigDef_File.h:859
RigDef::Shock2::progress_factor_damp_out
float progress_factor_damp_out
Progression factor dampout, 0 = disabled, 1...x as multipliers, example:maximum dampingrate == spring...
Definition: RigDef_File.h:1222
RigDef::Flexbody::y_axis_node
Node::Ref y_axis_node
Definition: RigDef_File.h:906
RigDef::Rope
Definition: RigDef_File.h:1139
RigDef::Cinecam::nodes
Node::Ref nodes[8]
Definition: RigDef_File.h:746
RigDef::Airbrake::offset
Ogre::Vector3 offset
Definition: RigDef_File.h:466
RigDef::Wheel2::rim_springiness
float rim_springiness
Definition: RigDef_File.h:1455
RigDef::Hook::option_speed_coef
float option_speed_coef
Definition: RigDef_File.h:966
RigDef::Ropable::group
int group
Definition: RigDef_File.h:1135
RigDef::Trigger::beam_defaults
std::shared_ptr< BeamDefaults > beam_defaults
Definition: RigDef_File.h:1395
RigDef_Regexes.h
Defines regular expressions to verify and pull data from rig-def file. 'E' stands for Expression.
RigDef::Wheel::face_material_name
Ogre::String face_material_name
Definition: RigDef_File.h:1449
RigDef::Wing::efficacy_coef
float efficacy_coef
Definition: RigDef_File.h:1476
RigDef::SlideNode
Definition: RigDef_File.h:1264
RigDef::Cinecam::damping
float damping
Definition: RigDef_File.h:748
RigDef::TractionControl::regulation_force
float regulation_force
Definition: RigDef_File.h:1358
RigDef::TransferCase::a2
int a2
Definition: RigDef_File.h:1370
RigDef::MeshWheel2
Definition: RigDef_File.h:1053
RigDef::Hook::flag_self_lock
bool flag_self_lock
Definition: RigDef_File.h:972
RigDef::SoundSource::sound_script_name
Ogre::String sound_script_name
Definition: RigDef_File.h:1287
RigDef::Pistonprop::turbine_power_kW
float turbine_power_kW
Definition: RigDef_File.h:1080
RigDef::Animation::MotorSource::source
BitMask_t source
Definition: RigDef_File.h:490
RigDef::WheelDetacher::detacher_group
int detacher_group
Definition: RigDef_File.h:1464
RigDef::TorqueCurve
Definition: RigDef_File.h:1337
RigDef::Shock3::damp_out_slow
float damp_out_slow
Damping value applied when shock is commpressing slower than split out velocity.
Definition: RigDef_File.h:1247
RigDef::Prop::BeaconSpecial::flare_material_name
Ogre::String flare_material_name
Definition: RigDef_File.h:1109
RigDef::DifferentialTypeVec
std::vector< DifferentialType > DifferentialTypeVec
Definition: RigDef_File.h:234
RigDef::Turboprop2::reference_node
Node::Ref reference_node
Definition: RigDef_File.h:1416
RigDef::Hook::node
Node::Ref node
Definition: RigDef_File.h:964
RigDef::Beam::nodes
Node::Ref nodes[2]
Definition: RigDef_File.h:633
RigDef::Rope::root_node
Node::Ref root_node
Definition: RigDef_File.h:1141
RigDef::Fileinfo::category_id
int category_id
Definition: RigDef_File.h:873
RigDef::VideoCamera::camera_name
Ogre::String camera_name
Definition: RigDef_File.h:1441
RigDef::Turbojet::back_node
Node::Ref back_node
Definition: RigDef_File.h:1404
RigDef::Cab::nodes
Node::Ref nodes[3]
Definition: RigDef_File.h:722
RigDef::Prop::BeaconSpecial::color
Ogre::ColourValue color
Definition: RigDef_File.h:1110