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