RigsofRods
Soft-body Physics Simulation
Actor.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-2022 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 
22 #include "Actor.h"
23 
24 #include "AirBrake.h"
25 #include "Airfoil.h"
26 #include "Application.h"
27 #include "AutoPilot.h"
28 #include "SimData.h"
29 #include "ActorManager.h"
30 #include "Buoyance.h"
31 #include "CacheSystem.h"
32 #include "ChatSystem.h"
33 #include "CmdKeyInertia.h"
34 #include "Collisions.h"
35 #include "DashBoardManager.h"
36 #include "Differentials.h"
37 #include "DynamicCollisions.h"
38 #include "EngineSim.h"
39 #include "ErrorUtils.h"
40 #include "FlexAirfoil.h"
41 #include "FlexBody.h"
42 #include "FlexMesh.h"
43 #include "FlexMeshWheel.h"
44 #include "FlexObj.h"
45 #include "GameContext.h"
46 #include "GfxScene.h"
47 #include "GUIManager.h"
48 #include "Console.h"
49 #include "GfxActor.h"
50 #include "InputEngine.h"
51 #include "Language.h"
52 #include "MeshObject.h"
53 #include "MovableText.h"
54 #include "Network.h"
55 #include "PointColDetector.h"
56 #include "Replay.h"
57 #include "ActorSpawner.h"
58 #include "RoRnet.h"
59 #include "ScrewProp.h"
60 #include "ScriptEngine.h"
61 #include "Skidmark.h"
62 #include "SlideNode.h"
63 #include "SoundScriptManager.h"
64 #include "Terrain.h"
65 #include "TuneupFileFormat.h"
66 #include "TurboJet.h"
67 #include "TurboProp.h"
68 #include "Utils.h"
69 #include "VehicleAI.h"
70 #include "Water.h"
71 #include <fmt/format.h>
72 
73 #include <sstream>
74 #include <iomanip>
75 
76 using namespace Ogre;
77 using namespace RoR;
78 
79 static const Ogre::Vector3 BOUNDING_BOX_PADDING(0.05f, 0.05f, 0.05f);
80 
81 Actor::~Actor()
82 {
83  // This class must be handled by `ActorManager::DeleteActorInternal()` (use MSG_SIM_DELETE_ACTOR_REQUESTED) which performs disposal.
84  ROR_ASSERT(ar_state == ActorState::DISPOSED);
85  // We don't dispose here as it's a complex process and not safe to do from a destructor, especially not at unpredictable time.
86 }
87 
88 void Actor::dispose()
89 {
90  // Handler for `MSG_SIM_DELETE_ACTOR_REQUESTED` message - should not be invoked otherwise.
91  // --------------------------------------------------------------------------------------
92 
93  ROR_ASSERT(ar_state != ActorState::DISPOSED);
94 
95  this->DisjoinInterActorBeams(); // OK to be invoked here - processing `MSG_SIM_DELETE_ACTOR_REQUESTED`.
96  ar_hooks.clear();
97  ar_ties.clear();
98  ar_node_to_beam_connections.clear();
99  ar_node_to_node_connections.clear();
100 
101  // delete all classes we might have constructed
102  if (ar_dashboard != nullptr)
103  {
104  delete ar_dashboard;
105  ar_dashboard = nullptr;
106  }
107 
108  // stop all the Sounds
109 #ifdef USE_OPENAL
110  for (int i = SS_TRIG_NONE + 1; i < SS_MAX_TRIG; i++)
111  {
112  SOUND_STOP(this, i);
113  }
114  muteAllSounds();
115  for (int i = 0; i < ar_num_soundsources; i++)
116  {
117  if (ar_soundsources[i].ssi)
118  {
119  App::GetSoundScriptManager()->removeInstance(ar_soundsources[i].ssi);
120  ar_soundsources[i].ssi = nullptr;
121  }
122  }
123  ar_num_soundsources = 0;
124 #endif // USE_OPENAL
125 
126  if (ar_engine != nullptr)
127  {
128  delete ar_engine;
129  ar_engine = nullptr;
130  }
131 
132  if (ar_autopilot != nullptr)
133  {
134  delete ar_autopilot;
135  ar_autopilot = nullptr;
136  }
137 
138  if (m_fusealge_airfoil)
139  delete m_fusealge_airfoil;
140  m_fusealge_airfoil = nullptr;
141 
142  if (m_replay_handler)
143  delete m_replay_handler;
144  m_replay_handler = nullptr;
145 
146  ar_vehicle_ai = nullptr; // RefCountingObjectPtr<> will handle the cleanup.
147 
148  // remove all scene nodes
149  if (m_deletion_scene_nodes.size() > 0)
150  {
151  for (unsigned int i = 0; i < m_deletion_scene_nodes.size(); i++)
152  {
153  try
154  {
155  if (!m_deletion_scene_nodes[i])
156  continue;
157  m_deletion_scene_nodes[i]->removeAndDestroyAllChildren();
158  App::GetGfxScene()->GetSceneManager()->destroySceneNode(m_deletion_scene_nodes[i]);
159  }
160  catch (...)
161  {
162  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting scenenode {}/{} from `deletion list`.",
163  ar_instance_id, ar_net_stream_id, ar_filename, i, m_deletion_scene_nodes.size()), HANDLEGENERICEXCEPTION_LOGFILE);
164  }
165  }
166  m_deletion_scene_nodes.clear();
167  }
168  // remove all entities
169  if (m_deletion_entities.size() > 0)
170  {
171  for (unsigned int i = 0; i < m_deletion_entities.size(); i++)
172  {
173  try
174  {
175  if (!m_deletion_entities[i])
176  continue;
177  m_deletion_entities[i]->detachAllObjectsFromBone();
178  App::GetGfxScene()->GetSceneManager()->destroyEntity(m_deletion_entities[i]->getName());
179  }
180  catch (...)
181  {
182  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting entity {}/{} from `deletion list`.",
183  ar_instance_id, ar_net_stream_id, ar_filename, i, m_deletion_entities.size()), HANDLEGENERICEXCEPTION_LOGFILE);
184  }
185  }
186  m_deletion_entities.clear();
187  }
188 
189  // delete GfxActor
190  m_gfx_actor.reset();
191 
192  // delete wings
193  for (int i = 0; i < ar_num_wings; i++)
194  {
195  try
196  {
197  // flexAirfoil, airfoil
198  if (ar_wings[i].fa)
199  delete ar_wings[i].fa;
200  if (ar_wings[i].cnode)
201  {
202  ar_wings[i].cnode->removeAndDestroyAllChildren();
203  App::GetGfxScene()->GetSceneManager()->destroySceneNode(ar_wings[i].cnode);
204  }
205  }
206  catch (...)
207  {
208  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting wing {}/{}.",
209  ar_instance_id, ar_net_stream_id, ar_filename, i, ar_num_wings), HANDLEGENERICEXCEPTION_LOGFILE);
210  }
211  }
212 
213  // delete aeroengines
214  for (int i = 0; i < ar_num_aeroengines; i++)
215  {
216  if (ar_aeroengines[i])
217  delete ar_aeroengines[i];
218  }
219 
220  // delete screwprops
221  for (int i = 0; i < ar_num_screwprops; i++)
222  {
223  if (ar_screwprops[i])
224  {
225  delete ar_screwprops[i];
226  ar_screwprops[i] = nullptr;
227  }
228  }
229 
230  // delete airbrakes
231  for (Airbrake* ab: ar_airbrakes)
232  {
233  delete ab;
234  }
235  ar_airbrakes.clear();
236 
237  // delete skidmarks
238  for (int i = 0; i < ar_num_wheels; ++i)
239  {
240  delete m_skid_trails[i];
241  m_skid_trails[i] = nullptr;
242  }
243 
244  // delete flares
245  for (size_t i = 0; i < this->ar_flares.size(); i++)
246  {
247  try
248  {
249  if (ar_flares[i].snode)
250  {
251  ar_flares[i].snode->removeAndDestroyAllChildren();
252  App::GetGfxScene()->GetSceneManager()->destroySceneNode(ar_flares[i].snode);
253  }
254  if (ar_flares[i].bbs)
255  App::GetGfxScene()->GetSceneManager()->destroyBillboardSet(ar_flares[i].bbs);
256  if (ar_flares[i].light)
257  App::GetGfxScene()->GetSceneManager()->destroyLight(ar_flares[i].light);
258  }
259  catch (...)
260  {
261  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting flare {}/{}.",
262  ar_instance_id, ar_net_stream_id, ar_filename, i, ar_flares.size()), HANDLEGENERICEXCEPTION_LOGFILE);
263  }
264  }
265  this->ar_flares.clear();
266 
267  // delete exhausts
268  for (std::vector<exhaust_t>::iterator it = exhausts.begin(); it != exhausts.end(); it++)
269  {
270  try
271  {
272  if (it->smokeNode)
273  {
274  it->smokeNode->removeAndDestroyAllChildren();
275  App::GetGfxScene()->GetSceneManager()->destroySceneNode(it->smokeNode);
276  }
277  if (it->smoker)
278  {
279  it->smoker->removeAllAffectors();
280  it->smoker->removeAllEmitters();
281  App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(it->smoker);
282  }
283  }
284  catch (...)
285  {
286  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting exhaust {}/{}.",
287  ar_instance_id, ar_net_stream_id, ar_filename, std::distance(exhausts.begin(), it), exhausts.size()), HANDLEGENERICEXCEPTION_LOGFILE);
288  }
289  }
290 
291  // delete custom particles
292  for (int i = 0; i < ar_num_custom_particles; i++)
293  {
294  try
295  {
296  if (ar_custom_particles[i].snode)
297  {
298  ar_custom_particles[i].snode->removeAndDestroyAllChildren();
299  App::GetGfxScene()->GetSceneManager()->destroySceneNode(ar_custom_particles[i].snode);
300  }
301  if (ar_custom_particles[i].psys)
302  {
303  ar_custom_particles[i].psys->removeAllAffectors();
304  ar_custom_particles[i].psys->removeAllEmitters();
305  App::GetGfxScene()->GetSceneManager()->destroyParticleSystem(ar_custom_particles[i].psys);
306  }
307  }
308  catch (...)
309  {
310  HandleGenericException(fmt::format("Actor::dispose(); instanceID:{}, streamID:{}, filename:{}; deleting custom particle {}/{}.",
311  ar_instance_id, ar_net_stream_id, ar_filename, i, ar_num_custom_particles), HANDLEGENERICEXCEPTION_LOGFILE);
312  }
313  }
314 
315  // delete Rails
316  for (std::vector<RailGroup*>::iterator it = m_railgroups.begin(); it != m_railgroups.end(); it++)
317  {
318  delete (*it);
319  }
320  m_railgroups.clear();
321 
322  if (m_intra_point_col_detector)
323  {
324  delete m_intra_point_col_detector;
325  m_intra_point_col_detector = nullptr;
326  }
327 
328  if (m_inter_point_col_detector)
329  {
330  delete m_inter_point_col_detector;
331  m_inter_point_col_detector = nullptr;
332  }
333 
334  if (m_transfer_case)
335  delete m_transfer_case;
336 
337  for (int i = 0; i < m_num_axle_diffs; ++i)
338  {
339  if (m_axle_diffs[i] != nullptr)
340  delete m_axle_diffs[i];
341  }
342  m_num_axle_diffs = 0;
343 
344  for (int i = 0; i < m_num_wheel_diffs; ++i)
345  {
346  if (m_wheel_diffs[i] != nullptr)
347  delete m_wheel_diffs[i];
348  }
349  m_num_wheel_diffs = 0;
350 
351  delete[] ar_nodes;
352  ar_num_nodes = 0;
353  m_wheel_node_count = 0;
354  delete[] ar_beams;
355  ar_num_beams = 0;
356  delete[] ar_shocks;
357  ar_num_shocks = 0;
358  delete[] ar_rotators;
359  ar_num_rotators = 0;
360  delete[] ar_wings;
361  ar_num_wings = 0;
362 
363  ar_state = ActorState::DISPOSED;
364 }
365 
366 // This method scales actors. Stresses should *NOT* be scaled, they describe
367 // the material type and they do not depend on length or scale.
368 void Actor::scaleTruck(float value)
369 {
370  if (ar_state == ActorState::DISPOSED)
371  return;
372  if (value < 0)
373  return;
374 
375  ar_scale *= value;
376  // scale beams
377  for (int i = 0; i < ar_num_beams; i++)
378  {
379  //ar_beams[i].k *= value;
380  ar_beams[i].d *= value;
381  ar_beams[i].L *= value;
382  ar_beams[i].refL *= value;
383  }
384  // scale hydros
385  for (hydrobeam_t& hbeam: ar_hydros)
386  {
387  hbeam.hb_ref_length *= value;
388  hbeam.hb_speed *= value;
389  }
390  // scale nodes
391  Vector3 refpos = ar_nodes[0].AbsPosition;
392  Vector3 relpos = ar_nodes[0].RelPosition;
393  for (int i = 1; i < ar_num_nodes; i++)
394  {
395  ar_initial_node_positions[i] = refpos + (ar_initial_node_positions[i] - refpos) * value;
396  ar_nodes[i].AbsPosition = refpos + (ar_nodes[i].AbsPosition - refpos) * value;
397  ar_nodes[i].RelPosition = relpos + (ar_nodes[i].RelPosition - relpos) * value;
398  ar_nodes[i].Velocity *= value;
399  ar_nodes[i].Forces *= value;
400  ar_nodes[i].mass *= value;
401  }
402  updateSlideNodePositions();
403 
404  m_gfx_actor->ScaleActor(relpos, value);
405 
406 }
407 
408 float Actor::getRotation()
409 {
410  if (ar_state == ActorState::DISPOSED)
411  return 0.f;
412 
413  Vector3 dir = getDirection();
414 
415  return atan2(dir.dotProduct(Vector3::UNIT_X), dir.dotProduct(-Vector3::UNIT_Z));
416 }
417 
418 Vector3 Actor::getDirection()
419 {
420  return ar_main_camera_dir_corr * this->GetCameraDir();
421 }
422 
423 Vector3 Actor::getPosition()
424 {
425  return m_avg_node_position; //the position is already in absolute position
426 }
427 
428 Ogre::Quaternion Actor::getOrientation()
429 {
430  Ogre::Vector3 localZ = ar_main_camera_dir_corr * -this->GetCameraDir();
431  Ogre::Vector3 localX = ar_main_camera_dir_corr * this->GetCameraRoll();
432  Ogre::Vector3 localY = localZ.crossProduct(localX);
433  return Ogre::Quaternion(localX, localY, localZ);
434 }
435 
436 void Actor::pushNetwork(char* data, int size)
437 {
438 #if USE_SOCKETW
439  NetUpdate update;
440 
441  update.veh_state.resize(sizeof(RoRnet::VehicleState));
442  update.node_data.resize(m_net_node_buf_size);
443  update.wheel_data.resize(ar_num_wheels * sizeof(float));
444 
445  // check if the size of the data matches to what we expected
446  if ((unsigned int)size == (m_net_total_buffer_size + sizeof(RoRnet::VehicleState)))
447  {
448  // we walk through the incoming data and separate it a bit
449  char* ptr = data;
450 
451  // put the RoRnet::VehicleState in front, describes actor basics, engine state, flares, etc
452  memcpy(update.veh_state.data(), ptr, sizeof(RoRnet::VehicleState));
453  ptr += sizeof(RoRnet::VehicleState);
454 
455  // then copy the node data
456  memcpy(update.node_data.data(), ptr, m_net_node_buf_size);
457  ptr += m_net_node_buf_size;
458 
459  // then take care of the wheel speeds
460  for (int i = 0; i < ar_num_wheels; i++)
461  {
462  float wspeed = *(float*)(ptr);
463  update.wheel_data[i] = wspeed;
464  ptr += sizeof(float);
465  }
466 
467  // then process the prop animation keys
468  for (size_t i = 0; i < m_prop_anim_key_states.size(); i++)
469  {
470  // Unpack bit array
471  char byte = *(ptr + (i / 8));
472  char mask = char(1) << (7 - (i % 8));
473  m_prop_anim_key_states[i].anim_active = (byte & mask);
474  }
475  }
476  else
477  {
478  if (!m_net_initialized)
479  {
480  // Update stream status (remote and local)
482  memset(&reg, 0, sizeof(RoRnet::StreamRegister));
483  reg.status = -2;
484  reg.origin_sourceid = ar_net_source_id;
485  reg.origin_streamid = ar_net_stream_id;
486  strncpy(reg.name, ar_filename.c_str(), 128);
488  sizeof(RoRnet::StreamRegister), (char *)&reg);
489  App::GetGameContext()->GetActorManager()->AddStreamMismatch(ar_net_source_id, ar_net_stream_id);
490 
491  // Inform the local player
492  RoRnet::UserInfo info;
493  App::GetNetwork()->GetUserInfo(reg.origin_sourceid, info);
494  Str<400> text;
495  text << info.username << _L(" content mismatch: ") << reg.name;
496  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING, text.ToCStr());
497 
498  // Remove self
499  ActorPtr self = App::GetGameContext()->GetActorManager()->GetActorById(ar_instance_id); // Get shared pointer to ourselves so references are added correctly.
501 
502  m_net_initialized = true;
503  }
504  RoR::LogFormat("[RoR|Network] Stream mismatch, filename: '%s'", ar_filename.c_str());
505  return;
506  }
507 
508  // Required to catch up when joining late (since the StreamRegister time stamp is received delayed)
509  if (!m_net_initialized)
510  {
511  RoRnet::VehicleState* oob = (RoRnet::VehicleState*)update.veh_state.data();
513  int rnow = std::max(0, tnow + App::GetGameContext()->GetActorManager()->GetNetTimeOffset(ar_net_source_id));
514  if (oob->time > rnow + 100)
515  {
516  App::GetGameContext()->GetActorManager()->UpdateNetTimeOffset(ar_net_source_id, oob->time - rnow);
517  }
518  }
519 
520  m_net_updates.push_back(update);
521 #endif // USE_SOCKETW
522 }
523 
524 void Actor::calcNetwork()
525 {
526  using namespace RoRnet;
527 
528  if (m_net_updates.size() < 2)
529  return;
530 
532  int rnow = std::max(0, tnow + App::GetGameContext()->GetActorManager()->GetNetTimeOffset(ar_net_source_id));
533 
534  // Find index offset into the stream data for the current time
535  int index_offset = 0;
536  for (int i = 0; i < m_net_updates.size() - 1; i++)
537  {
538  VehicleState* oob = (VehicleState*)m_net_updates[i].veh_state.data();
539  if (oob->time > rnow)
540  break;
541  index_offset = i;
542  }
543 
544  VehicleState* oob1 = (VehicleState*)m_net_updates[index_offset ].veh_state.data();
545  VehicleState* oob2 = (VehicleState*)m_net_updates[index_offset + 1].veh_state.data();
546  char* netb1 = (char*) m_net_updates[index_offset ].node_data.data();
547  char* netb2 = (char*) m_net_updates[index_offset + 1].node_data.data();
548  float* net_rp1 = (float*) m_net_updates[index_offset ].wheel_data.data();
549  float* net_rp2 = (float*) m_net_updates[index_offset + 1].wheel_data.data();
550 
551  float tratio = (float)(rnow - oob1->time) / (float)(oob2->time - oob1->time);
552 
553  if (tratio > 4.0f)
554  {
555  m_net_updates.clear();
556  return; // Wait for new data
557  }
558  else if (tratio > 1.0f)
559  {
560  App::GetGameContext()->GetActorManager()->UpdateNetTimeOffset(ar_net_source_id, -std::pow(2, tratio));
561  }
562  else if (index_offset == 0 && (m_net_updates.size() > 5 || (tratio < 0.125f && m_net_updates.size() > 2)))
563  {
564  App::GetGameContext()->GetActorManager()->UpdateNetTimeOffset(ar_net_source_id, +1);
565  }
566 
567  short* sp1 = (short*)(netb1 + sizeof(float) * 3);
568  short* sp2 = (short*)(netb2 + sizeof(float) * 3);
569  Vector3 p1ref = Vector3::ZERO;
570  Vector3 p2ref = Vector3::ZERO;
571  Vector3 p1 = Vector3::ZERO;
572  Vector3 p2 = Vector3::ZERO;
573 
574  for (int i = 0; i < m_net_first_wheel_node; i++)
575  {
576  if (i == 0)
577  {
578  // first node is uncompressed
579  p1.x = ((float*)netb1)[0];
580  p1.y = ((float*)netb1)[1];
581  p1.z = ((float*)netb1)[2];
582  p1ref = p1;
583 
584  p2.x = ((float*)netb2)[0];
585  p2.y = ((float*)netb2)[1];
586  p2.z = ((float*)netb2)[2];
587  p2ref = p2;
588  }
589  else
590  {
591  // all other nodes are compressed:
592  // short int compared to previous node
593  p1.x = (float)(sp1[(i - 1) * 3 + 0]) / m_net_node_compression;
594  p1.y = (float)(sp1[(i - 1) * 3 + 1]) / m_net_node_compression;
595  p1.z = (float)(sp1[(i - 1) * 3 + 2]) / m_net_node_compression;
596  p1 = p1 + p1ref;
597 
598  p2.x = (float)(sp2[(i - 1) * 3 + 0]) / m_net_node_compression;
599  p2.y = (float)(sp2[(i - 1) * 3 + 1]) / m_net_node_compression;
600  p2.z = (float)(sp2[(i - 1) * 3 + 2]) / m_net_node_compression;
601  p2 = p2 + p2ref;
602  }
603 
604  // linear interpolation
605  ar_nodes[i].AbsPosition = p1 + tratio * (p2 - p1);
606  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
607  ar_nodes[i].Velocity = (p2 - p1) * 1000.0f / (float)(oob2->time - oob1->time);
608  }
609 
610  for (int i = 0; i < ar_num_wheels; i++)
611  {
612  float rp = net_rp1[i] + tratio * (net_rp2[i] - net_rp1[i]);
613  //compute ideal positions
614  Vector3 axis = ar_wheels[i].wh_axis_node_1->RelPosition - ar_wheels[i].wh_axis_node_0->RelPosition;
615  axis.normalise();
616  Plane pplan = Plane(axis, ar_wheels[i].wh_axis_node_0->AbsPosition);
617  Vector3 ortho = -pplan.projectVector(ar_wheels[i].wh_near_attach_node->AbsPosition) - ar_wheels[i].wh_axis_node_0->AbsPosition;
618  Vector3 ray = ortho.crossProduct(axis);
619  ray.normalise();
620  ray *= ar_wheels[i].wh_radius;
621  float drp = Math::TWO_PI / (ar_wheels[i].wh_num_nodes / 2);
622  for (int j = 0; j < ar_wheels[i].wh_num_nodes / 2; j++)
623  {
624  Vector3 uray = Quaternion(Radian(rp - drp * j), axis) * ray;
625 
626  ar_wheels[i].wh_nodes[j * 2 + 0]->AbsPosition = ar_wheels[i].wh_axis_node_0->AbsPosition + uray;
627  ar_wheels[i].wh_nodes[j * 2 + 0]->RelPosition = ar_wheels[i].wh_nodes[j * 2]->AbsPosition - ar_origin;
628 
629  ar_wheels[i].wh_nodes[j * 2 + 1]->AbsPosition = ar_wheels[i].wh_axis_node_1->AbsPosition + uray;
630  ar_wheels[i].wh_nodes[j * 2 + 1]->RelPosition = ar_wheels[i].wh_nodes[j * 2 + 1]->AbsPosition - ar_origin;
631  }
632  ray.normalise();
633  ray *= ar_wheels[i].wh_rim_radius;
634  for (int j = 0; j < ar_wheels[i].wh_num_rim_nodes / 2; j++)
635  {
636  Vector3 uray = Quaternion(Radian(rp - drp * j), axis) * ray;
637 
638  ar_wheels[i].wh_rim_nodes[j * 2 + 0]->AbsPosition = ar_wheels[i].wh_axis_node_0->AbsPosition + uray;
639  ar_wheels[i].wh_rim_nodes[j * 2 + 0]->RelPosition = ar_wheels[i].wh_rim_nodes[j * 2]->AbsPosition - ar_origin;
640 
641  ar_wheels[i].wh_rim_nodes[j * 2 + 1]->AbsPosition = ar_wheels[i].wh_axis_node_1->AbsPosition + uray;
642  ar_wheels[i].wh_rim_nodes[j * 2 + 1]->RelPosition = ar_wheels[i].wh_rim_nodes[j * 2 + 1]->AbsPosition - ar_origin;
643  }
644  }
645  this->UpdateBoundingBoxes();
646  this->calculateAveragePosition();
647 
648  float engspeed = oob1->engine_speed + tratio * (oob2->engine_speed - oob1->engine_speed);
649  float engforce = oob1->engine_force + tratio * (oob2->engine_force - oob1->engine_force);
650  float engclutch = oob1->engine_clutch + tratio * (oob2->engine_clutch - oob1->engine_clutch);
651  float netwspeed = oob1->wheelspeed + tratio * (oob2->wheelspeed - oob1->wheelspeed);
652  float netbrake = oob1->brake + tratio * (oob2->brake - oob1->brake);
653 
654  ar_hydro_dir_wheel_display = oob1->hydrodirstate;
655  ar_wheel_speed = netwspeed;
656 
657  int gear = oob1->engine_gear;
658  const BitMask_t flagmask = oob1->flagmask;
659 
660  if (ar_engine)
661  {
662  SOUND_MODULATE(ar_instance_id, SS_MOD_ENGINE, engspeed);
663  SOUND_MODULATE(ar_instance_id, SS_MOD_INJECTOR, engforce);
664  }
665  if (ar_num_aeroengines > 0)
666  {
667  SOUND_MODULATE(ar_instance_id, SS_MOD_AEROENGINE1, engspeed);
668  SOUND_MODULATE(ar_instance_id, SS_MOD_AEROENGINE2, engspeed);
669  SOUND_MODULATE(ar_instance_id, SS_MOD_AEROENGINE3, engspeed);
670  SOUND_MODULATE(ar_instance_id, SS_MOD_AEROENGINE4, engspeed);
671  }
672 
673  ar_brake = netbrake;
674 
675  if (ar_engine)
676  {
677  int automode = -1;
678  if ((flagmask & NETMASK_ENGINE_MODE_AUTOMATIC) != 0) { automode = static_cast<int>(SimGearboxMode::AUTO); }
679  else if ((flagmask & NETMASK_ENGINE_MODE_SEMIAUTO) != 0) { automode = static_cast<int>(SimGearboxMode::SEMI_AUTO); }
680  else if ((flagmask & NETMASK_ENGINE_MODE_MANUAL) != 0) { automode = static_cast<int>(SimGearboxMode::MANUAL); }
681  else if ((flagmask & NETMASK_ENGINE_MODE_MANUAL_STICK) != 0) { automode = static_cast<int>(SimGearboxMode::MANUAL_STICK); }
682  else if ((flagmask & NETMASK_ENGINE_MODE_MANUAL_RANGES) != 0) { automode = static_cast<int>(SimGearboxMode::MANUAL_RANGES); }
683 
684  bool contact = ((flagmask & NETMASK_ENGINE_CONT) != 0);
685  bool running = ((flagmask & NETMASK_ENGINE_RUN) != 0);
686 
687  ar_engine->PushNetworkState(engspeed, engforce, engclutch, gear, running, contact, automode);
688  }
689 
690  // set particle cannon
691  if (((flagmask & NETMASK_PARTICLE) != 0) != m_custom_particles_enabled)
692  toggleCustomParticles();
693 
694  m_antilockbrake = flagmask & NETMASK_ALB_ACTIVE;
695  m_tractioncontrol = flagmask & NETMASK_TC_ACTIVE;
696  ar_parking_brake = flagmask & NETMASK_PBRAKE;
697 
698  this->setLightStateMask(oob1->lightmask);
699 
700  if ((flagmask & NETMASK_HORN))
701  SOUND_START(ar_instance_id, SS_TRIG_HORN);
702  else
703  SOUND_STOP(ar_instance_id, SS_TRIG_HORN);
704 
705  if ((oob1->lightmask & RoRnet::LIGHTMASK_REVERSE) && ar_engine && ar_engine->isRunning())
706  SOUND_START(ar_instance_id, SS_TRIG_REVERSE_GEAR);
707  else
708  SOUND_STOP(ar_instance_id, SS_TRIG_REVERSE_GEAR);
709 
710  for (int i = 0; i < index_offset; i++)
711  {
712  m_net_updates.pop_front();
713  }
714 
715  m_net_initialized = true;
716 }
717 
718 void Actor::RecalculateNodeMasses(Real total)
719 {
720  //reset
721  for (int i = 0; i < ar_num_nodes; i++)
722  {
723  if (!ar_nodes[i].nd_tyre_node)
724  {
725  if (!ar_nodes[i].nd_loaded_mass)
726  {
727  ar_nodes[i].mass = 0;
728  }
729  else if (!ar_nodes[i].nd_override_mass)
730  {
731  ar_nodes[i].mass = m_load_mass / (float)m_masscount;
732  }
733  }
734  }
735  //average linear density
736  Real len = 0.0f;
737  for (int i = 0; i < ar_num_beams; i++)
738  {
739  if (ar_beams[i].bm_type != BEAM_VIRTUAL)
740  {
741  Real half_newlen = ar_beams[i].L / 2.0;
742  if (!ar_beams[i].p1->nd_tyre_node)
743  len += half_newlen;
744  if (!ar_beams[i].p2->nd_tyre_node)
745  len += half_newlen;
746  }
747  }
748 
749  for (int i = 0; i < ar_num_beams; i++)
750  {
751  if (ar_beams[i].bm_type != BEAM_VIRTUAL)
752  {
753  Real half_mass = ar_beams[i].L * total / len / 2.0f;
754  if (!ar_beams[i].p1->nd_tyre_node)
755  ar_beams[i].p1->mass += half_mass;
756  if (!ar_beams[i].p2->nd_tyre_node)
757  ar_beams[i].p2->mass += half_mass;
758  }
759  }
760  //fix rope masses
761  for (std::vector<rope_t>::iterator it = ar_ropes.begin(); it != ar_ropes.end(); it++)
762  {
763  it->rp_beam->p2->mass = 100.0f;
764  }
765 
766  // Apply pre-defined cinecam node mass
767  for (int i = 0; i < this->ar_num_cinecams; ++i)
768  {
769  // TODO: this expects all cinecams to be defined in root module (i.e. outside 'section/end_section')
770  ar_nodes[ar_cinecam_node[i]].mass = m_definition->root_module->cinecam[i].node_mass;
771  }
772 
773  //update mass
774  for (int i = 0; i < ar_num_nodes; i++)
775  {
776  if (!ar_nodes[i].nd_tyre_node &&
777  !(ar_minimass_skip_loaded_nodes && ar_nodes[i].nd_loaded_mass) &&
778  ar_nodes[i].mass < ar_minimass[i])
779  {
780  if (App::diag_truck_mass->getBool())
781  {
782  char buf[300];
783  snprintf(buf, 300, "Node '%d' mass (%f Kg) is too light. Resetting to 'minimass' (%f Kg)", i, ar_nodes[i].mass, ar_minimass[i]);
784  LOG(buf);
785  }
786  ar_nodes[i].mass = ar_minimass[i];
787  }
788  }
789 
790  m_total_mass = 0;
791  for (int i = 0; i < ar_num_nodes; i++)
792  {
793  if (App::diag_truck_mass->getBool())
794  {
795  String msg = "Node " + TOSTRING(i) + " : " + TOSTRING((int)ar_nodes[i].mass) + " kg";
796  if (ar_nodes[i].nd_loaded_mass)
797  {
798  if (ar_nodes[i].nd_override_mass)
799  msg += " (overriden by node mass)";
800  else
801  msg += " (normal load node: " + TOSTRING(m_load_mass) + " kg / " + TOSTRING(m_masscount) + " nodes)";
802  }
803  LOG(msg);
804  }
805  m_total_mass += ar_nodes[i].mass;
806  }
807  LOG("TOTAL VEHICLE MASS: " + TOSTRING((int)m_total_mass) +" kg");
808 }
809 
810 float Actor::getTotalMass(bool withLocked)
811 {
812  if (ar_state == ActorState::DISPOSED)
813  return 0.f;
814 
815  if (!withLocked)
816  return m_total_mass; // already computed in RecalculateNodeMasses
817 
818  float mass = m_total_mass;
819 
820  for (ActorPtr& actor : ar_linked_actors)
821  {
822  mass += actor->m_total_mass;
823  }
824 
825  return mass;
826 }
827 
828 void Actor::DetermineLinkedActors()
829 {
830  // BEWARE: `ar_linked_actors` includes both direct and indirect links!
831  // --------------------------------------------------------------------------------------------------
832 
833  ar_linked_actors.clear();
834 
835  bool found = true;
836  std::map<ActorPtr, bool> lookup_table; // tracks visited actors
837 
838  lookup_table.insert(std::pair<ActorPtr, bool>(this, false));
839 
840  while (found)
841  {
842  found = false;
843 
844  for (auto it_actor = lookup_table.begin(); it_actor != lookup_table.end(); ++it_actor)
845  {
846  if (!it_actor->second) // not visited yet?
847  {
848  ActorPtr actor = it_actor->first;
849  for (auto inter_actor_link: App::GetGameContext()->GetActorManager()->inter_actor_links)
850  {
851  auto actor_pair = inter_actor_link.second;
852  if (actor == actor_pair.first || actor == actor_pair.second)
853  {
854  auto other_actor = (actor != actor_pair.first) ? actor_pair.first : actor_pair.second;
855  auto ret = lookup_table.insert(std::pair<ActorPtr, bool>(other_actor, false));
856  if (ret.second)
857  {
858  ar_linked_actors.push_back(other_actor);
859  found = true;
860  }
861  }
862  }
863  it_actor->second = true; // mark visited
864  }
865  }
866  }
867 }
868 
869 int Actor::getWheelNodeCount() const
870 {
871  return m_wheel_node_count;
872 }
873 
874 void Actor::calcNodeConnectivityGraph()
875 {
876  int i;
877 
878  ar_node_to_node_connections.resize(ar_num_nodes, std::vector<int>());
879  ar_node_to_beam_connections.resize(ar_num_nodes, std::vector<int>());
880 
881  for (i = 0; i < ar_num_beams; i++)
882  {
883  if (ar_beams[i].p1 != NULL && ar_beams[i].p2 != NULL && ar_beams[i].p1->pos >= 0 && ar_beams[i].p2->pos >= 0)
884  {
885  ar_node_to_node_connections[ar_beams[i].p1->pos].push_back(ar_beams[i].p2->pos);
886  ar_node_to_beam_connections[ar_beams[i].p1->pos].push_back(i);
887  ar_node_to_node_connections[ar_beams[i].p2->pos].push_back(ar_beams[i].p1->pos);
888  ar_node_to_beam_connections[ar_beams[i].p2->pos].push_back(i);
889  }
890  }
891 }
892 
893 bool Actor::Intersects(ActorPtr actor, Vector3 offset)
894 {
895  Vector3 bb_min = ar_bounding_box.getMinimum() + offset;
896  Vector3 bb_max = ar_bounding_box.getMaximum() + offset;
897  AxisAlignedBox bb = AxisAlignedBox(bb_min, bb_max);
898 
899  if (!bb.intersects(actor->ar_bounding_box))
900  return false;
901 
902  // Test own (contactable) beams against others cabs
903  for (int i = 0; i < ar_num_beams; i++)
904  {
905  if (!(ar_beams[i].p1->nd_contacter || ar_beams[i].p1->nd_contactable) ||
906  !(ar_beams[i].p2->nd_contacter || ar_beams[i].p2->nd_contactable))
907  continue;
908 
909  Vector3 origin = ar_beams[i].p1->AbsPosition + offset;
910  Vector3 target = ar_beams[i].p2->AbsPosition + offset;
911 
912  Ray ray(origin, target - origin);
913 
914  for (int j = 0; j < actor->ar_num_collcabs; j++)
915  {
916  int index = actor->ar_collcabs[j] * 3;
917  Vector3 a = actor->ar_nodes[actor->ar_cabs[index + 0]].AbsPosition;
918  Vector3 b = actor->ar_nodes[actor->ar_cabs[index + 1]].AbsPosition;
919  Vector3 c = actor->ar_nodes[actor->ar_cabs[index + 2]].AbsPosition;
920 
921  auto result = Ogre::Math::intersects(ray, a, b, c);
922  if (result.first && result.second < 1.0f)
923  {
924  return true;
925  }
926  }
927  }
928 
929  // Test own cabs against others (contactable) beams
930  for (int i = 0; i < actor->ar_num_beams; i++)
931  {
932  if (!(actor->ar_beams[i].p1->nd_contacter || actor->ar_beams[i].p1->nd_contactable) ||
933  !(actor->ar_beams[i].p2->nd_contacter || actor->ar_beams[i].p2->nd_contactable))
934  continue;
935 
936  Vector3 origin = actor->ar_beams[i].p1->AbsPosition;
937  Vector3 target = actor->ar_beams[i].p2->AbsPosition;
938 
939  Ray ray(origin, target - origin);
940 
941  for (int j = 0; j < ar_num_collcabs; j++)
942  {
943  int index = ar_collcabs[j] * 3;
944  Vector3 a = ar_nodes[ar_cabs[index + 0]].AbsPosition + offset;
945  Vector3 b = ar_nodes[ar_cabs[index + 1]].AbsPosition + offset;
946  Vector3 c = ar_nodes[ar_cabs[index + 2]].AbsPosition + offset;
947 
948  auto result = Ogre::Math::intersects(ray, a, b, c);
949  if (result.first && result.second < 1.0f)
950  {
951  return true;
952  }
953  }
954  }
955 
956  return false;
957 }
958 
959 Vector3 Actor::calculateCollisionOffset(Vector3 direction)
960 {
961  if (direction == Vector3::ZERO)
962  return Vector3::ZERO;
963 
964  Real max_distance = direction.normalise();
965 
966  // collision displacement
967  Vector3 collision_offset = Vector3::ZERO;
968 
969  while (collision_offset.length() < max_distance)
970  {
971  Vector3 bb_min = ar_bounding_box.getMinimum() + collision_offset;
972  Vector3 bb_max = ar_bounding_box.getMaximum() + collision_offset;
973  AxisAlignedBox bb = AxisAlignedBox(bb_min, bb_max);
974 
975  bool collision = false;
976 
977  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
978  {
979  if (actor == this)
980  continue;
981  if (!bb.intersects(actor->ar_bounding_box))
982  continue;
983 
984  // Test own contactables against others cabs
985  if (m_intra_point_col_detector)
986  {
987  for (int i = 0; i < actor->ar_num_collcabs; i++)
988  {
989  int tmpv = actor->ar_collcabs[i] * 3;
990  node_t* no = &actor->ar_nodes[actor->ar_cabs[tmpv]];
991  node_t* na = &actor->ar_nodes[actor->ar_cabs[tmpv + 1]];
992  node_t* nb = &actor->ar_nodes[actor->ar_cabs[tmpv + 2]];
993 
994  m_intra_point_col_detector->query(no->AbsPosition - collision_offset,
995  na->AbsPosition - collision_offset,
996  nb->AbsPosition - collision_offset,
997  actor->ar_collision_range * 3.0f);
998 
999  if (collision = !m_intra_point_col_detector->hit_list.empty())
1000  break;
1001  }
1002 
1003  if (collision)
1004  break;
1005  }
1006 
1007  float proximity = std::max(.05f, std::sqrt(std::max(m_min_camera_radius, actor->m_min_camera_radius)) / 50.f);
1008 
1009  // Test proximity of own nodes against others nodes
1010  for (int i = 0; i < ar_num_nodes; i++)
1011  {
1012  if (!ar_nodes[i].nd_contacter && !ar_nodes[i].nd_contactable)
1013  continue;
1014 
1015  Vector3 query_position = ar_nodes[i].AbsPosition + collision_offset;
1016 
1017  for (int j = 0; j < actor->ar_num_nodes; j++)
1018  {
1019  if (!actor->ar_nodes[j].nd_contacter && !actor->ar_nodes[j].nd_contactable)
1020  continue;
1021 
1022  if (collision = query_position.squaredDistance(actor->ar_nodes[j].AbsPosition) < proximity)
1023  break;
1024  }
1025 
1026  if (collision)
1027  break;
1028  }
1029 
1030  if (collision)
1031  break;
1032  }
1033 
1034  // Test own cabs against others contacters
1035  if (!collision && m_inter_point_col_detector)
1036  {
1037  for (int i = 0; i < ar_num_collcabs; i++)
1038  {
1039  int tmpv = ar_collcabs[i] * 3;
1040  node_t* no = &ar_nodes[ar_cabs[tmpv]];
1041  node_t* na = &ar_nodes[ar_cabs[tmpv + 1]];
1042  node_t* nb = &ar_nodes[ar_cabs[tmpv + 2]];
1043 
1044  m_inter_point_col_detector->query(no->AbsPosition + collision_offset,
1045  na->AbsPosition + collision_offset,
1046  nb->AbsPosition + collision_offset,
1047  ar_collision_range * 3.0f);
1048 
1049  if (collision = !m_inter_point_col_detector->hit_list.empty())
1050  break;
1051  }
1052  }
1053 
1054  // Test beams (between contactable nodes) against cabs
1055  if (!collision)
1056  {
1057  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
1058  {
1059  if (actor == this)
1060  continue;
1061  if (collision = this->Intersects(actor, collision_offset))
1062  break;
1063  }
1064  }
1065 
1066  if (!collision)
1067  break;
1068 
1069  collision_offset += direction * 0.05f;
1070  }
1071 
1072  return collision_offset;
1073 }
1074 
1075 void Actor::resolveCollisions(Ogre::Vector3 direction)
1076 {
1077  if (m_intra_point_col_detector)
1078  m_intra_point_col_detector->UpdateIntraPoint(true);
1079 
1080  if (m_inter_point_col_detector)
1081  m_inter_point_col_detector->UpdateInterPoint(true);
1082 
1083  Vector3 offset = calculateCollisionOffset(direction);
1084 
1085  if (offset == Vector3::ZERO)
1086  return;
1087 
1088  // Additional 20 cm safe-guard (horizontally)
1089  offset += 0.2f * Vector3(offset.x, 0.0f, offset.z).normalisedCopy();
1090 
1091  resetPosition(ar_nodes[0].AbsPosition.x + offset.x, ar_nodes[0].AbsPosition.z + offset.z, false, this->getMinHeight() + offset.y);
1092 }
1093 
1094 void Actor::resolveCollisions(float max_distance, bool consider_up)
1095 {
1096  if (m_intra_point_col_detector)
1097  m_intra_point_col_detector->UpdateIntraPoint(true);
1098 
1099  if (m_inter_point_col_detector)
1100  m_inter_point_col_detector->UpdateInterPoint(true);
1101 
1102  Vector3 u = Vector3::UNIT_Y;
1103  Vector3 f = Vector3(getDirection().x, 0.0f, getDirection().z).normalisedCopy();
1104  Vector3 l = u.crossProduct(f);
1105 
1106  // Calculate an ideal collision avoidance direction (prefer left over right over [front / back / up])
1107  Vector3 left = calculateCollisionOffset(+l * max_distance);
1108  Vector3 right = calculateCollisionOffset(-l * left.length());
1109  Vector3 lateral = left.length() < right.length() * 1.1f ? left : right;
1110 
1111  Vector3 front = calculateCollisionOffset(+f * lateral.length());
1112  Vector3 back = calculateCollisionOffset(-f * front.length());
1113  Vector3 sagittal = front.length() < back.length() * 1.1f ? front : back;
1114 
1115  Vector3 offset = lateral.length() < sagittal.length() * 1.2f ? lateral : sagittal;
1116 
1117  if (consider_up)
1118  {
1119  Vector3 up = calculateCollisionOffset(+u * offset.length());
1120  if (up.length() * 1.2f < offset.length())
1121  offset = up;
1122  }
1123 
1124  if (offset == Vector3::ZERO)
1125  return;
1126 
1127  // Additional 20 cm safe-guard (horizontally)
1128  offset += 0.2f * Vector3(offset.x, 0.0f, offset.z).normalisedCopy();
1129 
1130  resetPosition(ar_nodes[0].AbsPosition.x + offset.x, ar_nodes[0].AbsPosition.z + offset.z, true, this->getMinHeight() + offset.y);
1131 }
1132 
1133 void Actor::calculateAveragePosition()
1134 {
1135  // calculate average position
1136  if (ar_custom_camera_node != NODENUM_INVALID)
1137  {
1138  m_avg_node_position = ar_nodes[ar_custom_camera_node].AbsPosition;
1139  }
1140  else if (ar_extern_camera_mode == ExtCameraMode::CINECAM && ar_num_cinecams > 0)
1141  {
1142  // the new (strange) approach: reuse the cinecam node
1143  m_avg_node_position = ar_nodes[ar_cinecam_node[0]].AbsPosition;
1144  }
1145  else if (ar_extern_camera_mode == ExtCameraMode::NODE && ar_extern_camera_node != NODENUM_INVALID)
1146  {
1147  // the new (strange) approach #2: reuse a specified node
1148  m_avg_node_position = ar_nodes[ar_extern_camera_node].AbsPosition;
1149  }
1150  else
1151  {
1152  // the classic approach: average over all nodes and beams
1153  Vector3 aposition = Vector3::ZERO;
1154  for (int n = 0; n < ar_num_nodes; n++)
1155  {
1156  aposition += ar_nodes[n].AbsPosition;
1157  }
1158  m_avg_node_position = aposition / ar_num_nodes;
1159  }
1160 }
1161 
1162 inline void PadBoundingBox(Ogre::AxisAlignedBox& box) // Internal helper
1163 {
1164  box.setMinimum(box.getMinimum() - BOUNDING_BOX_PADDING);
1165  box.setMaximum(box.getMaximum() + BOUNDING_BOX_PADDING);
1166 }
1167 
1168 void Actor::UpdateBoundingBoxes()
1169 {
1170  // Reset
1171  ar_bounding_box = AxisAlignedBox::BOX_NULL;
1172  ar_predicted_bounding_box = AxisAlignedBox::BOX_NULL;
1173  ar_evboxes_bounding_box = AxisAlignedBox::BOX_NULL;
1174  for (size_t i = 0; i < ar_collision_bounding_boxes.size(); ++i)
1175  {
1176  ar_collision_bounding_boxes[i] = AxisAlignedBox::BOX_NULL;
1177  ar_predicted_coll_bounding_boxes[i] = AxisAlignedBox::BOX_NULL;
1178  }
1179 
1180  // To avoid performance choking by overstretched bounding box (happens when vehicle drops some nodes),
1181  // we set a maximum distance limit from the main camera.
1182  const float CABNODE_MAX_CAMDIST = 15.f;
1183  const Ogre::Vector3 mainCamPos = ar_nodes[ar_main_camera_node_pos].RelPosition;
1184 
1185  // Update
1186  for (int i = 0; i < ar_num_nodes; i++)
1187  {
1188  Vector3 vel = ar_nodes[i].Velocity;
1189  Vector3 pos = ar_nodes[i].AbsPosition;
1190  int16_t cid = ar_nodes[i].nd_coll_bbox_id;
1191 
1192  ar_bounding_box.merge(pos); // Current box
1193  if (mainCamPos.squaredDistance(ar_nodes[i].RelPosition) < (CABNODE_MAX_CAMDIST*CABNODE_MAX_CAMDIST))
1194  {
1195  ar_evboxes_bounding_box.merge(pos);
1196  }
1197  ar_predicted_bounding_box.merge(pos); // Predicted box (current position)
1198  ar_predicted_bounding_box.merge(pos + vel); // Predicted box (future position)
1199  if (cid != node_t::INVALID_BBOX)
1200  {
1201  ar_collision_bounding_boxes[cid].merge(pos);
1202  ar_predicted_coll_bounding_boxes[cid].merge(pos);
1203  ar_predicted_coll_bounding_boxes[cid].merge(pos + vel);
1204  }
1205  }
1206 
1207  // Finalize - add padding
1208  PadBoundingBox(ar_bounding_box);
1209  PadBoundingBox(ar_predicted_bounding_box);
1210  for (size_t i = 0; i < ar_collision_bounding_boxes.size(); ++i)
1211  {
1212  PadBoundingBox(ar_collision_bounding_boxes[i]);
1213  PadBoundingBox(ar_predicted_coll_bounding_boxes[i]);
1214  }
1215 }
1216 
1217 void Actor::UpdatePhysicsOrigin()
1218 {
1219  if (ar_nodes[0].RelPosition.squaredLength() > 10000.0)
1220  {
1221  Vector3 offset = ar_nodes[0].RelPosition;
1222  ar_origin += offset;
1223  for (int i = 0; i < ar_num_nodes; i++)
1224  {
1225  ar_nodes[i].RelPosition -= offset;
1226  }
1227  }
1228 }
1229 
1230 void Actor::ResetAngle(float rot)
1231 {
1232  // Set origin of rotation to camera node
1233  Vector3 origin = ar_nodes[ar_main_camera_node_pos].AbsPosition;
1234 
1235  // Set up matrix for yaw rotation
1236  Matrix3 matrix;
1237  matrix.FromEulerAnglesXYZ(Radian(0), Radian(-rot + m_spawn_rotation), Radian(0));
1238 
1239  for (int i = 0; i < ar_num_nodes; i++)
1240  {
1241  // Move node back to origin, apply rotation matrix, and move node back
1242  ar_nodes[i].AbsPosition -= origin;
1243  ar_nodes[i].AbsPosition = matrix * ar_nodes[i].AbsPosition;
1244  ar_nodes[i].AbsPosition += origin;
1245  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - this->ar_origin;
1246  }
1247 
1248  this->UpdateBoundingBoxes();
1249  calculateAveragePosition();
1250 }
1251 
1252 void Actor::updateInitPosition()
1253 {
1254  for (int i = 0; i < ar_num_nodes; i++)
1255  {
1256  ar_initial_node_positions[i] = ar_nodes[i].AbsPosition;
1257  }
1258 }
1259 
1260 void Actor::resetPosition(float px, float pz, bool setInitPosition, float miny)
1261 {
1262  // horizontal displacement
1263  Vector3 offset = Vector3(px, ar_nodes[0].AbsPosition.y, pz) - ar_nodes[0].AbsPosition;
1264  for (int i = 0; i < ar_num_nodes; i++)
1265  {
1266  ar_nodes[i].AbsPosition += offset;
1267  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1268  }
1269 
1270  // vertical displacement
1271  float vertical_offset = miny - this->getMinHeight();
1272  if (App::GetGameContext()->GetTerrain()->getWater())
1273  {
1274  vertical_offset += std::max(0.0f, App::GetGameContext()->GetTerrain()->getWater()->GetStaticWaterHeight() - miny);
1275  }
1276  for (int i = 1; i < ar_num_nodes; i++)
1277  {
1278  if (ar_nodes[i].nd_no_ground_contact)
1279  continue;
1280  float terrainHeight = App::GetGameContext()->GetTerrain()->GetHeightAt(ar_nodes[i].AbsPosition.x, ar_nodes[i].AbsPosition.z);
1281  vertical_offset += std::max(0.0f, terrainHeight - (ar_nodes[i].AbsPosition.y + vertical_offset));
1282  }
1283  for (int i = 0; i < ar_num_nodes; i++)
1284  {
1285  ar_nodes[i].AbsPosition.y += vertical_offset;
1286  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1287  }
1288 
1289  // mesh displacement
1290  float mesh_offset = 0.0f;
1291  for (int i = 0; i < ar_num_nodes; i++)
1292  {
1293  if (mesh_offset >= 1.0f)
1294  break;
1295  if (ar_nodes[i].nd_no_ground_contact)
1296  continue;
1297  float offset = mesh_offset;
1298  while (offset < 1.0f)
1299  {
1300  Vector3 query = ar_nodes[i].AbsPosition + Vector3(0.0f, offset, 0.0f);
1301  if (!App::GetGameContext()->GetTerrain()->GetCollisions()->collisionCorrect(&query, false))
1302  {
1303  mesh_offset = offset;
1304  break;
1305  }
1306  offset += 0.001f;
1307  }
1308  }
1309  for (int i = 0; i < ar_num_nodes; i++)
1310  {
1311  ar_nodes[i].AbsPosition.y += mesh_offset;
1312  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1313  }
1314 
1315  resetPosition(Vector3::ZERO, setInitPosition);
1316 }
1317 
1318 void Actor::resetPosition(Ogre::Vector3 translation, bool setInitPosition)
1319 {
1320  // total displacement
1321  if (translation != Vector3::ZERO)
1322  {
1323  Vector3 offset = translation - ar_nodes[0].AbsPosition;
1324  for (int i = 0; i < ar_num_nodes; i++)
1325  {
1326  ar_nodes[i].AbsPosition += offset;
1327  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1328  }
1329  }
1330 
1331  if (setInitPosition)
1332  {
1333  for (int i = 0; i < ar_num_nodes; i++)
1334  {
1335  ar_initial_node_positions[i] = ar_nodes[i].AbsPosition;
1336  }
1337  }
1338 
1339  this->UpdateBoundingBoxes();
1340  calculateAveragePosition();
1341 }
1342 
1343 void Actor::mouseMove(NodeNum_t node, Vector3 pos, float force)
1344 {
1345  m_mouse_grab_node = node;
1346  m_mouse_grab_move_force = force * std::pow(m_total_mass / 3000.0f, 0.75f);
1347  m_mouse_grab_pos = pos;
1348 }
1349 
1350 void Actor::toggleWheelDiffMode()
1351 {
1352  for (int i = 0; i < m_num_wheel_diffs; ++i)
1353  {
1354  m_wheel_diffs[i]->ToggleDifferentialMode();
1355  }
1356 }
1357 
1358 void Actor::toggleAxleDiffMode()
1359 {
1360  for (int i = 0; i < m_num_axle_diffs; ++i)
1361  {
1362  m_axle_diffs[i]->ToggleDifferentialMode();
1363  }
1364 }
1365 
1366 void Actor::displayAxleDiffMode()
1367 {
1368  if (m_num_axle_diffs == 0)
1369  {
1370  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1371  _L("No inter-axle differential installed on current vehicle!"), "error.png");
1372  }
1373  else
1374  {
1375  String message = "";
1376  for (int i = 0; i < m_num_axle_diffs; ++i)
1377  {
1378  if (m_axle_diffs[i])
1379  {
1380  if (i > 0)
1381  message += "\n";
1382 
1383  int a1 = m_axle_diffs[i]->di_idx_1 + 1;
1384  int a2 = m_axle_diffs[i]->di_idx_2 + 1;
1385  message += _L("Axle ") + TOSTRING(a1) + " <--> " + _L("Axle ") + TOSTRING(a2) + ": ";
1386  message += m_axle_diffs[i]->GetDifferentialTypeName();
1387  }
1388  }
1389  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1390  "Inter-axle differentials:\n" + message, "cog.png");
1391  }
1392 }
1393 
1394 void Actor::displayWheelDiffMode()
1395 {
1396  if (m_num_wheel_diffs == 0)
1397  {
1398  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1399  _L("No inter-wheel differential installed on current vehicle!"), "error.png");
1400  }
1401  else
1402  {
1403  String message = "";
1404  for (int i = 0; i < m_num_wheel_diffs; ++i)
1405  {
1406  if (m_wheel_diffs[i])
1407  {
1408  if (i > 0)
1409  message += "\n";
1410 
1411  message += _L("Axle ") + TOSTRING(i + 1) + ": ";
1412  message += m_wheel_diffs[i]->GetDifferentialTypeName();
1413  }
1414  }
1415  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1416  "Inter-wheel differentials:\n" + message, "cog.png");
1417  }
1418 }
1419 
1420 void Actor::displayTransferCaseMode()
1421 {
1422  if (m_transfer_case)
1423  {
1424  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1425  _L("Transfercase switched to: ") + this->getTransferCaseName(), "cog.png");
1426  }
1427  else
1428  {
1429  App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_NOTICE,
1430  _L("No transfercase installed on current vehicle!"), "error.png");
1431  }
1432 }
1433 
1434 void Actor::toggleTransferCaseMode()
1435 {
1436  if (!ar_engine || !m_transfer_case || m_transfer_case->tr_ax_2 < 0 || !m_transfer_case->tr_2wd)
1437  return;
1438 
1439  if (m_transfer_case->tr_4wd_mode && !m_transfer_case->tr_2wd_lo)
1440  {
1441  for (int i = 0; i < m_transfer_case->tr_gear_ratios.size(); i++)
1442  {
1443  this->toggleTransferCaseGearRatio();
1444  if (m_transfer_case->tr_gear_ratios[0] == 1.0f)
1445  break;
1446  }
1447  }
1448 
1449  m_transfer_case->tr_4wd_mode = !m_transfer_case->tr_4wd_mode;
1450 
1451  if (m_transfer_case->tr_4wd_mode)
1452  {
1453  ar_wheels[m_wheel_diffs[m_transfer_case->tr_ax_2]->di_idx_1].wh_propulsed = true;
1454  ar_wheels[m_wheel_diffs[m_transfer_case->tr_ax_2]->di_idx_2].wh_propulsed = true;
1455  m_num_proped_wheels += 2;
1456  }
1457  else
1458  {
1459  ar_wheels[m_wheel_diffs[m_transfer_case->tr_ax_2]->di_idx_1].wh_propulsed = false;
1460  ar_wheels[m_wheel_diffs[m_transfer_case->tr_ax_2]->di_idx_2].wh_propulsed = false;
1461  m_num_proped_wheels -= 2;
1462  }
1463 }
1464 
1465 void Actor::toggleTransferCaseGearRatio()
1466 {
1467  if (!ar_engine || !m_transfer_case || m_transfer_case->tr_gear_ratios.size() < 2)
1468  return;
1469 
1470  if (m_transfer_case->tr_4wd_mode || m_transfer_case->tr_2wd_lo)
1471  {
1472  auto gear_ratios = &m_transfer_case->tr_gear_ratios;
1473  std::rotate(gear_ratios->begin(), gear_ratios->begin() + 1, gear_ratios->end());
1474 
1475  ar_engine->SetTCaseRatio(m_transfer_case->tr_gear_ratios[0]);
1476  }
1477 }
1478 
1479 String Actor::getTransferCaseName()
1480 {
1481  String name = "";
1482  if (m_transfer_case)
1483  {
1484  name += m_transfer_case->tr_4wd_mode ? "4WD " : "2WD ";
1485  if (m_transfer_case->tr_gear_ratios[0] > 1.0f)
1486  name += "Lo (" + TOSTRING(m_transfer_case->tr_gear_ratios[0]) + ":1)";
1487  else
1488  name += "Hi";
1489  }
1490  return name;
1491 }
1492 
1493 Ogre::Vector3 Actor::getRotationCenter()
1494 {
1495  Vector3 sum = Vector3::ZERO;
1496  std::vector<Vector3> positions;
1497  for (int i = 0; i < ar_num_nodes; i++)
1498  {
1499  Vector3 pos = ar_nodes[i].AbsPosition;
1500  const auto it = std::find_if(positions.begin(), positions.end(),
1501  [pos](const Vector3 ref) { return pos.positionEquals(ref, 0.01f); });
1502  if (it == positions.end())
1503  {
1504  sum += pos;
1505  positions.push_back(pos);
1506  }
1507  }
1508  return sum / positions.size();
1509 }
1510 
1511 float Actor::getMinHeight(bool skip_virtual_nodes)
1512 {
1513  float height = std::numeric_limits<float>::max();
1514  for (int i = 0; i < ar_num_nodes; i++)
1515  {
1516  if (!skip_virtual_nodes || !ar_nodes[i].nd_no_ground_contact)
1517  {
1518  height = std::min(ar_nodes[i].AbsPosition.y, height);
1519  }
1520  }
1521  return (!skip_virtual_nodes || height < std::numeric_limits<float>::max()) ? height : getMinHeight(false);
1522 }
1523 
1524 float Actor::getMaxHeight(bool skip_virtual_nodes)
1525 {
1526  float height = std::numeric_limits<float>::min();
1527  for (int i = 0; i < ar_num_nodes; i++)
1528  {
1529  if (!skip_virtual_nodes || !ar_nodes[i].nd_no_ground_contact)
1530  {
1531  height = std::max(height, ar_nodes[i].AbsPosition.y);
1532  }
1533  }
1534  return (!skip_virtual_nodes || height > std::numeric_limits<float>::min()) ? height : getMaxHeight(false);
1535 }
1536 
1537 float Actor::getHeightAboveGround(bool skip_virtual_nodes)
1538 {
1539  float agl = std::numeric_limits<float>::max();
1540  for (int i = 0; i < ar_num_nodes; i++)
1541  {
1542  if (!skip_virtual_nodes || !ar_nodes[i].nd_no_ground_contact)
1543  {
1544  Vector3 pos = ar_nodes[i].AbsPosition;
1545  agl = std::min(pos.y - App::GetGameContext()->GetTerrain()->GetCollisions()->getSurfaceHeight(pos.x, pos.z), agl);
1546  }
1547  }
1548  return (!skip_virtual_nodes || agl < std::numeric_limits<float>::max()) ? agl : getHeightAboveGround(false);
1549 }
1550 
1551 float Actor::getHeightAboveGroundBelow(float height, bool skip_virtual_nodes)
1552 {
1553  float agl = std::numeric_limits<float>::max();
1554  for (int i = 0; i < ar_num_nodes; i++)
1555  {
1556  if (!skip_virtual_nodes || !ar_nodes[i].nd_no_ground_contact)
1557  {
1558  Vector3 pos = ar_nodes[i].AbsPosition;
1559  agl = std::min(pos.y - App::GetGameContext()->GetTerrain()->GetCollisions()->getSurfaceHeightBelow(pos.x, pos.z, height), agl);
1560  }
1561  }
1562  return (!skip_virtual_nodes || agl < std::numeric_limits<float>::max()) ? agl : getHeightAboveGroundBelow(height, false);
1563 }
1564 
1565 void Actor::reset(bool keep_position)
1566 {
1567  if (ar_state == ActorState::DISPOSED)
1568  return;
1569 
1571  rq->amr_actor = this->ar_instance_id;
1572  rq->amr_type = (keep_position) ? ActorModifyRequest::Type::RESET_ON_SPOT : ActorModifyRequest::Type::RESET_ON_INIT_POS;
1574 }
1575 
1576 void Actor::SoftReset()
1577 {
1578  TRIGGER_EVENT_ASYNC(SE_TRUCK_RESET, ar_instance_id);
1579 
1580  float agl = this->getHeightAboveGroundBelow(this->getMaxHeight(true), true);
1581 
1582  if (App::GetGameContext()->GetTerrain()->getWater())
1583  {
1584  agl = std::min(this->getMinHeight(true) - App::GetGameContext()->GetTerrain()->getWater()->GetStaticWaterHeight(), agl);
1585  }
1586 
1587  if (agl < 0.0f)
1588  {
1589  Vector3 translation = -agl * Vector3::UNIT_Y;
1590  this->resetPosition(ar_nodes[0].AbsPosition + translation, false);
1591  for (ActorPtr& actor : ar_linked_actors)
1592  {
1593  actor->resetPosition(actor->ar_nodes[0].AbsPosition + translation, false);
1594  }
1595  }
1596 
1597  m_ongoing_reset = true;
1598 }
1599 
1600 void Actor::SyncReset(bool reset_position)
1601 {
1602  TRIGGER_EVENT_ASYNC(SE_TRUCK_RESET, ar_instance_id);
1603 
1604  m_reset_timer.reset();
1605 
1606  m_camera_local_gforces_cur = Vector3::ZERO;
1607  m_camera_local_gforces_max = Vector3::ZERO;
1608 
1609  ar_hydro_dir_state = 0.0;
1610  ar_hydro_aileron_state = 0.0;
1611  ar_hydro_rudder_state = 0.0;
1612  ar_hydro_elevator_state = 0.0;
1613  ar_hydro_dir_wheel_display = 0.0;
1614 
1615  ar_fusedrag = Vector3::ZERO;
1616  this->setBlinkType(BlinkType::BLINK_NONE);
1617  ar_parking_brake = false;
1618  ar_trailer_parking_brake = false;
1619  ar_avg_wheel_speed = 0.0f;
1620  ar_wheel_speed = 0.0f;
1621  ar_wheel_spin = 0.0f;
1622  cc_mode = false;
1623 
1624  ar_origin = Vector3::ZERO;
1625  float cur_rot = getRotation();
1626  Vector3 cur_position = ar_nodes[0].AbsPosition;
1627 
1628  this->DisjoinInterActorBeams(); // OK to be invoked here - SyncReset() - `processing MSG_SIM_MODIFY_ACTOR_REQUESTED`
1629 
1630  for (int i = 0; i < ar_num_nodes; i++)
1631  {
1632  ar_nodes[i].AbsPosition = ar_initial_node_positions[i];
1633  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1634  ar_nodes[i].Velocity = Vector3::ZERO;
1635  ar_nodes[i].Forces = Vector3::ZERO;
1636  }
1637 
1638  for (int i = 0; i < ar_num_beams; i++)
1639  {
1640  ar_beams[i].maxposstress = ar_beams[i].default_beam_deform;
1641  ar_beams[i].maxnegstress = -ar_beams[i].default_beam_deform;
1642  ar_beams[i].minmaxposnegstress = ar_beams[i].default_beam_deform;
1643  ar_beams[i].strength = ar_beams[i].initial_beam_strength;
1644  ar_beams[i].L = ar_beams[i].refL;
1645  ar_beams[i].stress = 0.0;
1646  ar_beams[i].bm_broken = false;
1647  ar_beams[i].bm_disabled = false;
1648  }
1649 
1650  this->applyNodeBeamScales();
1651 
1652  // Extra cleanup for inter-actor beams (until the above `ar_beams` loop is fixed)
1653 
1654  for (auto& h : ar_hooks)
1655  {
1656  h.hk_beam->bm_disabled = true; // should only be active if the hook is locked
1657  }
1658 
1659  for (auto& t : ar_ties)
1660  {
1661  t.ti_locked_ropable = nullptr; // `tieToggle()` doesn't do this - bug or feature? ~ ohlidalp, 06/2024
1662  t.ti_beam->bm_disabled = true; // should only be active if the tie is tied
1663  }
1664 
1665  // End extra cleanup
1666 
1667  for (auto& r : ar_ropables)
1668  {
1669  r.attached_ties = 0;
1670  r.attached_ropes = 0;
1671  }
1672 
1673  for (int i = 0; i < ar_num_wheels; i++)
1674  {
1675  ar_wheels[i].wh_speed = 0.0;
1676  ar_wheels[i].wh_torque = 0.0;
1677  ar_wheels[i].wh_avg_speed = 0.0;
1678  ar_wheels[i].wh_is_detached = false;
1679  }
1680 
1681  if (ar_engine)
1682  {
1683  if (App::sim_spawn_running->getBool())
1684  {
1685  ar_engine->StartEngine();
1686  }
1687  ar_engine->SetWheelSpin(0.0f);
1688  }
1689 
1690  int num_axle_diffs = (m_transfer_case && m_transfer_case->tr_4wd_mode) ? m_num_axle_diffs + 1 : m_num_axle_diffs;
1691  for (int i = 0; i < num_axle_diffs; i++)
1692  m_axle_diffs[i]->di_delta_rotation = 0.0f;
1693  for (int i = 0; i < m_num_wheel_diffs; i++)
1694  m_wheel_diffs[i]->di_delta_rotation = 0.0f;
1695  for (int i = 0; i < ar_num_aeroengines; i++)
1696  ar_aeroengines[i]->reset();
1697  for (int i = 0; i < ar_num_screwprops; i++)
1698  ar_screwprops[i]->reset();
1699  for (int i = 0; i < ar_num_rotators; i++)
1700  ar_rotators[i].angle = 0.0;
1701  for (int i = 0; i < ar_num_wings; i++)
1702  ar_wings[i].fa->broken = false;
1703  if (ar_autopilot)
1704  this->ar_autopilot->reset();
1705  if (m_buoyance)
1706  m_buoyance->sink = false;
1707 
1708  for (hydrobeam_t& hydrobeam: ar_hydros)
1709  {
1710  hydrobeam.hb_inertia.ResetCmdKeyDelay();
1711  }
1712 
1713  this->GetGfxActor()->ResetFlexbodies();
1714 
1715  // reset on spot with backspace
1716  if (!reset_position)
1717  {
1718  this->ResetAngle(cur_rot);
1719  this->resetPosition(cur_position, false);
1720  float agl = this->getHeightAboveGroundBelow(this->getMaxHeight(true), true);
1721  if (App::GetGameContext()->GetTerrain()->getWater())
1722  {
1723  agl = std::min(this->getMinHeight(true) - App::GetGameContext()->GetTerrain()->getWater()->GetStaticWaterHeight(), agl);
1724  }
1725  if (agl < 0.0f)
1726  {
1727  this->resetPosition(ar_nodes[0].AbsPosition - agl * Vector3::UNIT_Y, false);
1728  }
1729  }
1730  else
1731  {
1732  this->UpdateBoundingBoxes();
1733  this->calculateAveragePosition();
1734  }
1735 
1736  for (int i = 1; i <= MAX_COMMANDS; i++) // BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
1737  {
1738  ar_command_key[i].commandValue = 0.0;
1739  ar_command_key[i].triggerInputValue = 0.0f;
1740  ar_command_key[i].playerInputValue = 0.0f;
1741  for (auto& b : ar_command_key[i].beams)
1742  {
1743  b.cmb_state->auto_moving_mode = 0;
1744  b.cmb_state->pressed_center_mode = false;
1745  }
1746  }
1747 
1748  this->resetSlideNodes();
1749  if (m_slidenodes_locked)
1750  {
1751  this->toggleSlideNodeLock(); // OK to be invoked here - SyncReset() - processing MSG_SIM_MODIFY_ACTOR_REQUESTED
1752  }
1753 
1754  m_ongoing_reset = true;
1755 }
1756 
1757 void Actor::applyNodeBeamScales()
1758 {
1759  for (int i = 0; i < ar_num_nodes; i++)
1760  {
1761  ar_nodes[i].mass = ar_initial_node_masses[i] * ar_nb_mass_scale;
1762  }
1763 
1764  m_total_mass = ar_initial_total_mass * ar_nb_mass_scale;
1765 
1766  for (int i = 0; i < ar_num_beams; i++)
1767  {
1768  if ((ar_beams[i].p1->nd_tyre_node || ar_beams[i].p1->nd_rim_node) ||
1769  (ar_beams[i].p2->nd_tyre_node || ar_beams[i].p2->nd_rim_node))
1770  {
1771  ar_beams[i].k = ar_initial_beam_defaults[i].first * ar_nb_wheels_scale.first;
1772  ar_beams[i].d = ar_initial_beam_defaults[i].second * ar_nb_wheels_scale.second;
1773  }
1774  else if (ar_beams[i].bounded == SHOCK1 || ar_beams[i].bounded == SHOCK2 || ar_beams[i].bounded == SHOCK3)
1775  {
1776  ar_beams[i].k = ar_initial_beam_defaults[i].first * ar_nb_shocks_scale.first;;
1777  ar_beams[i].d = ar_initial_beam_defaults[i].second * ar_nb_shocks_scale.second;
1778  }
1779  else
1780  {
1781  ar_beams[i].k = ar_initial_beam_defaults[i].first * ar_nb_beams_scale.first;
1782  ar_beams[i].d = ar_initial_beam_defaults[i].second * ar_nb_beams_scale.second;
1783  }
1784  }
1785 }
1786 
1787 void Actor::ForceFeedbackStep(int steps)
1788 {
1789  m_force_sensors.out_body_forces = m_force_sensors.accu_body_forces / steps;
1790  if (!ar_hydros.empty()) // Vehicle has hydros?
1791  {
1792  m_force_sensors.out_hydros_forces = (m_force_sensors.accu_hydros_forces / steps) / ar_hydros.size();
1793  }
1794 }
1795 
1796 void Actor::HandleAngelScriptEvents(float dt)
1797 {
1798 #ifdef USE_ANGELSCRIPT
1799 
1800  if (m_water_contact && !m_water_contact_old)
1801  {
1802  m_water_contact_old = m_water_contact;
1804  }
1805 #endif // USE_ANGELSCRIPT
1806 }
1807 
1808 void Actor::searchBeamDefaults()
1809 {
1810  SyncReset(true);
1811 
1812  auto old_beams_scale = ar_nb_beams_scale;
1813  auto old_shocks_scale = ar_nb_shocks_scale;
1814  auto old_wheels_scale = ar_nb_wheels_scale;
1815 
1816  if (ar_nb_initialized)
1817  {
1818  ar_nb_beams_scale.first = Math::RangeRandom(ar_nb_beams_k_interval.first, ar_nb_beams_k_interval.second);
1819  ar_nb_beams_scale.second = Math::RangeRandom(ar_nb_beams_d_interval.first, ar_nb_beams_d_interval.second);
1820  ar_nb_shocks_scale.first = Math::RangeRandom(ar_nb_shocks_k_interval.first, ar_nb_shocks_k_interval.second);
1821  ar_nb_shocks_scale.second = Math::RangeRandom(ar_nb_shocks_d_interval.first, ar_nb_shocks_d_interval.second);
1822  ar_nb_wheels_scale.first = Math::RangeRandom(ar_nb_wheels_k_interval.first, ar_nb_wheels_k_interval.second);
1823  ar_nb_wheels_scale.second = Math::RangeRandom(ar_nb_wheels_d_interval.first, ar_nb_wheels_d_interval.second);
1824  }
1825  else
1826  {
1827  ar_nb_beams_scale.first = Math::Clamp(1.0f, ar_nb_beams_k_interval.first, ar_nb_beams_k_interval.second);
1828  ar_nb_beams_scale.second = Math::Clamp(1.0f, ar_nb_beams_d_interval.first, ar_nb_beams_d_interval.second);
1829  ar_nb_shocks_scale.first = Math::Clamp(1.0f, ar_nb_shocks_k_interval.first, ar_nb_shocks_k_interval.second);
1830  ar_nb_shocks_scale.second = Math::Clamp(1.0f, ar_nb_shocks_d_interval.first, ar_nb_shocks_d_interval.second);
1831  ar_nb_wheels_scale.first = Math::Clamp(1.0f, ar_nb_wheels_k_interval.first, ar_nb_wheels_k_interval.second);
1832  ar_nb_wheels_scale.second = Math::Clamp(1.0f, ar_nb_wheels_d_interval.first, ar_nb_wheels_d_interval.second);
1833  ar_nb_reference = std::vector<float>(ar_nb_reference.size(), std::numeric_limits<float>::max());
1834  ar_nb_optimum = std::vector<float>(ar_nb_reference.size(), std::numeric_limits<float>::max());
1835  }
1836 
1837  this->applyNodeBeamScales();
1838 
1839  m_ongoing_reset = false;
1840  this->CalcForcesEulerPrepare(true);
1841  for (int i = 0; i < ar_nb_skip_steps; i++)
1842  {
1843  this->CalcForcesEulerCompute(i == 0, ar_nb_skip_steps);
1844  if (m_ongoing_reset)
1845  break;
1846  }
1847  m_ongoing_reset = true;
1848 
1849  float sum_movement = 0.0f;
1850  float movement = 0.0f;
1851  float sum_velocity = 0.0f;
1852  float velocity = 0.0f;
1853  float sum_stress = 0.0f;
1854  float stress = 0.0f;
1855  int sum_broken = 0;
1856  for (int k = 0; k < ar_nb_measure_steps; k++)
1857  {
1858  this->CalcForcesEulerCompute(false, ar_nb_measure_steps);
1859  for (int i = 0; i < ar_num_nodes; i++)
1860  {
1861  float v = ar_nodes[i].Velocity.length();
1862  sum_movement += v / (float)ar_nb_measure_steps;
1863  movement = std::max(movement, v);
1864  }
1865  for (int i = 0; i < ar_num_beams; i++)
1866  {
1867  Vector3 dis = (ar_beams[i].p1->RelPosition - ar_beams[i].p2->RelPosition).normalisedCopy();
1868  float v = (ar_beams[i].p1->Velocity - ar_beams[i].p2->Velocity).dotProduct(dis);
1869  sum_velocity += std::abs(v) / (float)ar_nb_measure_steps;
1870  velocity = std::max(velocity, std::abs(v));
1871  sum_stress += std::abs(ar_beams[i].stress) / (float)ar_nb_measure_steps;
1872  stress = std::max(stress, std::abs(ar_beams[i].stress));
1873  if (k == 0 && ar_beams[i].bm_broken)
1874  {
1875  sum_broken++;
1876  }
1877  }
1878  if (sum_broken > ar_nb_reference[6] ||
1879  stress > ar_nb_reference[0] || velocity > ar_nb_reference[2] || movement > ar_nb_optimum[4] ||
1880  sum_stress > ar_nb_reference[1] || sum_velocity > ar_nb_reference[3] || sum_movement > ar_nb_optimum[5] * 2.f)
1881  {
1882  ar_nb_beams_scale = old_beams_scale;
1883  ar_nb_shocks_scale = old_shocks_scale;
1884  ar_nb_wheels_scale = old_wheels_scale;
1885  SyncReset(true);
1886  return;
1887  }
1888  }
1889  SyncReset(true);
1890 
1891  ar_nb_optimum = {stress, sum_stress, velocity, sum_velocity, movement, sum_movement, (float)sum_broken};
1892  if (!ar_nb_initialized)
1893  {
1894  ar_nb_reference = ar_nb_optimum;
1895  }
1896  ar_nb_initialized = true;
1897 }
1898 
1899 void Actor::HandleInputEvents(float dt)
1900 {
1901  if (!m_ongoing_reset)
1902  return;
1903 
1904  if (m_anglesnap_request > 0)
1905  {
1906  float rotation = Radian(getRotation()).valueDegrees();
1907  float target_rotation = std::round(rotation / m_anglesnap_request) * m_anglesnap_request;
1908  m_rotation_request = -Degree(target_rotation - rotation).valueRadians();
1909  m_rotation_request_center = getRotationCenter();
1910  m_anglesnap_request = 0;
1911  }
1912 
1913  if (m_rotation_request != 0.0f)
1914  {
1915  Quaternion rot = Quaternion(Radian(m_rotation_request), Vector3::UNIT_Y);
1916 
1917  for (int i = 0; i < ar_num_nodes; i++)
1918  {
1919  ar_nodes[i].AbsPosition -= m_rotation_request_center;
1920  ar_nodes[i].AbsPosition = rot * ar_nodes[i].AbsPosition;
1921  ar_nodes[i].AbsPosition += m_rotation_request_center;
1922  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1923  ar_nodes[i].Velocity = rot * ar_nodes[i].Velocity;
1924  ar_nodes[i].Forces = rot * ar_nodes[i].Forces;
1925  }
1926 
1927  m_rotation_request = 0.0f;
1928  this->UpdateBoundingBoxes();
1929  calculateAveragePosition();
1930  }
1931 
1932  if (m_translation_request != Vector3::ZERO)
1933  {
1934  for (int i = 0; i < ar_num_nodes; i++)
1935  {
1936  ar_nodes[i].AbsPosition += m_translation_request;
1937  ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin;
1938  }
1939 
1940  m_translation_request = Vector3::ZERO;
1941  UpdateBoundingBoxes();
1942  calculateAveragePosition();
1943  }
1944 }
1945 
1946 void Actor::sendStreamSetup()
1947 {
1949  memset(&reg, 0, sizeof(RoRnet::ActorStreamRegister));
1950  reg.status = 0;
1951  reg.type = 0;
1953  strncpy(reg.name, ar_filename.c_str(), 128);
1954  if (m_used_skin_entry != nullptr)
1955  {
1956  strncpy(reg.skin, m_used_skin_entry->dname.c_str(), 60);
1957  }
1958  strncpy(reg.sectionconfig, m_section_config.c_str(), 60);
1959 
1960 #ifdef USE_SOCKETW
1962 #endif // USE_SOCKETW
1963 
1964  ar_net_source_id = reg.origin_sourceid;
1965  ar_net_stream_id = reg.origin_streamid;
1966 }
1967 
1968 void Actor::sendStreamData()
1969 {
1970  using namespace RoRnet;
1971 #ifdef USE_SOCKETW
1972  if (ar_net_timer.getMilliseconds() - ar_net_last_update_time < 100)
1973  return;
1974 
1975  ar_net_last_update_time = ar_net_timer.getMilliseconds();
1976 
1977  //look if the packet is too big first
1978  if (m_net_total_buffer_size + sizeof(RoRnet::VehicleState) > RORNET_MAX_MESSAGE_LENGTH)
1979  {
1980  ErrorUtils::ShowError(_L("Actor is too big to be sent over the net."), _L("Network error!"));
1981  exit(126);
1982  }
1983 
1984  char send_buffer[8192] = {0};
1985 
1986  unsigned int packet_len = 0;
1987 
1988  // RoRnet::VehicleState is at the beginning of the buffer
1989  {
1990  RoRnet::VehicleState* send_oob = (RoRnet::VehicleState *)send_buffer;
1991  packet_len += sizeof(RoRnet::VehicleState);
1992 
1993  send_oob->flagmask = 0;
1994 
1995  send_oob->time = App::GetGameContext()->GetActorManager()->GetNetTime();
1996  if (ar_engine)
1997  {
1998  send_oob->engine_speed = ar_engine->GetEngineRpm();
1999  send_oob->engine_force = ar_engine->GetAcceleration();
2000  send_oob->engine_clutch = ar_engine->GetClutch();
2001  send_oob->engine_gear = ar_engine->GetGear();
2002 
2003  if (ar_engine->hasContact())
2004  send_oob->flagmask += NETMASK_ENGINE_CONT;
2005  if (ar_engine->isRunning())
2006  send_oob->flagmask += NETMASK_ENGINE_RUN;
2007 
2008  switch (ar_engine->GetAutoShiftMode())
2009  {
2011  break;
2013  break;
2015  break;
2017  break;
2019  break;
2020  }
2021  }
2022  if (ar_num_aeroengines > 0)
2023  {
2024  float rpm = ar_aeroengines[0]->getRPM();
2025  send_oob->engine_speed = rpm;
2026  }
2027 
2028  send_oob->hydrodirstate = ar_hydro_dir_state;
2029  send_oob->brake = ar_brake;
2030  send_oob->wheelspeed = ar_wheel_speed;
2031 
2032  // RoRnet::Netmask
2033 
2034  if (getCustomParticleMode())
2035  send_oob->flagmask += NETMASK_PARTICLE;
2036 
2037  if (ar_parking_brake)
2038  send_oob->flagmask += NETMASK_PBRAKE;
2039  if (m_tractioncontrol)
2040  send_oob->flagmask += NETMASK_TC_ACTIVE;
2041  if (m_antilockbrake)
2042  send_oob->flagmask += NETMASK_ALB_ACTIVE;
2043 
2044  if (SOUND_GET_STATE(ar_instance_id, SS_TRIG_HORN))
2045  send_oob->flagmask += NETMASK_HORN;
2046 
2047  // RoRnet::Lightmask
2048 
2049  send_oob->lightmask = m_lightmask; // That's it baby :)
2050  }
2051 
2052  // then process the contents
2053  {
2054  char* ptr = send_buffer + sizeof(RoRnet::VehicleState);
2055  float* send_nodes = (float *)ptr;
2056  packet_len += m_net_total_buffer_size;
2057 
2058  // copy data into the buffer
2059  int i;
2060 
2061  // reference node first
2062  Vector3& refpos = ar_nodes[0].AbsPosition;
2063  send_nodes[0] = refpos.x;
2064  send_nodes[1] = refpos.y;
2065  send_nodes[2] = refpos.z;
2066 
2067  ptr += sizeof(float) * 3;// plus 3 floats from above
2068 
2069  // then copy the other nodes into a compressed short format
2070  short* sbuf = (short*)ptr;
2071  for (i = 1; i < m_net_first_wheel_node; i++)
2072  {
2073  Vector3 relpos = ar_nodes[i].AbsPosition - refpos;
2074  sbuf[(i - 1) * 3 + 0] = (short int)(relpos.x * m_net_node_compression);
2075  sbuf[(i - 1) * 3 + 1] = (short int)(relpos.y * m_net_node_compression);
2076  sbuf[(i - 1) * 3 + 2] = (short int)(relpos.z * m_net_node_compression);
2077 
2078  ptr += sizeof(short int) * 3; // increase pointer
2079  }
2080 
2081  // then to the wheels
2082  float* wfbuf = (float*)ptr;
2083  for (i = 0; i < ar_num_wheels; i++)
2084  {
2085  wfbuf[i] = ar_wheels[i].wh_net_rp;
2086  }
2087  ptr += ar_num_wheels * sizeof(float);
2088 
2089  // Then the anim key states
2090  for (size_t i = 0; i < m_prop_anim_key_states.size(); i++)
2091  {
2092  if (m_prop_anim_key_states[i].anim_active)
2093  {
2094  // Pack as bit array, starting with most signifficant bit
2095  char& dst_byte = *(ptr + (i / 8));
2096  char mask = ((char)m_prop_anim_key_states[i].anim_active) << (7 - (i % 8));
2097  dst_byte |= mask;
2098  }
2099  }
2100  }
2101 
2102  App::GetNetwork()->AddPacket(ar_net_stream_id, MSG2_STREAM_DATA_DISCARDABLE, packet_len, send_buffer);
2103 #endif //SOCKETW
2104 }
2105 
2106 void Actor::CalcAnimators(hydrobeam_t const& hydrobeam, float &cstate, int &div)
2107 {
2108  // boat rudder
2109  if (hydrobeam.hb_anim_flags & ANIM_FLAG_BRUDDER)
2110  {
2111  int spi;
2112  float ctmp = 0.0f;
2113  for (spi = 0; spi < ar_num_screwprops; spi++)
2114  if (ar_screwprops[spi])
2115  ctmp += ar_screwprops[spi]->getRudder();
2116 
2117  if (spi > 0)
2118  ctmp = ctmp / spi;
2119  cstate = ctmp;
2120  div++;
2121  }
2122 
2123  // boat throttle
2124  if (hydrobeam.hb_anim_flags & ANIM_FLAG_BTHROTTLE)
2125  {
2126  int spi;
2127  float ctmp = 0.0f;
2128  for (spi = 0; spi < ar_num_screwprops; spi++)
2129  if (ar_screwprops[spi])
2130  ctmp += ar_screwprops[spi]->getThrottle();
2131 
2132  if (spi > 0)
2133  ctmp = ctmp / spi;
2134  cstate = ctmp;
2135  div++;
2136  }
2137 
2138  // differential lock status
2139  if (hydrobeam.hb_anim_flags & ANIM_FLAG_DIFFLOCK)
2140  {
2141  if (m_num_wheel_diffs && m_wheel_diffs[0])
2142  {
2143  String name = m_wheel_diffs[0]->GetDifferentialTypeName();
2144  if (name == "Open")
2145  cstate = 0.0f;
2146  if (name == "Split")
2147  cstate = 0.5f;
2148  if (name == "Locked")
2149  cstate = 1.0f;
2150  }
2151  else // no axles/diffs avail, mode is split by default
2152  cstate = 0.5f;
2153 
2154  div++;
2155  }
2156 
2157  // heading
2158  if (hydrobeam.hb_anim_flags & ANIM_FLAG_HEADING)
2159  {
2160  float heading = getRotation();
2161  // rad2deg limitedrange -1 to +1
2162  cstate = (heading * 57.29578f) / 360.0f;
2163  div++;
2164  }
2165 
2166  // torque
2167  if (ar_engine && hydrobeam.hb_anim_flags & ANIM_FLAG_TORQUE)
2168  {
2169  float torque = ar_engine->GetCrankFactor();
2170  if (torque <= 0.0f)
2171  torque = 0.0f;
2172  if (torque >= ar_anim_previous_crank)
2173  cstate -= torque / 10.0f;
2174  else
2175  cstate = 0.0f;
2176 
2177  if (cstate <= -1.0f)
2178  cstate = -1.0f;
2179  ar_anim_previous_crank = torque;
2180  div++;
2181  }
2182 
2183  // shifterseq, to amimate sequentiell shifting
2184  if (ar_engine && (hydrobeam.hb_anim_flags & ANIM_FLAG_SHIFTER) && hydrobeam.hb_anim_param == 3.0f)
2185  {
2186 
2187  int shifter = ar_engine->GetGear();
2188  if (shifter > m_previous_gear)
2189  {
2190  cstate = 1.0f;
2191  ar_anim_shift_timer = 0.2f;
2192  }
2193  if (shifter < m_previous_gear)
2194  {
2195  cstate = -1.0f;
2196  ar_anim_shift_timer = -0.2f;
2197  }
2198  m_previous_gear = shifter;
2199 
2200  if (ar_anim_shift_timer > 0.0f)
2201  {
2202  cstate = 1.0f;
2203  ar_anim_shift_timer -= PHYSICS_DT;
2204  if (ar_anim_shift_timer < 0.0f)
2205  ar_anim_shift_timer = 0.0f;
2206  }
2207  if (ar_anim_shift_timer < 0.0f)
2208  {
2209  cstate = -1.0f;
2210  ar_anim_shift_timer += PHYSICS_DT;
2211  if (ar_anim_shift_timer > 0.0f)
2212  ar_anim_shift_timer = 0.0f;
2213  }
2214 
2215  div++;
2216  }
2217 
2218  // shifterman1, left/right
2219  if (ar_engine && (hydrobeam.hb_anim_flags & ANIM_FLAG_SHIFTER) && hydrobeam.hb_anim_param == 1.0f)
2220  {
2221  int shifter = ar_engine->GetGear();
2222  if (!shifter)
2223  {
2224  cstate = -0.5f;
2225  }
2226  else if (shifter < 0)
2227  {
2228  cstate = 1.0f;
2229  }
2230  else
2231  {
2232  cstate -= int((shifter - 1.0) / 2.0);
2233  }
2234  div++;
2235  }
2236 
2237  // shifterman2, up/down
2238  if (ar_engine && (hydrobeam.hb_anim_flags & ANIM_FLAG_SHIFTER) && hydrobeam.hb_anim_param == 2.0f)
2239  {
2240  int shifter = ar_engine->GetGear();
2241  cstate = 0.5f;
2242  if (shifter < 0)
2243  {
2244  cstate = 1.0f;
2245  }
2246  if (shifter > 0)
2247  {
2248  cstate = shifter % 2;
2249  }
2250  div++;
2251  }
2252 
2253  // shifterlinear, to amimate cockpit gearselect gauge and autotransmission stick
2254  if (ar_engine && (hydrobeam.hb_anim_flags & ANIM_FLAG_SHIFTER) && hydrobeam.hb_anim_param == 4.0f)
2255  {
2256  int shifter = ar_engine->GetGear();
2257  int numgears = ar_engine->getNumGears();
2258  cstate -= (shifter + 2.0) / (numgears + 2.0);
2259  div++;
2260  }
2261 
2262  // parking brake
2263  if (hydrobeam.hb_anim_flags & ANIM_FLAG_PBRAKE)
2264  {
2265  float pbrake = ar_parking_brake;
2266  cstate -= pbrake;
2267  div++;
2268  }
2269 
2270  // speedo ( scales with speedomax )
2271  if (hydrobeam.hb_anim_flags & ANIM_FLAG_SPEEDO)
2272  {
2273  float speedo = ar_wheel_speed / ar_guisettings_speedo_max_kph;
2274  cstate -= speedo * 3.0f;
2275  div++;
2276  }
2277 
2278  // engine tacho ( scales with maxrpm, default is 3500 )
2279  if (ar_engine && hydrobeam.hb_anim_flags & ANIM_FLAG_TACHO)
2280  {
2281  float tacho = ar_engine->GetEngineRpm() / ar_engine->getMaxRPM();
2282  cstate -= tacho;
2283  div++;
2284  }
2285 
2286  // turbo
2287  if (ar_engine && hydrobeam.hb_anim_flags & ANIM_FLAG_TURBO)
2288  {
2289  float turbo = ar_engine->GetTurboPsi() * 3.34;
2290  cstate -= turbo / 67.0f;
2291  div++;
2292  }
2293 
2294  // brake
2295  if (hydrobeam.hb_anim_flags & ANIM_FLAG_BRAKE)
2296  {
2297  cstate -= ar_brake;
2298  div++;
2299  }
2300 
2301  // accelerator
2302  if (ar_engine && hydrobeam.hb_anim_flags & ANIM_FLAG_ACCEL)
2303  {
2304  float accel = ar_engine->GetAcceleration();
2305  cstate -= accel + 0.06f;
2306  //( small correction, get acc is nver smaller then 0.06.
2307  div++;
2308  }
2309 
2310  // clutch
2311  if (ar_engine && hydrobeam.hb_anim_flags & ANIM_FLAG_CLUTCH)
2312  {
2313  float clutch = ar_engine->GetClutch();
2314  cstate -= fabs(1.0f - clutch);
2315  div++;
2316  }
2317 
2318  // aeroengines (hb_anim_param is the engine index)
2319  if ((int)hydrobeam.hb_anim_param < ar_num_aeroengines)
2320  {
2321  int aenum = (int)hydrobeam.hb_anim_param;
2322  if (hydrobeam.hb_anim_flags & ANIM_FLAG_RPM)
2323  {
2324  float angle;
2325  float pcent = ar_aeroengines[aenum]->getRPMpc();
2326  if (pcent < 60.0)
2327  angle = -5.0 + pcent * 1.9167;
2328  else if (pcent < 110.0)
2329  angle = 110.0 + (pcent - 60.0) * 4.075;
2330  else
2331  angle = 314.0;
2332  cstate -= angle / 314.0f;
2333  div++;
2334  }
2335  if (hydrobeam.hb_anim_flags & ANIM_FLAG_THROTTLE)
2336  {
2337  float throttle = ar_aeroengines[aenum]->getThrottle();
2338  cstate -= throttle;
2339  div++;
2340  }
2341 
2342  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AETORQUE)
2343  if (ar_aeroengines[aenum]->getType() == AeroEngineType::AE_XPROP)
2344  {
2345  Turboprop* tp = (Turboprop*)ar_aeroengines[aenum];
2346  cstate = (100.0 * tp->indicated_torque / tp->max_torque) / 120.0f;
2347  div++;
2348  }
2349 
2350  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AEPITCH)
2351  if (ar_aeroengines[aenum]->getType() == AeroEngineType::AE_XPROP)
2352  {
2353  Turboprop* tp = (Turboprop*)ar_aeroengines[aenum];
2354  cstate = tp->pitch / 120.0f;
2355  div++;
2356  }
2357 
2358  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AESTATUS)
2359  {
2360  if (!ar_aeroengines[aenum]->getIgnition())
2361  cstate = 0.0f;
2362  else
2363  cstate = 0.5f;
2364  if (ar_aeroengines[aenum]->isFailed())
2365  cstate = 1.0f;
2366  div++;
2367  }
2368  }
2369 
2370  // airspeed indicator
2371  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AIRSPEED)
2372  {
2373  float ground_speed_kt = ar_nodes[0].Velocity.length() * 1.9438;
2374  float altitude = ar_nodes[0].AbsPosition.y;
2375  float sea_level_pressure = 101325; //in Pa
2376  float airpressure = sea_level_pressure * pow(1.0 - 0.0065 * altitude / 288.15, 5.24947); //in Pa
2377  float airdensity = airpressure * 0.0000120896;//1.225 at sea level
2378  float kt = ground_speed_kt * sqrt(airdensity / 1.225);
2379  cstate -= kt / 100.0f;
2380  div++;
2381  }
2382 
2383  // vvi indicator
2384  if (hydrobeam.hb_anim_flags & ANIM_FLAG_VVI)
2385  {
2386  float vvi = ar_nodes[0].Velocity.y * 196.85;
2387  // limit vvi scale to +/- 6m/s
2388  cstate -= vvi / 6000.0f;
2389  if (cstate >= 1.0f)
2390  cstate = 1.0f;
2391  if (cstate <= -1.0f)
2392  cstate = -1.0f;
2393  div++;
2394  }
2395 
2396  // altimeter
2397  if (hydrobeam.hb_anim_flags & ANIM_FLAG_ALTIMETER)
2398  {
2399  //altimeter indicator 1k oscillating
2400  if (hydrobeam.hb_anim_param == 3.0f)
2401  {
2402  float altimeter = (ar_nodes[0].AbsPosition.y * 1.1811) / 360.0f;
2403  int alti_int = int(altimeter);
2404  float alti_mod = (altimeter - alti_int);
2405  cstate -= alti_mod;
2406  }
2407 
2408  //altimeter indicator 10k oscillating
2409  if (hydrobeam.hb_anim_param == 2.0f)
2410  {
2411  float alti = ar_nodes[0].AbsPosition.y * 1.1811 / 3600.0f;
2412  int alti_int = int(alti);
2413  float alti_mod = (alti - alti_int);
2414  cstate -= alti_mod;
2415  if (cstate <= -1.0f)
2416  cstate = -1.0f;
2417  }
2418 
2419  //altimeter indicator 100k limited
2420  if (hydrobeam.hb_anim_param == 1.0f)
2421  {
2422  float alti = ar_nodes[0].AbsPosition.y * 1.1811 / 36000.0f;
2423  cstate -= alti;
2424  if (cstate <= -1.0f)
2425  cstate = -1.0f;
2426  }
2427  div++;
2428  }
2429 
2430  // AOA
2431  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AOA)
2432  {
2433  float aoa = 0;
2434  if (ar_num_wings > 4)
2435  aoa = (ar_wings[4].fa->aoa) / 25.0f;
2436  if ((ar_nodes[0].Velocity.length() * 1.9438) < 10.0f)
2437  aoa = 0;
2438  cstate -= aoa;
2439  if (cstate <= -1.0f)
2440  cstate = -1.0f;
2441  if (cstate >= 1.0f)
2442  cstate = 1.0f;
2443  div++;
2444  }
2445 
2446  // roll
2447  if (hydrobeam.hb_anim_flags & ANIM_FLAG_ROLL)
2448  {
2449  Vector3 rollv = this->GetCameraRoll();
2450  Vector3 dirv = this->GetCameraDir();
2451  Vector3 upv = dirv.crossProduct(-rollv);
2452  float rollangle = asin(rollv.dotProduct(Vector3::UNIT_Y));
2453  // rad to deg
2454  rollangle = Math::RadiansToDegrees(rollangle);
2455  // flip to other side when upside down
2456  if (upv.y < 0)
2457  rollangle = 180.0f - rollangle;
2458  cstate = rollangle / 180.0f;
2459  // data output is -0.5 to 1.5, normalize to -1 to +1 without changing the zero position.
2460  // this is vital for the animator beams and does not effect the animated props
2461  if (cstate >= 1.0f)
2462  cstate = cstate - 2.0f;
2463  div++;
2464  }
2465 
2466  // pitch
2467  if (hydrobeam.hb_anim_flags & ANIM_FLAG_PITCH)
2468  {
2469  Vector3 dirv = this->GetCameraDir();
2470  float pitchangle = asin(dirv.dotProduct(Vector3::UNIT_Y));
2471  // radian to degrees with a max cstate of +/- 1.0
2472  cstate = (Math::RadiansToDegrees(pitchangle) / 90.0f);
2473  div++;
2474  }
2475 
2476  // airbrake
2477  if (hydrobeam.hb_anim_flags & ANIM_FLAG_AIRBRAKE)
2478  {
2479  float airbrake = ar_airbrake_intensity;
2480  // cstate limited to -1.0f
2481  cstate -= airbrake / 5.0f;
2482  div++;
2483  }
2484 
2485  // flaps
2486  if (hydrobeam.hb_anim_flags & ANIM_FLAG_FLAP)
2487  {
2488  float flaps = FLAP_ANGLES[ar_aerial_flap];
2489  // cstate limited to -1.0f
2490  cstate = flaps;
2491  div++;
2492  }
2493 }
2494 
2495 void Actor::CalcCabCollisions()
2496 {
2497  for (int i = 0; i < ar_num_nodes; i++)
2498  {
2499  ar_nodes[i].nd_has_mesh_contact = false;
2500  }
2501  if (m_intra_point_col_detector != nullptr)
2502  {
2503  m_intra_point_col_detector->UpdateIntraPoint();
2505  *m_intra_point_col_detector,
2506  ar_num_collcabs,
2507  ar_collcabs,
2508  ar_cabs,
2509  ar_intra_collcabrate,
2510  ar_nodes,
2511  ar_collision_range,
2512  *ar_submesh_ground_model);
2513  }
2514 }
2515 
2516 void Actor::CalcShocks2(int i, Real difftoBeamL, Real& k, Real& d, Real v)
2517 {
2518  if (v > 0) // Extension
2519  {
2520  k = ar_beams[i].shock->springout;
2521  d = ar_beams[i].shock->dampout;
2522  // add progression
2523  float logafactor = 1.0f;
2524  if (ar_beams[i].longbound != 0.0f)
2525  {
2526  logafactor = difftoBeamL / (ar_beams[i].longbound * ar_beams[i].L);
2527  logafactor = std::min(logafactor * logafactor, 1.0f);
2528  }
2529  k += ar_beams[i].shock->sprogout * k * logafactor;
2530  d += ar_beams[i].shock->dprogout * d * logafactor;
2531  }
2532  else // Compression
2533  {
2534  k = ar_beams[i].shock->springin;
2535  d = ar_beams[i].shock->dampin;
2536  // add progression
2537  float logafactor = 1.0f;
2538  if (ar_beams[i].shortbound != 0.0f)
2539  {
2540  logafactor = difftoBeamL / (ar_beams[i].shortbound * ar_beams[i].L);
2541  logafactor = std::min(logafactor * logafactor, 1.0f);
2542  }
2543  k += ar_beams[i].shock->sprogin * k * logafactor;
2544  d += ar_beams[i].shock->dprogin * d * logafactor;
2545  }
2546  if (ar_beams[i].shock->flags & SHOCK_FLAG_SOFTBUMP)
2547  {
2548  // soft bump shocks
2549  float beamsLep = ar_beams[i].L * 0.8f;
2550  float longboundprelimit = ar_beams[i].longbound * beamsLep;
2551  float shortboundprelimit = -ar_beams[i].shortbound * beamsLep;
2552  if (difftoBeamL > longboundprelimit)
2553  {
2554  // reset to longbound progressive values (oscillating beam workaround)
2555  k = ar_beams[i].shock->springout;
2556  d = ar_beams[i].shock->dampout;
2557  // add progression
2558  float logafactor = 1.0f;
2559  if (ar_beams[i].longbound != 0.0f)
2560  {
2561  logafactor = difftoBeamL / (ar_beams[i].longbound * ar_beams[i].L);
2562  logafactor = std::min(logafactor * logafactor, 1.0f);
2563  }
2564  k += ar_beams[i].shock->sprogout * k * logafactor;
2565  d += ar_beams[i].shock->dprogout * d * logafactor;
2566  // add shortbump progression
2567  logafactor = 1.0f;
2568  if (ar_beams[i].longbound != 0.0f)
2569  {
2570  logafactor = ((difftoBeamL - longboundprelimit) * 5.0f) / (ar_beams[i].longbound * ar_beams[i].L);
2571  logafactor = std::min(logafactor * logafactor, 1.0f);
2572  }
2573  k += (k + 100.0f) * ar_beams[i].shock->sprogout * logafactor;
2574  d += (d + 100.0f) * ar_beams[i].shock->dprogout * logafactor;
2575  if (v < 0)
2576  // rebound mode..get new values
2577  {
2578  k = ar_beams[i].shock->springin;
2579  d = ar_beams[i].shock->dampin;
2580  }
2581  }
2582  else if (difftoBeamL < shortboundprelimit)
2583  {
2584  // reset to shortbound progressive values (oscillating beam workaround)
2585  k = ar_beams[i].shock->springin;
2586  d = ar_beams[i].shock->dampin;
2587  // add progression
2588  float logafactor = 1.0f;
2589  if (ar_beams[i].shortbound != 0.0f)
2590  {
2591  logafactor = difftoBeamL / (ar_beams[i].shortbound * ar_beams[i].L);
2592  logafactor = std::min(logafactor * logafactor, 1.0f);
2593  }
2594  k += ar_beams[i].shock->sprogin * k * logafactor;
2595  d += ar_beams[i].shock->dprogin * d * logafactor;
2596  // add shortbump progression
2597  logafactor = 1.0f;
2598  if (ar_beams[i].shortbound != 0.0f)
2599  {
2600  logafactor = ((difftoBeamL - shortboundprelimit) * 5.0f) / (ar_beams[i].shortbound * ar_beams[i].L);
2601  logafactor = std::min(logafactor * logafactor, 1.0f);
2602  }
2603  k += (k + 100.0f) * ar_beams[i].shock->sprogout * logafactor;
2604  d += (d + 100.0f) * ar_beams[i].shock->dprogout * logafactor;
2605  if (v > 0)
2606  // rebound mode..get new values
2607  {
2608  k = ar_beams[i].shock->springout;
2609  d = ar_beams[i].shock->dampout;
2610  }
2611  }
2612  if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L || difftoBeamL < -ar_beams[i].shortbound * ar_beams[i].L)
2613  {
2614  // block reached...hard bump in soft mode with 4x default damping
2615  k = std::max(k, ar_beams[i].shock->sbd_spring);
2616  d = std::max(d, ar_beams[i].shock->sbd_damp);
2617  }
2618  }
2619  else if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L || difftoBeamL < -ar_beams[i].shortbound * ar_beams[i].L)
2620  {
2621  // hard (normal) shock bump
2622  k = ar_beams[i].shock->sbd_spring;
2623  d = ar_beams[i].shock->sbd_damp;
2624  }
2625 }
2626 
2627 void Actor::CalcShocks3(int i, Real difftoBeamL, Real &k, Real& d, Real v)
2628 {
2629  if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L)
2630  {
2631  float interp_ratio = difftoBeamL - ar_beams[i].longbound * ar_beams[i].L;
2632  k += (ar_beams[i].shock->sbd_spring - k) * interp_ratio;
2633  d += (ar_beams[i].shock->sbd_damp - d) * interp_ratio;
2634  }
2635  else if (difftoBeamL < -ar_beams[i].shortbound * ar_beams[i].L)
2636  {
2637  float interp_ratio = -difftoBeamL - ar_beams[i].shortbound * ar_beams[i].L;
2638  k += (ar_beams[i].shock->sbd_spring - k) * interp_ratio;
2639  d += (ar_beams[i].shock->sbd_damp - d) * interp_ratio;
2640  }
2641  else if (v > 0) // Extension
2642  {
2643  v = Math::Clamp(std::abs(v), +0.15f, +20.0f);
2644  k = ar_beams[i].shock->springout;
2645  d = ar_beams[i].shock->dampout * ar_beams[i].shock->dslowout * std::min(v, ar_beams[i].shock->splitout) +
2646  ar_beams[i].shock->dampout * ar_beams[i].shock->dfastout * std::max(0.0f, v - ar_beams[i].shock->splitout);
2647  d /= v;
2648  }
2649  else if (v < 0) // Compression
2650  {
2651  v = Math::Clamp(std::abs(v), +0.15f, +20.0f);
2652  k = ar_beams[i].shock->springin;
2653  d = ar_beams[i].shock->dampin * ar_beams[i].shock->dslowin * std::min(v, ar_beams[i].shock->splitin ) +
2654  ar_beams[i].shock->dampin * ar_beams[i].shock->dfastin * std::max(0.0f, v - ar_beams[i].shock->splitin );
2655  d /= v;
2656  }
2657 }
2658 
2659 void Actor::CalcTriggers(int i, Real difftoBeamL, bool trigger_hooks)
2660 {
2661  if ((ar_beams[i].shock->flags & SHOCK_FLAG_ISTRIGGER) && ar_beams[i].shock->trigger_enabled) // this is a trigger and its enabled
2662  {
2663  const float dt = PHYSICS_DT;
2664 
2665  if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L || difftoBeamL < -ar_beams[i].shortbound * ar_beams[i].L) // that has hit boundary
2666  {
2667  ar_beams[i].shock->trigger_switch_state -= dt;
2668  if (ar_beams[i].shock->trigger_switch_state <= 0.0f) // emergency release for dead-switched trigger
2669  ar_beams[i].shock->trigger_switch_state = 0.0f;
2670  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_BLOCKER) // this is an enabled blocker and past boundary
2671  {
2672  for (int scount = i + 1; scount <= i + ar_beams[i].shock->trigger_cmdshort; scount++) // (cycle blockerbeamID +1) to (blockerbeamID + beams to lock)
2673  {
2674  if (ar_beams[scount].shock && (ar_beams[scount].shock->flags & SHOCK_FLAG_ISTRIGGER)) // don't mess anything up if the user set the number too big
2675  {
2676  if (m_trigger_debug_enabled && !ar_beams[scount].shock->trigger_enabled && ar_beams[i].shock->last_debug_state != 1)
2677  {
2678  LOG(" Trigger disabled. Blocker BeamID " + TOSTRING(i) + " enabled trigger " + TOSTRING(scount));
2679  ar_beams[i].shock->last_debug_state = 1;
2680  }
2681  ar_beams[scount].shock->trigger_enabled = false; // disable the trigger
2682  }
2683  }
2684  }
2685  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_BLOCKER_A) // this is an enabled inverted blocker and inside boundary
2686  {
2687  for (int scount = i + 1; scount <= i + ar_beams[i].shock->trigger_cmdlong; scount++) // (cycle blockerbeamID + 1) to (blockerbeamID + beams to release)
2688  {
2689  if (ar_beams[scount].shock && (ar_beams[scount].shock->flags & SHOCK_FLAG_ISTRIGGER)) // don't mess anything up if the user set the number too big
2690  {
2691  if (m_trigger_debug_enabled && ar_beams[scount].shock->trigger_enabled && ar_beams[i].shock->last_debug_state != 9)
2692  {
2693  LOG(" Trigger enabled. Inverted Blocker BeamID " + TOSTRING(i) + " disabled trigger " + TOSTRING(scount));
2694  ar_beams[i].shock->last_debug_state = 9;
2695  }
2696  ar_beams[scount].shock->trigger_enabled = true; // enable the triggers
2697  }
2698  }
2699  }
2700  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CMD_BLOCKER) // this an enabled cmd-key-blocker and past a boundary
2701  {
2702  ar_command_key[ar_beams[i].shock->trigger_cmdshort].trigger_cmdkeyblock_state = false; // Release the cmdKey
2703  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 2)
2704  {
2705  LOG(" F-key trigger block released. Blocker BeamID " + TOSTRING(i) + " Released F" + TOSTRING(ar_beams[i].shock->trigger_cmdshort));
2706  ar_beams[i].shock->last_debug_state = 2;
2707  }
2708  }
2709  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CMD_SWITCH) // this is an enabled cmdkey switch and past a boundary
2710  {
2711  if (!ar_beams[i].shock->trigger_switch_state)// this switch is triggered first time in this boundary
2712  {
2713  for (int scount = 0; scount < ar_num_shocks; scount++)
2714  {
2715  int short1 = ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdshort; // cmdshort of checked trigger beam
2716  int short2 = ar_beams[i].shock->trigger_cmdshort; // cmdshort of switch beam
2717  int long1 = ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdlong; // cmdlong of checked trigger beam
2718  int long2 = ar_beams[i].shock->trigger_cmdlong; // cmdlong of switch beam
2719  int tmpi = ar_beams[ar_shocks[scount].beamid].shock->beamid; // beamID global of checked trigger beam
2720  if (((short1 == short2 && long1 == long2) || (short1 == long2 && long1 == short2)) && i != tmpi) // found both command triggers then swap if its not the switching trigger
2721  {
2722  int tmpcmdkey = ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdlong;
2723  ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdlong = ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdshort;
2724  ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdshort = tmpcmdkey;
2725  ar_beams[i].shock->trigger_switch_state = ar_beams[i].shock->trigger_boundary_t; //prevent trigger switching again before leaving boundaries or timeout
2726  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 3)
2727  {
2728  LOG(" Trigger F-key commands switched. Switch BeamID " + TOSTRING(i)+ " switched commands of Trigger BeamID " + TOSTRING(ar_beams[ar_shocks[scount].beamid].shock->beamid) + " to cmdShort: F" + TOSTRING(ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdshort) + ", cmdlong: F" + TOSTRING(ar_beams[ar_shocks[scount].beamid].shock->trigger_cmdlong));
2729  ar_beams[i].shock->last_debug_state = 3;
2730  }
2731  }
2732  }
2733  }
2734  }
2735  else
2736  { // just a trigger, check high/low boundary and set action
2737  if (difftoBeamL > ar_beams[i].longbound * ar_beams[i].L) // trigger past longbound
2738  {
2739  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_HOOK_UNLOCK)
2740  {
2741  if (trigger_hooks)
2742  {
2743  //autolock hooktoggle unlock
2744  //hookToggle(ar_beams[i].shock->trigger_cmdlong, HOOK_UNLOCK, NODENUM_INVALID);
2746  rq->alr_type = ActorLinkingRequestType::HOOK_UNLOCK;
2747  rq->alr_actor_instance_id = ar_instance_id;
2748  rq->alr_hook_group = ar_beams[i].shock->trigger_cmdlong;
2750  }
2751  }
2752  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_HOOK_LOCK)
2753  {
2754  if (trigger_hooks)
2755  {
2756  //autolock hooktoggle lock
2757  //hookToggle(ar_beams[i].shock->trigger_cmdlong, HOOK_LOCK, NODENUM_INVALID);
2759  rq->alr_type = ActorLinkingRequestType::HOOK_LOCK;
2760  rq->alr_actor_instance_id = ar_instance_id;
2761  rq->alr_hook_group = ar_beams[i].shock->trigger_cmdlong;
2763  }
2764  }
2765  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_ENGINE)
2766  {
2767  engineTriggerHelper(ar_beams[i].shock->trigger_cmdshort, EngineTriggerType(ar_beams[i].shock->trigger_cmdlong), 1.0f);
2768  }
2769  else
2770  {
2771  //just a trigger
2772  if (!ar_command_key[ar_beams[i].shock->trigger_cmdlong].trigger_cmdkeyblock_state) // related cmdkey is not blocked
2773  {
2774  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CONTINUOUS)
2775  ar_command_key[ar_beams[i].shock->trigger_cmdshort].triggerInputValue = 1; // continuous trigger only operates on trigger_cmdshort
2776  else
2777  ar_command_key[ar_beams[i].shock->trigger_cmdlong].triggerInputValue = 1;
2778  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 4)
2779  {
2780  LOG(" Trigger Longbound activated. Trigger BeamID " + TOSTRING(i) + " Triggered F" + TOSTRING(ar_beams[i].shock->trigger_cmdlong));
2781  ar_beams[i].shock->last_debug_state = 4;
2782  }
2783  }
2784  }
2785  }
2786  else // trigger past short bound
2787  {
2788  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_HOOK_UNLOCK)
2789  {
2790  if (trigger_hooks)
2791  {
2792  //autolock hooktoggle unlock
2793  //hookToggle(ar_beams[i].shock->trigger_cmdshort, HOOK_UNLOCK, NODENUM_INVALID);
2795  rq->alr_type = ActorLinkingRequestType::HOOK_UNLOCK;
2796  rq->alr_actor_instance_id = ar_instance_id;
2797  rq->alr_hook_group = ar_beams[i].shock->trigger_cmdshort;
2799  }
2800  }
2801  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_HOOK_LOCK)
2802  {
2803  if (trigger_hooks)
2804  {
2805  //autolock hooktoggle lock
2806  //hookToggle(ar_beams[i].shock->trigger_cmdshort, HOOK_LOCK, NODENUM_INVALID);
2808  rq->alr_type = ActorLinkingRequestType::HOOK_LOCK;
2809  rq->alr_actor_instance_id = ar_instance_id;
2810  rq->alr_hook_group = ar_beams[i].shock->trigger_cmdshort;
2812  }
2813  }
2814  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_ENGINE)
2815  {
2816  bool triggerValue = !(ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CONTINUOUS); // 0 if trigger is continuous, 1 otherwise
2817 
2818  engineTriggerHelper(ar_beams[i].shock->trigger_cmdshort, EngineTriggerType(ar_beams[i].shock->trigger_cmdlong), triggerValue);
2819  }
2820  else
2821  {
2822  //just a trigger
2823  if (!ar_command_key[ar_beams[i].shock->trigger_cmdshort].trigger_cmdkeyblock_state) // related cmdkey is not blocked
2824  {
2825  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CONTINUOUS)
2826  ar_command_key[ar_beams[i].shock->trigger_cmdshort].triggerInputValue = 0; // continuous trigger only operates on trigger_cmdshort
2827  else
2828  ar_command_key[ar_beams[i].shock->trigger_cmdshort].triggerInputValue = 1;
2829 
2830  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 5)
2831  {
2832  LOG(" Trigger Shortbound activated. Trigger BeamID " + TOSTRING(i) + " Triggered F" + TOSTRING(ar_beams[i].shock->trigger_cmdshort));
2833  ar_beams[i].shock->last_debug_state = 5;
2834  }
2835  }
2836  }
2837  }
2838  }
2839  }
2840  else // this is a trigger inside boundaries and its enabled
2841  {
2842  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CONTINUOUS) // this is an enabled continuous trigger
2843  {
2844  if (ar_beams[i].longbound - ar_beams[i].shortbound > 0.0f)
2845  {
2846  float diffPercentage = difftoBeamL / ar_beams[i].L;
2847  float triggerValue = (diffPercentage - ar_beams[i].shortbound) / (ar_beams[i].longbound - ar_beams[i].shortbound);
2848 
2849  triggerValue = std::max(0.0f, triggerValue);
2850  triggerValue = std::min(triggerValue, 1.0f);
2851 
2852  if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_ENGINE) // this trigger controls an engine
2853  {
2854  engineTriggerHelper(ar_beams[i].shock->trigger_cmdshort, EngineTriggerType(ar_beams[i].shock->trigger_cmdlong), triggerValue);
2855  }
2856  else
2857  {
2858  // normal trigger
2859  ar_command_key[ar_beams[i].shock->trigger_cmdshort].triggerInputValue = triggerValue;
2860  ar_command_key[ar_beams[i].shock->trigger_cmdlong].triggerInputValue = triggerValue;
2861  }
2862  }
2863  }
2864  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_BLOCKER) // this is an enabled blocker and inside boundary
2865  {
2866  for (int scount = i + 1; scount <= i + ar_beams[i].shock->trigger_cmdlong; scount++) // (cycle blockerbeamID + 1) to (blockerbeamID + beams to release)
2867  {
2868  if (ar_beams[scount].shock && (ar_beams[scount].shock->flags & SHOCK_FLAG_ISTRIGGER)) // don't mess anything up if the user set the number too big
2869  {
2870  if (m_trigger_debug_enabled && ar_beams[scount].shock->trigger_enabled && ar_beams[i].shock->last_debug_state != 6)
2871  {
2872  LOG(" Trigger enabled. Blocker BeamID " + TOSTRING(i) + " disabled trigger " + TOSTRING(scount));
2873  ar_beams[i].shock->last_debug_state = 6;
2874  }
2875  ar_beams[scount].shock->trigger_enabled = true; // enable the triggers
2876  }
2877  }
2878  }
2879  else if (ar_beams[i].shock->flags & SHOCK_FLAG_TRG_BLOCKER_A) // this is an enabled reverse blocker and past boundary
2880  {
2881  for (int scount = i + 1; scount <= i + ar_beams[i].shock->trigger_cmdshort; scount++) // (cylce blockerbeamID +1) to (blockerbeamID + beams tob lock)
2882  {
2883  if (ar_beams[scount].shock && (ar_beams[scount].shock->flags & SHOCK_FLAG_ISTRIGGER)) // dont mess anything up if the user set the number too big
2884  {
2885  if (m_trigger_debug_enabled && !ar_beams[scount].shock->trigger_enabled && ar_beams[i].shock->last_debug_state != 10)
2886  {
2887  LOG(" Trigger disabled. Inverted Blocker BeamID " + TOSTRING(i) + " enabled trigger " + TOSTRING(scount));
2888  ar_beams[i].shock->last_debug_state = 10;
2889  }
2890  ar_beams[scount].shock->trigger_enabled = false; // disable the trigger
2891  }
2892  }
2893  }
2894  else if ((ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CMD_SWITCH) && ar_beams[i].shock->trigger_switch_state) // this is a switch that was activated and is back inside boundaries again
2895  {
2896  ar_beams[i].shock->trigger_switch_state = 0.0f; //trigger_switch reset
2897  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 7)
2898  {
2899  LOG(" Trigger switch reset. Switch BeamID " + TOSTRING(i));
2900  ar_beams[i].shock->last_debug_state = 7;
2901  }
2902  }
2903  else if ((ar_beams[i].shock->flags & SHOCK_FLAG_TRG_CMD_BLOCKER) && !ar_command_key[ar_beams[i].shock->trigger_cmdshort].trigger_cmdkeyblock_state) // this cmdkeyblocker is inside boundaries and cmdkeystate is diabled
2904  {
2905  ar_command_key[ar_beams[i].shock->trigger_cmdshort].trigger_cmdkeyblock_state = true; // activate trigger blocking
2906  if (m_trigger_debug_enabled && ar_beams[i].shock->last_debug_state != 8)
2907  {
2908  LOG(" F-key trigger blocked. Blocker BeamID " + TOSTRING(i) + " Blocked F" + TOSTRING(ar_beams[i].shock->trigger_cmdshort));
2909  ar_beams[i].shock->last_debug_state = 8;
2910  }
2911  }
2912  }
2913  }
2914 }
2915 
2916 void Actor::setAirbrakeIntensity(float intensity)
2917 {
2918  ar_airbrake_intensity = intensity;
2919  for (Airbrake* ab: ar_airbrakes)
2920  {
2921  ab->updatePosition((float)ar_airbrake_intensity / 5.0);
2922  }
2923 }
2924 
2925 // call this once per frame in order to update the skidmarks
2926 void Actor::updateSkidmarks()
2927 {
2928  for (int i = 0; i < ar_num_wheels; i++)
2929  {
2930  if (!m_skid_trails[i])
2931  continue;
2932 
2933  for (int j = 0; j < ar_wheels[i].wh_num_nodes; j++)
2934  {
2935  auto n = ar_wheels[i].wh_nodes[j];
2936  if (!n || !n->nd_has_ground_contact || n->nd_last_collision_gm == nullptr ||
2937  n->nd_last_collision_gm->fx_type != Collisions::FX_HARD)
2938  {
2939  continue;
2940  }
2941  if (n->nd_avg_collision_slip > 6.f && n->nd_last_collision_slip.squaredLength() > 9.f)
2942  {
2943  m_skid_trails[i]->update(n->AbsPosition, j, n->nd_avg_collision_slip, n->nd_last_collision_gm->name);
2944  return;
2945  }
2946  }
2947  }
2948 }
2949 
2950 void Actor::prepareInside(bool inside)
2951 {
2952  // TODO: this whole function belongs to GfxActor ~ 08/2018
2953  if (inside)
2954  {
2955  App::GetCameraManager()->GetCamera()->setNearClipDistance(0.1f);
2956 
2957  // enable transparent seat
2958  MaterialPtr seatmat = (MaterialPtr)(MaterialManager::getSingleton().getByName("driversseat"));
2959  seatmat->setDepthWriteEnabled(false);
2960  seatmat->setSceneBlending(SBT_TRANSPARENT_ALPHA);
2961  }
2962  else
2963  {
2964  if (ar_dashboard)
2965  {
2966  ar_dashboard->setVisible(false);
2967  }
2968 
2969  App::GetCameraManager()->GetCamera()->setNearClipDistance(0.5f);
2970 
2971  // disable transparent seat
2972  MaterialPtr seatmat = (MaterialPtr)(MaterialManager::getSingleton().getByName("driversseat"));
2973  seatmat->setDepthWriteEnabled(true);
2974  seatmat->setSceneBlending(SBT_REPLACE);
2975  }
2976 
2977  // TEMPORARY - until this function is moved to GfxActor ~ 08/2018
2978  // if (m_cab_scene_node != nullptr)
2979  // {
2980  // m_gfx_actor->GetCabTransMaterial()->setReceiveShadows(!inside);
2981  // }
2982 
2983  if (App::gfx_reduce_shadows->getBool())
2984  {
2985  m_gfx_actor->SetCastShadows(!inside);
2986  }
2987 }
2988 
2989 void Actor::toggleHeadlights()
2990 {
2991  // export light command
2992  ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
2993  if (ar_state == ActorState::LOCAL_SIMULATED && this == player_actor.GetRef() && ar_forward_commands)
2994  {
2995  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
2996  {
2997  if (actor->ar_state == ActorState::LOCAL_SIMULATED && this != actor.GetRef() && actor->ar_import_commands)
2998  actor->toggleHeadlights();
2999  }
3000  }
3001 
3002  // flip the flag
3003  BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_HEADLIGHT, !this->getHeadlightsVisible());
3004 
3005  // sync cab light state
3006  m_gfx_actor->SetCabLightsActive(this->getHeadlightsVisible());
3007 
3008  TRIGGER_EVENT_ASYNC(SE_TRUCK_LIGHT_TOGGLE, ar_instance_id);
3009 }
3010 
3011 void Actor::forceAllFlaresOff()
3012 {
3013  for (size_t i = 0; i < ar_flares.size(); i++)
3014  {
3015  ar_flares[i].snode->setVisible(false);
3016  }
3017 }
3018 
3019 void Actor::updateFlareStates(float dt)
3020 {
3021  if (m_flares_mode == GfxFlaresMode::NONE) { return; }
3022 
3023  for (size_t i = 0; i < this->ar_flares.size(); i++)
3024  {
3025  // let the light blink
3026  if (ar_flares[i].blinkdelay != 0)
3027  {
3028  ar_flares[i].blinkdelay_curr -= dt;
3029  if (ar_flares[i].blinkdelay_curr <= 0)
3030  {
3031  ar_flares[i].blinkdelay_curr = ar_flares[i].blinkdelay;
3032  ar_flares[i].blinkdelay_state = !ar_flares[i].blinkdelay_state;
3033  }
3034  }
3035  else
3036  {
3037  ar_flares[i].blinkdelay_state = true;
3038  }
3039 
3040  // manage light states
3041  bool isvisible = false;
3042  switch (ar_flares[i].fl_type)
3043  {
3044  case FlareType::HEADLIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_HEADLIGHT); break;
3045  case FlareType::HIGH_BEAM: isvisible = (m_lightmask & RoRnet::LIGHTMASK_HIGHBEAMS); break;
3046  case FlareType::FOG_LIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_FOGLIGHTS); break;
3047  case FlareType::SIDELIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_SIDELIGHTS); break;
3048  case FlareType::TAIL_LIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_HEADLIGHT); break;
3049  case FlareType::BRAKE_LIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_BRAKES); break;
3050  case FlareType::REVERSE_LIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_REVERSE); break;
3051  case FlareType::BLINKER_LEFT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_BLINK_LEFT || m_lightmask & RoRnet::LIGHTMASK_BLINK_WARN); break;
3052  case FlareType::BLINKER_RIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_BLINK_RIGHT || m_lightmask & RoRnet::LIGHTMASK_BLINK_WARN); break;
3053  case FlareType::DASHBOARD: isvisible = ar_dashboard->_getBool(ar_flares[i].dashboard_link); break;
3054  case FlareType::USER: isvisible = this->getCustomLightVisible(ar_flares[i].controlnumber); break;
3055  }
3056 
3057  // apply blinking
3058  isvisible = isvisible && ar_flares[i].blinkdelay_state;
3059 
3060  // update turn signal state
3061  switch (ar_flares[i].fl_type)
3062  {
3063  case FlareType::BLINKER_LEFT: m_blinker_left_lit = isvisible; break;
3064  case FlareType::BLINKER_RIGHT: m_blinker_right_lit = isvisible; break;
3065  default:;
3066  }
3067 
3068  // update light intensity
3069  if (ar_flares[i].uses_inertia)
3070  {
3071  ar_flares[i].intensity = ar_flares[i].inertia.CalcSimpleDelay(isvisible, dt);
3072  }
3073  else
3074  {
3075  ar_flares[i].intensity = (float)isvisible;
3076  }
3077  }
3078 }
3079 
3080 void Actor::toggleBlinkType(BlinkType blink)
3081 {
3082  if (this->getBlinkType() == blink)
3083  setBlinkType(BLINK_NONE);
3084  else
3085  setBlinkType(blink);
3086 }
3087 
3088 void Actor::setBlinkType(BlinkType blink)
3089 {
3090  if (ar_state == ActorState::DISPOSED)
3091  return;
3092 
3093  switch (blink)
3094  {
3095  case BlinkType::BLINK_LEFT:
3099  SOUND_START(ar_instance_id, SS_TRIG_TURN_SIGNAL);
3100  break;
3105  SOUND_START(ar_instance_id, SS_TRIG_TURN_SIGNAL);
3106  break;
3107  case BlinkType::BLINK_WARN:
3111  SOUND_START(ar_instance_id, SS_TRIG_TURN_SIGNAL);
3112  break;
3113  case BlinkType::BLINK_NONE:
3117  SOUND_STOP(ar_instance_id, SS_TRIG_TURN_SIGNAL);
3118  break;
3119  }
3120 }
3121 
3122 void Actor::autoBlinkReset()
3123 {
3124  // TODO: make this set-able per actor
3125  const float blink_lock_range = App::io_blink_lock_range->getFloat();
3126 
3127  if (this->getBlinkType() == BLINK_LEFT && ar_hydro_dir_state < -blink_lock_range)
3128  {
3129  // passed the threshold: the turn signal gets locked
3130  m_blinker_autoreset = true;
3131  }
3132 
3133  if (this->getBlinkType() == BLINK_LEFT && m_blinker_autoreset && ar_hydro_dir_state > -blink_lock_range)
3134  {
3135  // steering wheel turned back: turn signal gets automatically unlocked
3136  setBlinkType(BLINK_NONE);
3137  m_blinker_autoreset = false;
3138  }
3139 
3140  // same for the right turn signal
3141  if (this->getBlinkType() == BLINK_RIGHT && ar_hydro_dir_state > blink_lock_range)
3142  m_blinker_autoreset = true;
3143 
3144  if (this->getBlinkType() == BLINK_RIGHT && m_blinker_autoreset && ar_hydro_dir_state < blink_lock_range)
3145  {
3146  setBlinkType(BLINK_NONE);
3147  m_blinker_autoreset = false;
3148  }
3149 }
3150 
3151 void Actor::setLightStateMask(BitMask_t lightmask)
3152 {
3153  using namespace RoRnet;
3154 
3155  // Perform any special toggling logic where needed.
3156  if ((m_lightmask & LIGHTMASK_HEADLIGHT) != (lightmask & LIGHTMASK_HEADLIGHT))
3157  this->toggleHeadlights();
3158  if ((m_lightmask & LIGHTMASK_BEACONS) != (lightmask & LIGHTMASK_BEACONS))
3159  this->beaconsToggle();
3160 
3161  BlinkType btype = BLINK_NONE;
3162  if ((lightmask & LIGHTMASK_BLINK_LEFT) != 0)
3163  btype = BLINK_LEFT;
3164  else if ((lightmask & LIGHTMASK_BLINK_RIGHT) != 0)
3165  btype = BLINK_RIGHT;
3166  else if ((lightmask & LIGHTMASK_BLINK_WARN) != 0)
3167  btype = BLINK_WARN;
3168  this->setBlinkType(btype);
3169 
3170  // Update all lights at once (this overwrites the toggled lights with the same value, so it's harmless).
3171  m_lightmask = lightmask;
3172 }
3173 
3174 void Actor::toggleCustomParticles()
3175 {
3176  if (ar_state == ActorState::DISPOSED)
3177  return;
3178 
3179  m_custom_particles_enabled = !m_custom_particles_enabled;
3180  for (int i = 0; i < ar_num_custom_particles; i++)
3181  {
3182  ar_custom_particles[i].active = !ar_custom_particles[i].active;
3183  for (int j = 0; j < ar_custom_particles[i].psys->getNumEmitters(); j++)
3184  {
3185  ar_custom_particles[i].psys->getEmitter(j)->setEnabled(ar_custom_particles[i].active);
3186  }
3187  }
3188 
3189  //ScriptEvent - Particle Toggle
3191 }
3192 
3193 void Actor::updateSoundSources()
3194 {
3195 #ifdef USE_OPENAL
3196  if (App::GetSoundScriptManager()->isDisabled())
3197  return;
3198  for (int i = 0; i < ar_num_soundsources; i++)
3199  {
3200  // TODO: Investigate segfaults after terrain reloads ~ ulteq 11/2018
3201  ar_soundsources[i].ssi->setPosition(ar_nodes[ar_soundsources[i].nodenum].AbsPosition);
3202  ar_soundsources[i].ssi->setVelocity(ar_nodes[ar_soundsources[i].nodenum].Velocity);
3203  }
3204  //also this, so it is updated always, and for any vehicle
3205  SOUND_MODULATE(ar_instance_id, SS_MOD_AIRSPEED, ar_nodes[0].Velocity.length() * 1.9438);
3206  SOUND_MODULATE(ar_instance_id, SS_MOD_WHEELSPEED, ar_wheel_speed * 3.6);
3207 #endif //OPENAL
3208 }
3209 
3210 void Actor::updateVisual(float dt)
3211 {
3212  Vector3 ref(Vector3::UNIT_Y);
3213  autoBlinkReset();
3214  updateSoundSources();
3215 
3216 #ifdef USE_OPENAL
3217  //airplane radio chatter
3218  if (ar_driveable == AIRPLANE && ar_state != ActorState::LOCAL_SLEEPING)
3219  {
3220  // play random chatter at random time
3221  m_avionic_chatter_timer -= dt;
3222  if (m_avionic_chatter_timer < 0)
3223  {
3224  SOUND_PLAY_ONCE(ar_instance_id, SS_TRIG_AVICHAT01 + Math::RangeRandom(0, 12));
3225  m_avionic_chatter_timer = Math::RangeRandom(11, 30);
3226  }
3227  }
3228 #endif //openAL
3229 
3230  // update exhausts
3231  // TODO: Move to GfxActor, don't forget dt*m_simulation_speed
3232  if (ar_engine && exhausts.size() > 0)
3233  {
3234  std::vector<exhaust_t>::iterator it;
3235  for (it = exhausts.begin(); it != exhausts.end(); it++)
3236  {
3237  if (!it->smoker)
3238  continue;
3239  Vector3 dir = ar_nodes[it->emitterNode].AbsPosition - ar_nodes[it->directionNode].AbsPosition;
3240  // dir.normalise();
3241  ParticleEmitter* emit = it->smoker->getEmitter(0);
3242  it->smokeNode->setPosition(ar_nodes[it->emitterNode].AbsPosition);
3243  emit->setDirection(dir);
3244  if (!m_disable_smoke && ar_engine->GetSmoke() != -1.0)
3245  {
3246  emit->setEnabled(true);
3247  emit->setColour(ColourValue(0.0, 0.0, 0.0, 0.02 + ar_engine->GetSmoke() * 0.06));
3248  emit->setTimeToLive((0.02 + ar_engine->GetSmoke() * 0.06) / 0.04);
3249  }
3250  else
3251  {
3252  emit->setEnabled(false);
3253  }
3254  emit->setParticleVelocity(1.0 + ar_engine->GetSmoke() * 2.0, 2.0 + ar_engine->GetSmoke() * 3.0);
3255  }
3256  }
3257 
3258  // Wings (only physics, graphics are updated in GfxActor)
3259  float autoaileron = 0;
3260  float autorudder = 0;
3261  float autoelevator = 0;
3262  if (ar_autopilot)
3263  {
3264  ar_autopilot->UpdateIls(App::GetGameContext()->GetTerrain()->getObjectManager()->GetLocalizers());
3265  autoaileron = ar_autopilot->getAilerons();
3266  autorudder = ar_autopilot->getRudder();
3267  autoelevator = ar_autopilot->getElevator();
3268  ar_autopilot->gpws_update(ar_posnode_spawn_height);
3269  }
3270  autoaileron += ar_aileron;
3271  autorudder += ar_rudder;
3272  autoelevator += ar_elevator;
3273  if (autoaileron < -1.0)
3274  autoaileron = -1.0;
3275  if (autoaileron > 1.0)
3276  autoaileron = 1.0;
3277  if (autorudder < -1.0)
3278  autorudder = -1.0;
3279  if (autorudder > 1.0)
3280  autorudder = 1.0;
3281  if (autoelevator < -1.0)
3282  autoelevator = -1.0;
3283  if (autoelevator > 1.0)
3284  autoelevator = 1.0;
3285  for (int i = 0; i < ar_num_wings; i++)
3286  {
3287  if (ar_wings[i].fa->type == 'a')
3288  ar_wings[i].fa->setControlDeflection(autoaileron);
3289  if (ar_wings[i].fa->type == 'b')
3290  ar_wings[i].fa->setControlDeflection(-autoaileron);
3291  if (ar_wings[i].fa->type == 'r')
3292  ar_wings[i].fa->setControlDeflection(autorudder);
3293  if (ar_wings[i].fa->type == 'e' || ar_wings[i].fa->type == 'S' || ar_wings[i].fa->type == 'T')
3294  ar_wings[i].fa->setControlDeflection(autoelevator);
3295  if (ar_wings[i].fa->type == 'f')
3296  ar_wings[i].fa->setControlDeflection(FLAP_ANGLES[ar_aerial_flap]);
3297  if (ar_wings[i].fa->type == 'c' || ar_wings[i].fa->type == 'V')
3298  ar_wings[i].fa->setControlDeflection((autoaileron + autoelevator) / 2.0);
3299  if (ar_wings[i].fa->type == 'd' || ar_wings[i].fa->type == 'U')
3300  ar_wings[i].fa->setControlDeflection((-autoaileron + autoelevator) / 2.0);
3301  if (ar_wings[i].fa->type == 'g')
3302  ar_wings[i].fa->setControlDeflection((autoaileron + FLAP_ANGLES[ar_aerial_flap]) / 2.0);
3303  if (ar_wings[i].fa->type == 'h')
3304  ar_wings[i].fa->setControlDeflection((-autoaileron + FLAP_ANGLES[ar_aerial_flap]) / 2.0);
3305  if (ar_wings[i].fa->type == 'i')
3306  ar_wings[i].fa->setControlDeflection((-autoelevator + autorudder) / 2.0);
3307  if (ar_wings[i].fa->type == 'j')
3308  ar_wings[i].fa->setControlDeflection((autoelevator + autorudder) / 2.0);
3309  ar_wings[i].fa->updateVerticesPhysics(); // Actual graphics update moved to GfxActor
3310  }
3311  //setup commands for hydros
3312  ar_hydro_aileron_command = autoaileron;
3313  ar_hydro_rudder_command = autorudder;
3314  ar_hydro_elevator_command = autoelevator;
3315 }
3316 
3317 void Actor::AddInterActorBeam(beam_t* beam, ActorPtr other, ActorLinkingRequestType type)
3318 {
3319  // We can't assert the beam setup here because ropes do it differently (not actually using inter-beams, just exhibiting the same gamelogic).
3320  beam->bm_locked_actor = other; // This isn't entirely valid for 'ropes' either, but for compatibility I won't touch it now ~ ohlidalp, 2024
3321 
3322  auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam);
3323  ROR_ASSERT(pos == ar_inter_beams.end());
3324  if (pos == ar_inter_beams.end())
3325  {
3326  ar_inter_beams.push_back(beam);
3327  }
3328 
3329  const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3330  ROR_ASSERT(App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam) == App::GetGameContext()->GetActorManager()->inter_actor_links.end());
3331  std::pair<ActorPtr, ActorPtr> actor_pair(this, other);
3332  App::GetGameContext()->GetActorManager()->inter_actor_links[beam] = actor_pair;
3333  const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3334 
3335  if (linked_before != linked_now)
3336  {
3337  // Update lists of directly/indirectly linked actors.
3338  this->DetermineLinkedActors();
3339  for (ActorPtr& actor : this->ar_linked_actors)
3340  actor->DetermineLinkedActors();
3341 
3342  other->DetermineLinkedActors();
3343  for (ActorPtr& actor : other->ar_linked_actors)
3344  actor->DetermineLinkedActors();
3345 
3346  // Forward toggled states.
3347  for (ActorPtr& actor : this->ar_linked_actors)
3348  {
3349  actor->ar_physics_paused = this->ar_physics_paused;
3350  actor->GetGfxActor()->SetDebugView(this->GetGfxActor()->GetDebugView());
3351  }
3352 
3353  // Let scripts know.
3354  TRIGGER_EVENT_ASYNC(SE_GENERIC_TRUCK_LINKING_CHANGED, 1, (int)type, this->ar_instance_id, other->ar_instance_id);
3355  }
3356 }
3357 
3358 void Actor::RemoveInterActorBeam(beam_t* beam, ActorLinkingRequestType type)
3359 {
3360  ROR_ASSERT(beam->bm_locked_actor);
3361  ActorPtr other = beam->bm_locked_actor;
3362  beam->bm_locked_actor = nullptr;
3363 
3364  auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam);
3365  ROR_ASSERT(pos != ar_inter_beams.end());
3366  if (pos != ar_inter_beams.end())
3367  {
3368  ar_inter_beams.erase(pos);
3369  }
3370 
3371  const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3372  auto it = App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam);
3373  ROR_ASSERT(it != App::GetGameContext()->GetActorManager()->inter_actor_links.end());
3374  if (it != App::GetGameContext()->GetActorManager()->inter_actor_links.end())
3375  {
3377  }
3378  const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3379 
3380  if (linked_before != linked_now)
3381  {
3382  // Update lists of directly/indirectly linked actors.
3383  this->DetermineLinkedActors();
3384  for (ActorPtr& actor : this->ar_linked_actors)
3385  actor->DetermineLinkedActors();
3386 
3387  other->DetermineLinkedActors();
3388  for (ActorPtr& actor : other->ar_linked_actors)
3389  actor->DetermineLinkedActors();
3390 
3391  // Reset toggled states.
3392  other->ar_physics_paused = false;
3393  other->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE);
3394  for (ActorPtr& actor : other->ar_linked_actors)
3395  {
3396  actor->ar_physics_paused = false;
3397  actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE);
3398  }
3399 
3400  // Let scripts know.
3401  TRIGGER_EVENT_ASYNC(SE_GENERIC_TRUCK_LINKING_CHANGED, 0, (int)type, this->ar_instance_id, other->ar_instance_id);
3402  }
3403 }
3404 
3405 void Actor::DisjoinInterActorBeams()
3406 {
3407  // Helper for `MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`, do not invoke otherwise!
3408  // Removes all (both ways) inter-actor connections from this actor.
3409  // Note the repetitive 'OK to be invoked...' comments are for fulltext search results.
3410  // ------------------------------------------------------------------
3411 
3412  // Remove all inter-linking beams which belong to this actor.
3413  this->hookToggle(-1, ActorLinkingRequestType::HOOK_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3414  this->ropeToggle(-1, ActorLinkingRequestType::ROPE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3415  this->tieToggle(-1, ActorLinkingRequestType::TIE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3416 
3417  // Remove any possible links from other actors to this actor.
3418  for (ActorPtr& other_actor : App::GetGameContext()->GetActorManager()->GetActors())
3419  {
3420  if (other_actor->ar_state != ActorState::LOCAL_SIMULATED)
3421  continue;
3422 
3423  // Use the new `unlock_filter` param to only unlock the links to this actor (brute force but safe approach).
3424  other_actor->hookToggle(-1, ActorLinkingRequestType::HOOK_RESET, NODENUM_INVALID, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3425  other_actor->tieToggle(-1, ActorLinkingRequestType::TIE_RESET, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3426  other_actor->ropeToggle(-1, ActorLinkingRequestType::ROPE_RESET, /*unlock_filter:*/ar_instance_id); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3427  }
3428 }
3429 
3430 void Actor::tieToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter)
3431 {
3432  ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3433 
3434  // untie all ties if one is tied
3435  bool istied = false;
3436 
3437  for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
3438  {
3439  // only handle ties with correct group
3440  if (group != -1 && (it->ti_group != -1 && it->ti_group != group))
3441  continue;
3442 
3443  // When RESET-ing, filter by the locked actor, if specified.
3444  if (mode == ActorLinkingRequestType::TIE_RESET
3445  && forceunlock_filter != ACTORINSTANCEID_INVALID && it->ti_locked_actor && it->ti_locked_actor->ar_instance_id != forceunlock_filter)
3446  {
3447  continue;
3448  }
3449 
3450  // if tied, untie it.
3451  if (it->ti_tied)
3452  {
3453  istied = !it->ti_beam->bm_disabled;
3454 
3455  // tie is locked and should get unlocked and stop tying
3456  it->ti_tied = false;
3457  it->ti_tying = false;
3458  if (it->ti_locked_ropable)
3459  it->ti_locked_ropable->attached_ties--;
3460  // disable the ties beam
3461  it->ti_beam->p2 = &ar_nodes[0];
3462  it->ti_beam->bm_inter_actor = false;
3463  it->ti_beam->bm_disabled = true;
3464  if (it->ti_locked_actor != this)
3465  {
3466  this->RemoveInterActorBeam(it->ti_beam, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3467  }
3468  it->ti_locked_actor = nullptr;
3469  }
3470  }
3471 
3472  // iterate over all ties
3473  if (!istied && mode == ActorLinkingRequestType::TIE_TOGGLE)
3474  {
3475  for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
3476  {
3477  // only handle ties with correct group
3478  if (group != -1 && (it->ti_group != -1 && it->ti_group != group))
3479  continue;
3480 
3481  if (!it->ti_tied)
3482  {
3483  // tie is unlocked and should get locked, search new remote ropable to lock to
3484  float mindist = it->ti_beam->refL;
3485  node_t* nearest_node = 0;
3486  ActorPtr nearest_actor = 0;
3487  ropable_t* locktedto = 0;
3488  // iterate over all actors
3489  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
3490  {
3491  if (actor->ar_state == ActorState::LOCAL_SLEEPING ||
3492  (actor == this && it->ti_no_self_lock))
3493  {
3494  continue;
3495  }
3496 
3497  // and their ropables
3498  for (std::vector<ropable_t>::iterator itr = actor->ar_ropables.begin(); itr != actor->ar_ropables.end(); itr++)
3499  {
3500  // if the ropable is not multilock and used, then discard this ropable
3501  if (!itr->multilock && itr->attached_ties > 0)
3502  continue;
3503 
3504  // skip if tienode is ropable too (no selflock)
3505  if (this == actor.GetRef() && itr->node->pos == it->ti_beam->p1->pos)
3506  continue;
3507 
3508  // calculate the distance and record the nearest ropable
3509  float dist = (it->ti_beam->p1->AbsPosition - itr->node->AbsPosition).length();
3510  if (dist < mindist)
3511  {
3512  mindist = dist;
3513  nearest_node = itr->node;
3514  nearest_actor = actor;
3515  locktedto = &(*itr);
3516  }
3517  }
3518  }
3519  // if we found a ropable, then tie towards it
3520  if (nearest_node)
3521  {
3522  // enable the beam and visually display the beam
3523  it->ti_beam->bm_disabled = false;
3524  // now trigger the tying action
3525  it->ti_locked_actor = nearest_actor;
3526  it->ti_beam->p2 = nearest_node;
3527  it->ti_beam->bm_inter_actor = nearest_actor != this;
3528  it->ti_beam->stress = 0;
3529  it->ti_beam->L = it->ti_beam->refL;
3530  it->ti_tied = true;
3531  it->ti_tying = true;
3532  it->ti_locked_ropable = locktedto;
3533  it->ti_locked_ropable->attached_ties++;
3534  if (it->ti_beam->bm_inter_actor)
3535  {
3536  this->AddInterActorBeam(it->ti_beam, nearest_actor, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3537  }
3538  }
3539  }
3540  }
3541  }
3542 
3543  //ScriptEvent - Tie toggle
3544  TRIGGER_EVENT_ASYNC(SE_TRUCK_TIE_TOGGLE, ar_instance_id);
3545 }
3546 
3547 void Actor::ropeToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter)
3548 {
3549  ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3550 
3551  // iterate over all ropes
3552  for (std::vector<rope_t>::iterator it = ar_ropes.begin(); it != ar_ropes.end(); it++)
3553  {
3554  // only handle ropes with correct group
3555  if (group != -1 && (it->rp_group != -1 && it->rp_group != group))
3556  continue;
3557 
3558  // When RESET-ing, filter by the locked actor, if specified.
3559  if (mode == ActorLinkingRequestType::ROPE_RESET
3560  && forceunlock_filter != ACTORINSTANCEID_INVALID && it->rp_locked_actor && it->rp_locked_actor->ar_instance_id != forceunlock_filter)
3561  {
3562  continue;
3563  }
3564 
3565  if (it->rp_locked == LOCKED || it->rp_locked == PRELOCK) // Do this for both `ROPE_TOGGLE` and `ROPE_RESET`
3566  {
3567  // we unlock ropes
3568  it->rp_locked = UNLOCKED;
3569  // remove node locking
3570  if (it->rp_locked_ropable)
3571  it->rp_locked_ropable->attached_ropes--;
3572  if (it->rp_locked_actor != this)
3573  {
3574  this->RemoveInterActorBeam(it->rp_beam, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3575  }
3576  it->rp_locked_actor = nullptr;
3577  it->rp_locked_ropable = nullptr;
3578  }
3579  else if (mode == ActorLinkingRequestType::ROPE_TOGGLE) // Do this only for `ROPE_TOGGLE`
3580  {
3581  //we lock ropes
3582  // search new remote ropable to lock to
3583  float mindist = it->rp_beam->L;
3584  ActorPtr nearest_actor = nullptr;
3585  ropable_t* rop = 0;
3586  // iterate over all actor_slots
3587  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
3588  {
3589  if (actor->ar_state == ActorState::LOCAL_SLEEPING)
3590  continue;
3591  // and their ropables
3592  for (std::vector<ropable_t>::iterator itr = actor->ar_ropables.begin(); itr != actor->ar_ropables.end(); itr++)
3593  {
3594  // if the ropable is not multilock and used, then discard this ropable
3595  if (!itr->multilock && itr->attached_ropes > 0)
3596  continue;
3597 
3598  // calculate the distance and record the nearest ropable
3599  float dist = (it->rp_beam->p1->AbsPosition - itr->node->AbsPosition).length();
3600  if (dist < mindist)
3601  {
3602  mindist = dist;
3603  nearest_actor = actor;
3604  rop = &(*itr);
3605  }
3606  }
3607  }
3608  // if we found a ropable, then lock it
3609  if (nearest_actor)
3610  {
3611  //okay, we have found a rope to tie
3612  it->rp_locked = LOCKED;
3613  it->rp_locked_ropable = rop;
3614  it->rp_locked_ropable->attached_ropes++;
3615  if (nearest_actor != this)
3616  {
3617  this->AddInterActorBeam(it->rp_beam, nearest_actor, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3618  }
3619  }
3620  }
3621  }
3622 }
3623 
3624 void Actor::hookToggle(int group, ActorLinkingRequestType mode, NodeNum_t mousenode /*=NODENUM_INVALID*/, ActorInstanceID_t forceunlock_filter)
3625 {
3626  ROR_ASSERT(mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK
3627  || mode == ActorLinkingRequestType::HOOK_TOGGLE || mode == ActorLinkingRequestType::HOOK_MOUSE_TOGGLE
3628  || mode == ActorLinkingRequestType::HOOK_RESET);
3629 
3630  // iterate over all hooks
3631  for (std::vector<hook_t>::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++)
3632  {
3633  if (mode == ActorLinkingRequestType::HOOK_MOUSE_TOGGLE && it->hk_hook_node->pos != mousenode)
3634  {
3635  //skip all other nodes except the one manually toggled by mouse
3636  continue;
3637  }
3638  if (mode == ActorLinkingRequestType::HOOK_TOGGLE && group == -1)
3639  {
3640  //manually triggerd (EV_COMMON_LOCK). Toggle all hooks groups with group#: -1, 0, 1 ++
3641  if (it->hk_group <= -2)
3642  continue;
3643  }
3644  if (mode == ActorLinkingRequestType::HOOK_LOCK && group == -2)
3645  {
3646  //automatic lock attempt (cyclic with doupdate). Toggle all hooks groups with group#: -2, -3, -4 --, skip the ones which are not autolock (triggered only)
3647  if (it->hk_group >= -1 || !it->hk_autolock)
3648  continue;
3649  }
3650  if (mode == ActorLinkingRequestType::HOOK_UNLOCK && group == -2)
3651  {
3652  //manual unlock ALL autolock and triggerlock, do not unlock standard hooks (EV_COMMON_AUTOLOCK)
3653  if (it->hk_group >= -1 || !it->hk_autolock)
3654  continue;
3655  }
3656  if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group <= -3)
3657  {
3658  //trigger beam lock or unlock. Toggle one hook group with group#: group
3659  if (it->hk_group != group)
3660  continue;
3661  }
3662  if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group >= -1)
3663  {
3664  continue;
3665  }
3666  if (mode == ActorLinkingRequestType::HOOK_LOCK && it->hk_timer > 0.0f)
3667  {
3668  //check relock delay timer for autolock nodes and skip if not 0
3669  continue;
3670  }
3671 
3672  // When RESET-ing, filter by the locked actor, if specified.
3673  if (mode == ActorLinkingRequestType::HOOK_RESET
3674  && forceunlock_filter != ACTORINSTANCEID_INVALID && it->hk_locked_actor && it->hk_locked_actor->ar_instance_id != forceunlock_filter)
3675  {
3676  continue;
3677  }
3678 
3679  ActorPtr prev_locked_actor = it->hk_locked_actor; // memorize current value
3680 
3681  // do this only for toggle or lock attempts, skip prelocked or locked nodes for performance
3682  if ((mode != ActorLinkingRequestType::HOOK_UNLOCK && mode != ActorLinkingRequestType::HOOK_RESET) && it->hk_locked == UNLOCKED)
3683  {
3684  // we lock hooks
3685  // search new remote ropable to lock to
3686  float mindist = it->hk_lockrange;
3687  float distance = 100000000.0f;
3688  // iterate over all actor_slots
3689  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
3690  {
3691  if (actor->ar_state == ActorState::LOCAL_SLEEPING)
3692  continue;
3693  if (this == actor.GetRef() && !it->hk_selflock)
3694  continue; // don't lock to self
3695 
3696  node_t* nearest_node = nullptr;
3697  for (int i = 0; i < actor->ar_num_nodes; i++)
3698  {
3699  // skip all nodes with lockgroup 9999 (deny lock)
3700  if (actor->ar_nodes[i].nd_lockgroup == 9999)
3701  continue;
3702 
3703  // exclude this truck and its current hooknode from the locking search
3704  if (this == actor.GetRef() && i == it->hk_hook_node->pos)
3705  continue;
3706 
3707  // a lockgroup for this hooknode is set -> skip all nodes that do not have the same lockgroup (-1 = default(all nodes))
3708  if (it->hk_lockgroup != -1 && it->hk_lockgroup != actor->ar_nodes[i].nd_lockgroup)
3709  continue;
3710 
3711  // measure distance
3712  float n2n_distance = (it->hk_hook_node->AbsPosition - actor->ar_nodes[i].AbsPosition).length();
3713  if (n2n_distance < mindist)
3714  {
3715  if (distance >= n2n_distance)
3716  {
3717  // located a node that is closer
3718  distance = n2n_distance;
3719  nearest_node = &actor->ar_nodes[i];
3720  }
3721  }
3722  }
3723  if (nearest_node)
3724  {
3725  // we found a node, lock to it
3726  it->hk_lock_node = nearest_node;
3727  it->hk_locked_actor = actor;
3728  it->hk_locked = PRELOCK;
3729  //enable beam if not enabled yet between those 2 nodes
3730  if (it->hk_beam->bm_disabled)
3731  {
3732  it->hk_beam->p2 = it->hk_lock_node;
3733  it->hk_beam->bm_inter_actor = (it->hk_locked_actor != nullptr);
3734  it->hk_beam->L = (it->hk_hook_node->AbsPosition - it->hk_lock_node->AbsPosition).length();
3735  it->hk_beam->bm_disabled = false;
3736  this->AddInterActorBeam(it->hk_beam, it->hk_locked_actor, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3737  }
3738  }
3739  }
3740  }
3741  // this is a locked or prelocked hook and its not a locking attempt or the locked actor was removed (bm_inter_actor == false)
3742  else if ((it->hk_locked == LOCKED || it->hk_locked == PRELOCK) && (mode != ActorLinkingRequestType::HOOK_LOCK || !it->hk_beam->bm_inter_actor))
3743  {
3744  // we unlock ropes immediatelly
3745  it->hk_locked = UNLOCKED;
3746  this->RemoveInterActorBeam(it->hk_beam, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3747  if (it->hk_group <= -2)
3748  {
3749  it->hk_timer = it->hk_timer_preset; //timer reset for autolock nodes
3750  }
3751  it->hk_lock_node = 0;
3752  it->hk_locked_actor = 0;
3753  //disable hook-assistance beam
3754  it->hk_beam->p2 = &ar_nodes[0];
3755  it->hk_beam->bm_inter_actor = false;
3756  it->hk_beam->L = (ar_nodes[0].AbsPosition - it->hk_hook_node->AbsPosition).length();
3757  it->hk_beam->bm_disabled = true;
3758  }
3759  }
3760 }
3761 
3762 void Actor::parkingbrakeToggle()
3763 {
3764  if (ar_state == ActorState::DISPOSED)
3765  return;
3766 
3767  ar_parking_brake = !ar_parking_brake;
3768 
3769  if (ar_parking_brake)
3770  SOUND_START(ar_instance_id, SS_TRIG_PARK);
3771  else
3772  SOUND_STOP(ar_instance_id, SS_TRIG_PARK);
3773 
3775 }
3776 
3777 void Actor::antilockbrakeToggle()
3778 {
3779  if (ar_state == ActorState::DISPOSED)
3780  return;
3781 
3782  if (!alb_notoggle)
3783  alb_mode = !alb_mode;
3784 }
3785 
3786 void Actor::tractioncontrolToggle()
3787 {
3788  if (ar_state == ActorState::DISPOSED)
3789  return;
3790 
3791  if (!tc_notoggle)
3792  tc_mode = !tc_mode;
3793 }
3794 
3795 void Actor::beaconsToggle()
3796 {
3797  if (ar_state == ActorState::DISPOSED)
3798  return;
3799 
3800  if (m_flares_mode == GfxFlaresMode::NONE)
3801  {
3802  return;
3803  }
3804  // flip the flag
3805  BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_BEACONS, !this->getBeaconMode());
3806 
3807  //ScriptEvent - Beacon toggle
3809 }
3810 
3811 void Actor::muteAllSounds()
3812 {
3813 #ifdef USE_OPENAL
3814  if (ar_state == ActorState::DISPOSED)
3815  return;
3816 
3817  for (int i = 0; i < ar_num_soundsources; i++)
3818  {
3819  if (ar_soundsources[i].ssi)
3820  ar_soundsources[i].ssi->setEnabled(false);
3821  }
3822 #endif // USE_OPENAL
3823 }
3824 
3825 void Actor::unmuteAllSounds()
3826 {
3827 #ifdef USE_OPENAL
3828  if (ar_state == ActorState::DISPOSED)
3829  return;
3830 
3831  for (int i = 0; i < ar_num_soundsources; i++)
3832  {
3833  bool enabled = (ar_soundsources[i].type == -2 || ar_soundsources[i].type == ar_current_cinecam);
3834  ar_soundsources[i].ssi->setEnabled(enabled);
3835  }
3836 #endif // USE_OPENAL
3837 }
3838 
3839 void Actor::NotifyActorCameraChanged()
3840 {
3841  // change sound setup
3842 #ifdef USE_OPENAL
3843  if (ar_state == ActorState::DISPOSED)
3844  return;
3845 
3846  for (int i = 0; i < ar_num_soundsources; i++)
3847  {
3848  bool enabled = (ar_soundsources[i].type == -2 || ar_soundsources[i].type == ar_current_cinecam);
3849  ar_soundsources[i].ssi->setEnabled(enabled);
3850  }
3851 #endif // USE_OPENAL
3852 
3853  // NOTE: Prop visibility now updated in GfxActor::UpdateProps() ~ only_a_ptr, 06/2018
3854 
3855 
3856 }
3857 
3858 //Returns the number of active (non bounded) beams connected to a node
3859 int Actor::GetNumActiveConnectedBeams(int nodeid)
3860 {
3861  int totallivebeams = 0;
3862  for (unsigned int ni = 0; ni < ar_node_to_beam_connections[nodeid].size(); ++ni)
3863  {
3864  if (!ar_beams[ar_node_to_beam_connections[nodeid][ni]].bm_disabled && !ar_beams[ar_node_to_beam_connections[nodeid][ni]].bounded)
3865  totallivebeams++;
3866  }
3867  return totallivebeams;
3868 }
3869 
3870 bool Actor::isTied()
3871 {
3872  for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
3873  if (it->ti_tied)
3874  return true;
3875  return false;
3876 }
3877 
3878 bool Actor::isLocked()
3879 {
3880  for (std::vector<hook_t>::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++)
3881  if (it->hk_locked == LOCKED)
3882  return true;
3883  return false;
3884 }
3885 
3886 void Actor::updateDashBoards(float dt)
3887 {
3888  if (!ar_dashboard)
3889  return;
3890  // some temp vars
3891  Vector3 dir;
3892 
3893  // engine and gears
3894  if (ar_engine)
3895  {
3896  // gears first
3897  int gear = ar_engine->GetGear();
3898  ar_dashboard->setInt(DD_ENGINE_GEAR, gear);
3899 
3900  int numGears = (int)ar_engine->getNumGears();
3901  ar_dashboard->setInt(DD_ENGINE_NUM_GEAR, numGears);
3902 
3903  String str = String();
3904 
3905  // now construct that classic gear string
3906  if (gear > 0)
3907  str = TOSTRING(gear) + "/" + TOSTRING(numGears);
3908  else if (gear == 0)
3909  str = String("N");
3910  else
3911  str = String("R");
3912 
3913  ar_dashboard->setChar(DD_ENGINE_GEAR_STRING, str.c_str());
3914 
3915  // R N D 2 1 String
3916  int cg = ar_engine->getAutoShift();
3917  if (cg != EngineSim::MANUALMODE)
3918  {
3919  str = ((cg == EngineSim::REAR) ? "#ffffff" : "#868686") + String("R\n");
3920  str += ((cg == EngineSim::NEUTRAL) ? "#ff0012" : "#8a000a") + String("N\n");
3921  str += ((cg == EngineSim::DRIVE) ? "#12ff00" : "#248c00") + String("D\n");
3922  str += ((cg == EngineSim::TWO) ? "#ffffff" : "#868686") + String("2\n");
3923  str += ((cg == EngineSim::ONE) ? "#ffffff" : "#868686") + String("1");
3924  }
3925  else
3926  {
3927  //str = "#b8b8b8M\na\nn\nu\na\nl";
3928  str = "#b8b8b8M\na\nn\nu";
3929  }
3930  ar_dashboard->setChar(DD_ENGINE_AUTOGEAR_STRING, str.c_str());
3931 
3932  // autogears
3933  int autoGear = ar_engine->getAutoShift();
3934  ar_dashboard->setInt(DD_ENGINE_AUTO_GEAR, autoGear);
3935 
3936  // clutch
3937  float clutch = ar_engine->GetClutch();
3938  ar_dashboard->setFloat(DD_ENGINE_CLUTCH, clutch);
3939 
3940  // accelerator
3941  float acc = ar_engine->GetAcceleration();
3942  ar_dashboard->setFloat(DD_ACCELERATOR, acc);
3943 
3944  // RPM
3945  float rpm = ar_engine->GetEngineRpm();
3946  ar_dashboard->setFloat(DD_ENGINE_RPM, rpm);
3947 
3948  // turbo
3949  float turbo = ar_engine->GetTurboPsi() * 3.34f; // MAGIC :/
3950  ar_dashboard->setFloat(DD_ENGINE_TURBO, turbo);
3951 
3952  // ignition
3953  bool ign = (ar_engine->hasContact() && !ar_engine->isRunning());
3954  ar_dashboard->setBool(DD_ENGINE_IGNITION, ign);
3955 
3956  // battery
3957  bool batt = (ar_engine->hasContact() && !ar_engine->isRunning());
3958  ar_dashboard->setBool(DD_ENGINE_BATTERY, batt);
3959 
3960  // clutch warning
3961  bool cw = (fabs(ar_engine->GetTorque()) >= ar_engine->GetClutchForce() * 10.0f);
3962  ar_dashboard->setBool(DD_ENGINE_CLUTCH_WARNING, cw);
3963  }
3964 
3965  // brake
3966  ar_dashboard->setFloat(DD_BRAKE, ar_brake);
3967 
3968  Vector3 cam_dir = this->GetCameraDir();
3969  Vector3 cam_roll = this->GetCameraRoll();
3970 
3971  // speedo
3972  float velocity = ar_nodes[0].Velocity.length();
3973  if (cam_dir != Vector3::ZERO)
3974  {
3975  velocity = cam_dir.dotProduct(ar_nodes[0].Velocity);
3976  }
3977 
3978  // KPH
3979  float cur_speed_kph = ar_wheel_speed * 3.6f;
3980  float smooth_kph = (cur_speed_kph * 0.3) + (ar_dashboard->_getFloat(DD_ENGINE_SPEEDO_KPH) * 0.7);
3981  ar_dashboard->setFloat(DD_ENGINE_SPEEDO_KPH, smooth_kph);
3982 
3983  // MPH
3984  float cur_speed_mph = ar_wheel_speed * 2.23693629f;
3985  float smooth_mph = (cur_speed_mph * 0.3) + (ar_dashboard->_getFloat(DD_ENGINE_SPEEDO_MPH) * 0.7);
3986  ar_dashboard->setFloat(DD_ENGINE_SPEEDO_MPH, smooth_mph);
3987 
3988  // roll
3989  if (cam_roll != Vector3::ZERO)
3990  {
3991  float angle = asin(cam_roll.dotProduct(Vector3::UNIT_Y));
3992  if (angle < -1)
3993  angle = -1;
3994  if (angle > 1)
3995  angle = 1;
3996 
3997  float f = Radian(angle).valueDegrees();
3998  ar_dashboard->setFloat(DD_ROLL, f);
3999  }
4000 
4001  // active shocks / roll correction
4002  if (this->ar_has_active_shocks)
4003  {
4004  // TOFIX: certainly not working:
4005  float roll_corr = - m_stabilizer_shock_ratio * 10.0f;
4006  ar_dashboard->setFloat(DD_ROLL_CORR, roll_corr);
4007 
4008  bool corr_active = (m_stabilizer_shock_request > 0);
4009  ar_dashboard->setBool(DD_ROLL_CORR_ACTIVE, corr_active);
4010  }
4011 
4012  // pitch
4013  if (cam_dir != Vector3::ZERO)
4014  {
4015  float angle = asin(cam_dir.dotProduct(Vector3::UNIT_Y));
4016  if (angle < -1)
4017  angle = -1;
4018  if (angle > 1)
4019  angle = 1;
4020 
4021  float f = Radian(angle).valueDegrees();
4022  ar_dashboard->setFloat(DD_PITCH, f);
4023  }
4024 
4025  // parking brake
4026  ar_dashboard->setBool(DD_PARKINGBRAKE, ar_parking_brake);
4027 
4028  // locked lamp
4029  bool locked = isLocked();
4030  ar_dashboard->setBool(DD_LOCKED, locked);
4031 
4032  // low pressure lamp
4033  bool low_pres = !ar_engine_hydraulics_ready;
4034  ar_dashboard->setBool(DD_LOW_PRESSURE, low_pres);
4035 
4036  // turn signals already done by `updateFlareStates()` and `setBlinkType()`
4037 
4038  // Traction Control
4039  if (!tc_nodash)
4040  {
4041  int dash_tc_mode = 1; // 0 = not present, 1 = off, 2 = on, 3 = active
4042  if (tc_mode)
4043  {
4044  if (m_tractioncontrol)
4045  dash_tc_mode = 3;
4046  else
4047  dash_tc_mode = 2;
4048  }
4049  ar_dashboard->setInt(DD_TRACTIONCONTROL_MODE, dash_tc_mode);
4050  }
4051 
4052  // Anti Lock Brake
4053  if (!alb_nodash)
4054  {
4055  int dash_alb_mode = 1; // 0 = not present, 1 = off, 2 = on, 3 = active
4056  if (alb_mode)
4057  {
4058  if (m_antilockbrake)
4059  dash_alb_mode = 3;
4060  else
4061  dash_alb_mode = 2;
4062  }
4063  ar_dashboard->setInt(DD_ANTILOCKBRAKE_MODE, dash_alb_mode);
4064  }
4065 
4066  // load secured lamp
4067  int ties_mode = 0; // 0 = not locked, 1 = prelock, 2 = lock
4068  if (isTied())
4069  {
4070  if (fabs(ar_command_key[0].commandValue) > 0.000001f)
4071  ties_mode = 1;
4072  else
4073  ties_mode = 2;
4074  }
4075  ar_dashboard->setInt(DD_TIES_MODE, ties_mode);
4076 
4077  // Boat things now: screwprops and alike
4078  if (ar_num_screwprops)
4079  {
4080  // the throttle and rudder
4081  for (int i = 0; i < ar_num_screwprops && i < DD_MAX_SCREWPROP; i++)
4082  {
4083  float throttle = ar_screwprops[i]->getThrottle();
4084  ar_dashboard->setFloat(DD_SCREW_THROTTLE_0 + i, throttle);
4085 
4086  float steering = ar_screwprops[i]->getRudder();
4087  ar_dashboard->setFloat(DD_SCREW_STEER_0 + i, steering);
4088  }
4089 
4090  // water depth display, only if we have a screw prop at least
4091  float depth = this->getHeightAboveGround();
4092  ar_dashboard->setFloat(DD_WATER_DEPTH, depth);
4093 
4094  // water speed
4095  Vector3 hdir = this->GetCameraDir();
4096  float knots = hdir.dotProduct(ar_nodes[ar_main_camera_node_pos].Velocity) * 1.9438f; // 1.943 = m/s in knots/s
4097  ar_dashboard->setFloat(DD_WATER_SPEED, knots);
4098  }
4099 
4100  // now airplane things, aeroengines, etc.
4101  if (ar_num_aeroengines)
4102  {
4103  for (int i = 0; i < ar_num_aeroengines && i < DD_MAX_AEROENGINE; i++)
4104  {
4105  float throttle = ar_aeroengines[i]->getThrottle();
4106  ar_dashboard->setFloat(DD_AEROENGINE_THROTTLE_0 + i, throttle);
4107 
4108  bool failed = ar_aeroengines[i]->isFailed();
4109  ar_dashboard->setBool(DD_AEROENGINE_FAILED_0 + i, failed);
4110 
4111  float pcent = ar_aeroengines[i]->getRPMpc();
4112  ar_dashboard->setFloat(DD_AEROENGINE_RPM_0 + i, pcent);
4113  }
4114  }
4115 
4116  // wings stuff, you dont need an aeroengine
4117  if (ar_num_wings)
4118  {
4119  for (int i = 0; i < ar_num_wings && i < DD_MAX_WING; i++)
4120  {
4121  // Angle of Attack (AOA)
4122  float aoa = ar_wings[i].fa->aoa;
4123  ar_dashboard->setFloat(DD_WING_AOA_0 + i, aoa);
4124  }
4125  }
4126 
4127  // some things only activate when a wing or an aeroengine is present
4128  if (ar_num_wings || ar_num_aeroengines)
4129  {
4130  //airspeed
4131  {
4132  float ground_speed_kt = ar_nodes[0].Velocity.length() * 1.9438f; // 1.943 = m/s in knots/s
4133 
4134  //tropospheric model valid up to 11.000m (33.000ft)
4135  float altitude = ar_nodes[0].AbsPosition.y;
4136  //float sea_level_temperature = 273.15 + 15.0; //in Kelvin // MAGICs D:
4137  float sea_level_pressure = 101325; //in Pa
4138  //float airtemperature = sea_level_temperature - altitude * 0.0065f; //in Kelvin
4139  float airpressure = sea_level_pressure * pow(1.0f - 0.0065f * altitude / 288.15f, 5.24947f); //in Pa
4140  float airdensity = airpressure * 0.0000120896f; //1.225 at sea level
4141 
4142  float knots = ground_speed_kt * sqrt(airdensity / 1.225f); //KIAS
4143  ar_dashboard->setFloat(DD_AIRSPEED, knots);
4144  }
4145 
4146  // altimeter (height above ground)
4147  {
4148  float alt = ar_nodes[0].AbsPosition.y * 1.1811f; // MAGIC
4149  ar_dashboard->setFloat(DD_ALTITUDE, alt);
4150 
4151  char altc[11];
4152  sprintf(altc, "%03u", (int)(ar_nodes[0].AbsPosition.y / 30.48f)); // MAGIC
4153  ar_dashboard->setChar(DD_ALTITUDE_STRING, altc);
4154  }
4155  }
4156 
4157  ar_dashboard->setFloat(DD_ODOMETER_TOTAL, m_odometer_total);
4158  ar_dashboard->setFloat(DD_ODOMETER_USER, m_odometer_user);
4159 
4160  // set the features of this vehicle once
4161  if (!m_hud_features_ok)
4162  {
4163  bool hasEngine = (ar_engine != nullptr);
4164  bool hasturbo = false;
4165  bool autogearVisible = false;
4166 
4167  if (hasEngine)
4168  {
4169  hasturbo = ar_engine->HasTurbo();
4170  autogearVisible = (ar_engine->getAutoShift() != EngineSim::MANUALMODE);
4171  }
4172 
4173  ar_dashboard->setEnabled(DD_ENGINE_TURBO, hasturbo);
4174  ar_dashboard->setEnabled(DD_ENGINE_GEAR, hasEngine);
4175  ar_dashboard->setEnabled(DD_ENGINE_NUM_GEAR, hasEngine);
4176  ar_dashboard->setEnabled(DD_ENGINE_GEAR_STRING, hasEngine);
4177  ar_dashboard->setEnabled(DD_ENGINE_AUTO_GEAR, hasEngine);
4178  ar_dashboard->setEnabled(DD_ENGINE_CLUTCH, hasEngine);
4179  ar_dashboard->setEnabled(DD_ENGINE_RPM, hasEngine);
4180  ar_dashboard->setEnabled(DD_ENGINE_IGNITION, hasEngine);
4181  ar_dashboard->setEnabled(DD_ENGINE_BATTERY, hasEngine);
4182  ar_dashboard->setEnabled(DD_ENGINE_CLUTCH_WARNING, hasEngine);
4183 
4184  ar_dashboard->setEnabled(DD_TRACTIONCONTROL_MODE, !tc_nodash);
4185  ar_dashboard->setEnabled(DD_ANTILOCKBRAKE_MODE, !alb_nodash);
4186  ar_dashboard->setEnabled(DD_TIES_MODE, !ar_ties.empty());
4187  ar_dashboard->setEnabled(DD_LOCKED, !ar_hooks.empty());
4188 
4189  ar_dashboard->setEnabled(DD_ENGINE_AUTOGEAR_STRING, autogearVisible);
4190 
4191  ar_dashboard->updateFeatures();
4192  m_hud_features_ok = true;
4193  }
4194 
4195  // Lights (all kinds)
4196  // PLEASE maintain the same order as in 'DashBoardManager.h'
4197 
4198  ar_dashboard->setBool(DD_CUSTOM_LIGHT1 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM1 );
4199  ar_dashboard->setBool(DD_CUSTOM_LIGHT2 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM2 );
4200  ar_dashboard->setBool(DD_CUSTOM_LIGHT3 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM3 );
4201  ar_dashboard->setBool(DD_CUSTOM_LIGHT4 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM4 );
4202  ar_dashboard->setBool(DD_CUSTOM_LIGHT5 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM5 );
4203  ar_dashboard->setBool(DD_CUSTOM_LIGHT6 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM6 );
4204  ar_dashboard->setBool(DD_CUSTOM_LIGHT7 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM7 );
4205  ar_dashboard->setBool(DD_CUSTOM_LIGHT8 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM8 );
4206  ar_dashboard->setBool(DD_CUSTOM_LIGHT9 , m_lightmask & RoRnet::LIGHTMASK_CUSTOM9 );
4207  ar_dashboard->setBool(DD_CUSTOM_LIGHT10, m_lightmask & RoRnet::LIGHTMASK_CUSTOM10 );
4208 
4209  ar_dashboard->setBool(DD_HEADLIGHTS , m_lightmask & RoRnet::LIGHTMASK_HEADLIGHT );
4210  ar_dashboard->setBool(DD_HIGHBEAMS , m_lightmask & RoRnet::LIGHTMASK_HIGHBEAMS );
4211  ar_dashboard->setBool(DD_FOGLIGHTS , m_lightmask & RoRnet::LIGHTMASK_FOGLIGHTS );
4212  ar_dashboard->setBool(DD_SIDELIGHTS , m_lightmask & RoRnet::LIGHTMASK_SIDELIGHTS);
4213  ar_dashboard->setBool(DD_LIGHTS_LEGACY , m_lightmask & RoRnet::LIGHTMASK_SIDELIGHTS);
4214  ar_dashboard->setBool(DD_BRAKE_LIGHTS , m_lightmask & RoRnet::LIGHTMASK_BRAKES );
4215  ar_dashboard->setBool(DD_REVERSE_LIGHT , m_lightmask & RoRnet::LIGHTMASK_REVERSE );
4216  ar_dashboard->setBool(DD_BEACONS , m_lightmask & RoRnet::LIGHTMASK_BEACONS );
4217 
4218  ar_dashboard->setBool(DD_SIGNAL_WARNING, m_blinker_right_lit && m_blinker_left_lit);
4219  ar_dashboard->setBool(DD_SIGNAL_TURNRIGHT, m_blinker_right_lit);
4220  ar_dashboard->setBool(DD_SIGNAL_TURNLEFT, m_blinker_left_lit);
4221 
4222  // TODO: compass value
4223 
4224 #if 0
4225  // ADI - attitude director indicator
4226  //roll
4227  Vector3 rollv=curr_truck->ar_nodes[curr_truck->ar_camera_node_pos[0]].RelPosition-curr_truck->ar_nodes[curr_truck->ar_camera_node_roll[0]].RelPosition;
4228  rollv.normalise();
4229  float rollangle=asin(rollv.dotProduct(Vector3::UNIT_Y));
4230 
4231  //pitch
4232  Vector3 dirv=curr_truck->ar_nodes[curr_truck->ar_camera_node_pos[0]].RelPosition-curr_truck->ar_nodes[curr_truck->ar_camera_node_dir[0]].RelPosition;
4233  dirv.normalise();
4234  float pitchangle=asin(dirv.dotProduct(Vector3::UNIT_Y));
4235  Vector3 upv=dirv.crossProduct(-rollv);
4236  if (upv.y<0) rollangle=3.14159-rollangle;
4237  RoR::App::GetOverlayWrapper()->adibugstexture->setTextureRotate(Radian(-rollangle));
4238  RoR::App::GetOverlayWrapper()->aditapetexture->setTextureVScroll(-pitchangle*0.25);
4239  RoR::App::GetOverlayWrapper()->aditapetexture->setTextureRotate(Radian(-rollangle));
4240 
4241  // HSI - Horizontal Situation Indicator
4242  Vector3 idir=curr_truck->ar_nodes[curr_truck->ar_camera_node_pos[0]].RelPosition-curr_truck->ar_nodes[curr_truck->ar_camera_node_dir[0]].RelPosition;
4243  // idir.normalise();
4244  float dirangle=atan2(idir.dotProduct(Vector3::UNIT_X), idir.dotProduct(-Vector3::UNIT_Z));
4245  RoR::App::GetOverlayWrapper()->hsirosetexture->setTextureRotate(Radian(dirangle));
4246  if (curr_truck->autopilot)
4247  {
4248  RoR::App::GetOverlayWrapper()->hsibugtexture->setTextureRotate(Radian(dirangle)-Degree(curr_truck->autopilot->heading));
4249  float vdev=0;
4250  float hdev=0;
4251  curr_truck->autopilot->getRadioFix(localizers, free_localizer, &vdev, &hdev);
4252  if (hdev>15) hdev=15;
4253  if (hdev<-15) hdev=-15;
4254  RoR::App::GetOverlayWrapper()->hsivtexture->setTextureUScroll(-hdev*0.02);
4255  if (vdev>15) vdev=15;
4256  if (vdev<-15) vdev=-15;
4257  RoR::App::GetOverlayWrapper()->hsihtexture->setTextureVScroll(-vdev*0.02);
4258  }
4259 
4260  // VVI - Vertical Velocity Indicator
4261  float vvi=curr_truck->ar_nodes[0].Velocity.y*196.85;
4262  if (vvi<1000.0 && vvi>-1000.0) angle=vvi*0.047;
4263  if (vvi>1000.0 && vvi<6000.0) angle=47.0+(vvi-1000.0)*0.01175;
4264  if (vvi>6000.0) angle=105.75;
4265  if (vvi<-1000.0 && vvi>-6000.0) angle=-47.0+(vvi+1000.0)*0.01175;
4266  if (vvi<-6000.0) angle=-105.75;
4267  RoR::App::GetOverlayWrapper()->vvitexture->setTextureRotate(Degree(-angle+90.0));
4268 
4269 
4270  if (curr_truck->aeroengines[0]->getType() == AeroEngineType::AE_XPROP)
4271  {
4272  Turboprop *tp=(Turboprop*)curr_truck->aeroengines[0];
4273  //pitch
4274  RoR::App::GetOverlayWrapper()->airpitch1texture->setTextureRotate(Degree(-tp->pitch*2.0));
4275  //torque
4276  pcent=100.0*tp->indicated_torque/tp->max_torque;
4277  if (pcent<60.0) angle=-5.0+pcent*1.9167;
4278  else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4279  else angle=314.0;
4280  RoR::App::GetOverlayWrapper()->airtorque1texture->setTextureRotate(Degree(-angle));
4281  }
4282 
4283  if (ftp>1 && curr_truck->aeroengines[1]->getType() == AeroEngineType::AE_XPROP)
4284  {
4285  Turboprop *tp=(Turboprop*)curr_truck->aeroengines[1];
4286  //pitch
4287  RoR::App::GetOverlayWrapper()->airpitch2texture->setTextureRotate(Degree(-tp->pitch*2.0));
4288  //torque
4289  pcent=100.0*tp->indicated_torque/tp->max_torque;
4290  if (pcent<60.0) angle=-5.0+pcent*1.9167;
4291  else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4292  else angle=314.0;
4293  RoR::App::GetOverlayWrapper()->airtorque2texture->setTextureRotate(Degree(-angle));
4294  }
4295 
4296  if (ftp>2 && curr_truck->aeroengines[2]->getType() == AeroEngineType::AE_XPROP)
4297  {
4298  Turboprop *tp=(Turboprop*)curr_truck->aeroengines[2];
4299  //pitch
4300  RoR::App::GetOverlayWrapper()->airpitch3texture->setTextureRotate(Degree(-tp->pitch*2.0));
4301  //torque
4302  pcent=100.0*tp->indicated_torque/tp->max_torque;
4303  if (pcent<60.0) angle=-5.0+pcent*1.9167;
4304  else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4305  else angle=314.0;
4306  RoR::App::GetOverlayWrapper()->airtorque3texture->setTextureRotate(Degree(-angle));
4307  }
4308 
4309  if (ftp>3 && curr_truck->aeroengines[3]->getType() == AeroEngineType::AE_XPROP)
4310  {
4311  Turboprop *tp=(Turboprop*)curr_truck->aeroengines[3];
4312  //pitch
4313  RoR::App::GetOverlayWrapper()->airpitch4texture->setTextureRotate(Degree(-tp->pitch*2.0));
4314  //torque
4315  pcent=100.0*tp->indicated_torque/tp->max_torque;
4316  if (pcent<60.0) angle=-5.0+pcent*1.9167;
4317  else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4318  else angle=314.0;
4319  RoR::App::GetOverlayWrapper()->airtorque4texture->setTextureRotate(Degree(-angle));
4320  }
4321 
4322  //starters
4323  if (curr_truck->aeroengines[0]->getIgnition()) RoR::App::GetOverlayWrapper()->engstarto1->setMaterialName("tracks/engstart-on"); else RoR::App::GetOverlayWrapper()->engstarto1->setMaterialName("tracks/engstart-off");
4324  if (ftp>1 && curr_truck->aeroengines[1]->getIgnition()) RoR::App::GetOverlayWrapper()->engstarto2->setMaterialName("tracks/engstart-on"); else RoR::App::GetOverlayWrapper()->engstarto2->setMaterialName("tracks/engstart-off");
4325  if (ftp>2 && curr_truck->aeroengines[2]->getIgnition()) RoR::App::GetOverlayWrapper()->engstarto3->setMaterialName("tracks/engstart-on"); else RoR::App::GetOverlayWrapper()->engstarto3->setMaterialName("tracks/engstart-off");
4326  if (ftp>3 && curr_truck->aeroengines[3]->getIgnition()) RoR::App::GetOverlayWrapper()->engstarto4->setMaterialName("tracks/engstart-on"); else RoR::App::GetOverlayWrapper()->engstarto4->setMaterialName("tracks/engstart-off");
4327 }
4328 
4329 #endif //0
4330  ar_dashboard->update(dt);
4331 }
4332 
4333 void Actor::calculateLocalGForces()
4334 {
4335  Vector3 cam_dir = this->GetCameraDir();
4336  Vector3 cam_roll = this->GetCameraRoll();
4337  Vector3 cam_up = cam_dir.crossProduct(cam_roll);
4338 
4339  float gravity = App::GetGameContext()->GetTerrain()->getGravity();
4340 
4341  float vertacc = m_camera_gforces.dotProduct(cam_up) + gravity;
4342  float longacc = m_camera_gforces.dotProduct(cam_dir);
4343  float latacc = m_camera_gforces.dotProduct(cam_roll);
4344 
4345  m_camera_local_gforces_cur = Vector3(vertacc, longacc, latacc) / gravity;
4346 
4347  // Let it settle before we start recording the maximum forces
4348  if (m_reset_timer.getMilliseconds() > 500)
4349  {
4350  m_camera_local_gforces_max.makeCeil(-m_camera_local_gforces_cur);
4351  m_camera_local_gforces_max.makeCeil(+m_camera_local_gforces_cur);
4352  }
4353 }
4354 
4355 void Actor::engineTriggerHelper(int engineNumber, EngineTriggerType type, float triggerValue)
4356 {
4357  // engineNumber tells us which engine
4358  EngineSim* e = ar_engine; // placeholder: actors do not have multiple engines yet
4359 
4360  switch (type)
4361  {
4362  case TRG_ENGINE_CLUTCH:
4363  if (e)
4364  e->SetClutch(triggerValue);
4365  break;
4366  case TRG_ENGINE_BRAKE:
4367  ar_brake = triggerValue;
4368  break;
4369  case TRG_ENGINE_ACC:
4370  if (e)
4371  e->SetAcceleration(triggerValue);
4372  break;
4373  case TRG_ENGINE_RPM:
4374  // TODO: Implement setTargetRPM in the EngineSim.cpp
4375  break;
4376  case TRG_ENGINE_SHIFTUP:
4377  if (e)
4378  e->shift(1);
4379  break;
4380  case TRG_ENGINE_SHIFTDOWN:
4381  if (e)
4382  e->shift(-1);
4383  break;
4384  default:
4385  break;
4386  }
4387 }
4388 
4389 Actor::Actor(
4390  ActorInstanceID_t actor_id,
4391  unsigned int vector_index,
4392  RigDef::DocumentPtr def,
4394 )
4395  // Special values
4396  : ar_nb_optimum(7, std::numeric_limits<float>::max())
4397  , ar_nb_reference(7, std::numeric_limits<float>::max())
4398  , m_tyre_pressure(this)
4399  , ar_nb_beams_scale(std::make_pair(1.0f, 1.0f))
4400  , ar_nb_shocks_scale(std::make_pair(1.0f, 1.0f))
4401  , ar_nb_wheels_scale(std::make_pair(1.0f, 1.0f))
4402  , ar_nb_beams_k_interval(std::make_pair(0.1f, 2.0f))
4403  , ar_nb_beams_d_interval(std::make_pair(0.1f, 2.0f))
4404  , ar_nb_shocks_k_interval(std::make_pair(0.1f, 8.0f))
4405  , ar_nb_shocks_d_interval(std::make_pair(0.1f, 8.0f))
4406  , ar_nb_wheels_k_interval(std::make_pair(1.0f, 1.0f))
4407  , ar_nb_wheels_d_interval(std::make_pair(1.0f, 1.0f))
4408 
4409  // Constructor parameters
4410  , m_avg_node_position_prev(rq.asr_position)
4411  , m_preloaded_with_terrain(rq.asr_origin == RoR::ActorSpawnRequest::Origin::TERRN_DEF)
4412  , m_avg_node_position(rq.asr_position)
4413  , ar_instance_id(actor_id)
4414  , ar_vector_index(vector_index)
4415  , m_avg_proped_wheel_radius(0.2f)
4416  , ar_filename(rq.asr_filename)
4417  , m_section_config(rq.asr_config)
4418  , m_used_actor_entry(rq.asr_cache_entry)
4419  , m_used_skin_entry(rq.asr_skin_entry)
4420  , m_working_tuneup_def(rq.asr_working_tuneup)
4421 
4422  // Public bit flags
4423  , ar_update_physics(false)
4424  , ar_disable_aerodyn_turbulent_drag(false)
4425  , ar_engine_hydraulics_ready(true) // !!
4426  , ar_guisettings_use_engine_max_rpm(false)
4427  , ar_hydro_speed_coupling(false)
4428  , ar_collision_relevant(false)
4429  , ar_is_police(false)
4430  , ar_rescuer_flag(false)
4431  , ar_forward_commands(false)
4432  , ar_import_commands(false)
4433  , ar_toggle_ropes(false)
4434  , ar_toggle_ties(false)
4435  , ar_physics_paused(false)
4436 
4437  // Private bit flags
4438  , m_hud_features_ok(false)
4439  , m_slidenodes_locked(false)
4440  , m_net_initialized(false)
4441  , m_water_contact(false)
4442  , m_water_contact_old(false)
4443  , m_has_command_beams(false)
4444  , m_custom_particles_enabled(false)
4445  , m_beam_break_debug_enabled(false)
4446  , m_beam_deform_debug_enabled(false)
4447  , m_trigger_debug_enabled(false)
4448  , m_disable_default_sounds(false)
4449  , m_disable_smoke(false)
4450 {
4451 }
4452 
4454 {
4455  return ar_hydro_dir_command;
4456 }
4457 
4458 std::vector<authorinfo_t> Actor::getAuthors()
4459 {
4460  return authors;
4461 }
4462 
4463 std::vector<std::string> Actor::getDescription()
4464 {
4465  return m_description;
4466 }
4467 
4468 void Actor::setMass(float m)
4469 {
4470  m_dry_mass = m;
4471 }
4472 
4474 {
4475  if (number < 0 || number >= MAX_CLIGHTS)
4476  {
4477  LOG(fmt::format("invalid custom-light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4478  return false;
4479  }
4480 
4481  if (number == 0) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM1);
4482  if (number == 1) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM2);
4483  if (number == 2) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM3);
4484  if (number == 3) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM4);
4485  if (number == 4) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM5);
4486  if (number == 5) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM6);
4487  if (number == 6) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM7);
4488  if (number == 7) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM8);
4489  if (number == 8) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM9);
4490  if (number == 9) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM10);
4491 
4492  return false;
4493 }
4494 
4495 void Actor::setCustomLightVisible(int number, bool visible)
4496 {
4497  if (number < 0 || number >= MAX_CLIGHTS)
4498  {
4499  LOG(fmt::format("invalid Light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4500  return;
4501  }
4502 
4503  if (number == 0) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM1 , visible);
4504  if (number == 1) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM2 , visible);
4505  if (number == 2) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM3 , visible);
4506  if (number == 3) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM4 , visible);
4507  if (number == 4) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM5 , visible);
4508  if (number == 5) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM6 , visible);
4509  if (number == 6) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM7 , visible);
4510  if (number == 7) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM8 , visible);
4511  if (number == 8) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM9 , visible);
4512  if (number == 9) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM10, visible);
4513 }
4514 
4516 {
4517  if (number < 0 || number >= MAX_CLIGHTS)
4518  {
4519  LOG(fmt::format("invalid custom-light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4520  return -1;
4521  }
4522 
4523  int count = 0;
4524  for (int i = 0; i < ar_flares.size(); i++)
4525  {
4526  if (ar_flares[i].controlnumber == number)
4527  count++;
4528  }
4529  return count;
4530 }
4531 
4533 {
4534  int count = 0;
4535  for (int i = 0; i < ar_flares.size(); i++)
4536  {
4537  if (ar_flares[i].fl_type == type)
4538  count++;
4539  }
4540  return count;
4541 }
4542 
4544 {
4548  return BlinkType::BLINK_NONE;
4549 }
4550 
4552 {
4554 }
4555 
4557 {
4558  return m_min_camera_radius;
4559 }
4560 
4562 {
4564  return m_replay_handler;
4565  else
4566  return nullptr;
4567 }
4568 
4569 Ogre::MaterialPtr Actor::getManagedMaterialInstance(const std::string& orig_name)
4570 {
4571  auto it = ar_managed_materials.find(orig_name);
4572  if (it != ar_managed_materials.end())
4573  return it->second;
4574  else
4575  return Ogre::MaterialPtr(); // null
4576 }
4577 
4578 std::vector<std::string> Actor::getManagedMaterialNames()
4579 {
4580  std::vector<std::string> names;
4581  for (auto it = ar_managed_materials.begin(); it != ar_managed_materials.end(); ++it)
4582  {
4583  names.push_back(it->first);
4584  }
4585  return names;
4586 }
4587 
4588 Vector3 Actor::getNodePosition(int nodeNumber)
4589 {
4590  if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4591  {
4592  return ar_nodes[nodeNumber].AbsPosition;
4593  }
4594  else
4595  {
4596  return Ogre::Vector3::ZERO;
4597  }
4598 }
4599 
4600 bool Actor::isNodeWheelRim(int nodeNumber)
4601 {
4602  if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4603  {
4604  return ar_nodes[nodeNumber].nd_rim_node;
4605  }
4606  else
4607  {
4608  return false;
4609  }
4610 }
4611 
4612 bool Actor::isNodeWheelTire(int nodeNumber)
4613 {
4614  if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4615  {
4616  return ar_nodes[nodeNumber].nd_tyre_node;
4617  }
4618  else
4619  {
4620  return false;
4621  }
4622 }
4623 
4624 void Actor::WriteDiagnosticDump(std::string const& fileName)
4625 {
4626  // Purpose: to diff against output from https://github.com/only-a-ptr/rigs-of-rods/tree/retro-0407
4627  std::stringstream buf;
4628 
4629  buf << "[nodes]" << std::endl;
4630  for (int i = 0; i < ar_num_nodes; i++)
4631  {
4632  buf
4633  << " pos:" << std::setw(3) << ar_nodes[i].pos // indicated pos in node buffer
4634  << ((ar_nodes[i].pos != i) ? " !!sync " : "") // warn if the indicated pos doesn't match
4635  << " (nodes)"
4636  << " id:" << std::setw(3) << ar_nodes_id[i]
4637  << " name:" << std::setw(ar_nodes_name_top_length) << ar_nodes_name[i]
4638  << ", buoyancy:" << std::setw(8) << ar_nodes[i].buoyancy
4639  << ", loaded:" << (int)(ar_nodes[i].nd_loaded_mass)
4640  << " (wheels)"
4641  << " wheel_rim:" << (int)ar_nodes[i].nd_rim_node
4642  << ", wheel_tyre:" << (int)ar_nodes[i].nd_tyre_node
4643  << " (set_node_defaults)"
4644  << " mass:" << std::setw(8) << ar_nodes[i].mass // param 1 load weight
4645  << ", friction_coef:" << std::setw(5) << ar_nodes[i].friction_coef // param 2 friction coef
4646  << ", volume_coef:" << ar_nodes[i].volume_coef // param 3 volume coef
4647  << ", surface_coef:" << ar_nodes[i].surface_coef // param 4 surface coef
4648  << ", overrideMass:" << ar_nodes[i].nd_override_mass // depends on param 1 load weight
4649 
4650  // only set by `ActorSpawner::UpdateCollcabContacterNodes()` based on collcabs
4651  // The 'retro-0407' equivalent is `node::contacter` set by `Beam::updateContacterNodes()` based on collcabs!
4652  << " (collcabs)"
4653  << " " << ar_nodes[i].nd_cab_node
4654  << std::endl;
4655  }
4656 
4657  buf << "[beams]" << std::endl;
4658  for (int i = 0; i < ar_num_beams; i++)
4659  {
4660  buf
4661  << " " << std::setw(4) << i // actual pos in beam buffer
4662  << ", node1:" << std::setw(3) << ((ar_beams[i].p1) ? ar_nodes_id[ar_beams[i].p1->pos] : -1)
4663  << ", node2:" << std::setw(3) << ((ar_beams[i].p2) ? ar_nodes_id[ar_beams[i].p2->pos] : -1)
4664  << ", refLen:" << std::setw(9) << ar_beams[i].refL
4665  << " (set_beam_defaults/scale)"
4666  << " spring:" << std::setw(8) << ar_beams[i].k //param1 default_spring
4667  << ", damp:" << std::setw(8) << ar_beams[i].d //param2 default_damp
4668  << ", default_deform:" << std::setw(8) << ar_beams[i].default_beam_deform //param3 default_deform
4669  << ", strength:" << std::setw(8) << ar_beams[i].strength //param4 default_break
4670  //param5 default_beam_diameter ~ only visual
4671  //param6 default_beam_material2 ~ only visual
4672  << ", plastic_coef:" << std::setw(8) << ar_beams[i].plastic_coef //param7 default_plastic_coef
4673  << std::endl;
4674  }
4675 
4676  // Write out to 'logs' using OGRE resource system - works with Unicode paths on Windows
4677  Ogre::DataStreamPtr outStream = Ogre::ResourceGroupManager::getSingleton().createResource(fileName, RGN_LOGS, /*overwrite=*/true);
4678  std::string text = buf.str();
4679  outStream->write(text.c_str(), text.length());
4680 }
4681 
4683 {
4685  {
4686  bool ev_active = App::GetInputEngine()->getEventValue(state.event_id);
4687  if (state.eventlock_present)
4688  {
4689  // Toggle-mode
4690  if (ev_active && (ev_active != state.event_active_prev))
4691  {
4692  state.anim_active = !state.anim_active;
4693  }
4694  }
4695  else
4696  {
4697  // Direct-mode
4698  state.anim_active = ev_active;
4699  }
4700  state.event_active_prev = ev_active;
4701  }
4702 }
4703 
4705 {
4707 }
4708 
4710 {
4711  return m_used_actor_entry;
4712 }
4713 
4715 {
4716  return m_used_skin_entry;
4717 }
4718 
4720 {
4721  return m_working_tuneup_def;
4722 }
4723 
4725 {
4726  if (!m_working_tuneup_def)
4727  {
4730  }
4731 }
4732 
4734 {
4735  m_working_tuneup_def = nullptr;
4736 }
4737 
4738 float Actor::getShockSpringRate(int shock_number)
4739 {
4740  if (shock_number >= 0 && shock_number < ar_num_shocks)
4741  {
4742  return ar_beams[ar_shocks[shock_number].beamid].debug_k;
4743  }
4744  return -1.f;
4745 }
4746 
4747 float Actor::getShockDamping(int shock_number)
4748 {
4749  if (shock_number >= 0 && shock_number < ar_num_shocks)
4750  {
4751  return ar_beams[ar_shocks[shock_number].beamid].debug_d;
4752  }
4753  return -1.f;
4754 }
4755 
4756 float Actor::getShockVelocity(int shock_number)
4757 {
4758  if (shock_number >= 0 && shock_number < ar_num_shocks)
4759  {
4760  return ar_beams[ar_shocks[shock_number].beamid].debug_v;
4761  }
4762  return -1.f;
4763 }
4764 
4765 int Actor::getShockNode1(int shock_number)
4766 {
4767  if (shock_number >= 0 && shock_number < ar_num_shocks)
4768  {
4769  return ar_beams[ar_shocks[shock_number].beamid].p1->pos;
4770  }
4771  return -1.f;
4772 }
4773 
4774 int Actor::getShockNode2(int shock_number)
4775 {
4776  if (shock_number >= 0 && shock_number < ar_num_shocks)
4777  {
4778  return ar_beams[ar_shocks[shock_number].beamid].p2->pos;
4779  }
4780  return -1.f;
4781 }
RoR::TuneupDef
Dual purpose:
Definition: TuneupFileFormat.h:93
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
GameContext.h
Game state manager and message-queue provider.
RoR::App::diag_truck_mass
CVar * diag_truck_mass
Definition: Application.cpp:137
RoRnet::LIGHTMASK_SIDELIGHTS
@ LIGHTMASK_SIDELIGHTS
Definition: RoRnet.h:117
RoR::ActorLinkingRequest::alr_hook_group
int alr_hook_group
Definition: SimData.h:922
RoR::Actor::ar_nodes_name
std::string * ar_nodes_name
Name in truck file, only if defined with 'nodes2'.
Definition: Actor.h:281
MAX_COMMANDS
static const int MAX_COMMANDS
maximum number of commands per actor
Definition: SimConstants.h:28
RoR::GfxActor::SetDebugView
void SetDebugView(DebugViewType dv)
Definition: GfxActor.cpp:1528
RoR::ANIM_FLAG_HEADING
@ ANIM_FLAG_HEADING
Definition: SimData.h:165
RoR::App::GetNetwork
Network * GetNetwork()
Definition: Application.cpp:284
RoR::SHOCK_FLAG_SOFTBUMP
@ SHOCK_FLAG_SOFTBUMP
Definition: SimData.h:202
RoR::App::GetSoundScriptManager
SoundScriptManager * GetSoundScriptManager()
Definition: Application.cpp:277
RoRnet::ActorStreamRegister
Definition: RoRnet.h:150
RoR::ANIM_FLAG_BRUDDER
@ ANIM_FLAG_BRUDDER
Definition: SimData.h:171
RoR::node_t::nd_contacter
bool nd_contacter
Attr; User-defined.
Definition: SimData.h:312
RoR::SHOCK3
@ SHOCK3
shock3
Definition: SimData.h:110
RoRnet::LIGHTMASK_CUSTOM9
@ LIGHTMASK_CUSTOM9
custom light 9 on
Definition: RoRnet.h:111
RoRnet::LIGHTMASK_FOGLIGHTS
@ LIGHTMASK_FOGLIGHTS
Definition: RoRnet.h:116
RoR::ANIM_FLAG_ACCEL
@ ANIM_FLAG_ACCEL
Definition: SimData.h:153
RoR::SS_MOD_AEROENGINE1
@ SS_MOD_AEROENGINE1
Definition: SoundScriptManager.h:127
RoR::Actor::isNodeWheelRim
bool isNodeWheelRim(int nodeNumber)
Is node marked as wheel rim? Note some wheel models use only tire nodes. See https://docs....
Definition: Actor.cpp:4600
BITMASK_SET
void BITMASK_SET(BitMask_t &mask, BitMask_t flag, bool val)
Definition: BitFlags.h:19
RoR::ActorManager::GetNetTime
unsigned long GetNetTime()
Definition: ActorManager.h:82
RoR::DD_ENGINE_SPEEDO_MPH
@ DD_ENGINE_SPEEDO_MPH
speedo in kilometer per hour
Definition: DashBoardManager.h:88
RoR::EngineSim::SetClutch
void SetClutch(float clutch)
Definition: EngineSim.cpp:970
RoR::DD_HIGHBEAMS
@ DD_HIGHBEAMS
Definition: DashBoardManager.h:189
RoRnet::VehicleState::hydrodirstate
float hydrodirstate
the turning direction status
Definition: RoRnet.h:193
RoR::MSG_SIM_MODIFY_ACTOR_REQUESTED
@ MSG_SIM_MODIFY_ACTOR_REQUESTED
Payload = RoR::ActorModifyRequest* (owner)
Definition: Application.h:120
RoR::ResolveIntraActorCollisions
void ResolveIntraActorCollisions(const float dt, PointColDetector &intraPointCD, const int free_collcab, int collcabs[], int cabs[], collcab_rate_t intra_collcabrate[], node_t nodes[], const float collrange, ground_model_t &submesh_ground_model)
Definition: DynamicCollisions.cpp:210
FlexMeshWheel.h
DD_MAX_AEROENGINE
#define DD_MAX_AEROENGINE
Definition: DashBoardManager.h:40
RoR::Actor::NetUpdate
Definition: Actor.h:652
RoR::Actor::getShockNode2
int getShockNode2(int shock_number)
Definition: Actor.cpp:4774
RoR::EngineSim::shift
void shift(int val)
Changes gear by a relative offset. Plays sounds.
Definition: EngineSim.cpp:1087
RoR::OverlayWrapper::update
void update(float dt)
Definition: OverlayWrapper.cpp:345
RoR::SE_GENERIC_TRUCK_LINKING_CHANGED
@ SE_GENERIC_TRUCK_LINKING_CHANGED
Triggered when 2 actors become linked or unlinked via ties/hooks/ropes/slidenodes; args: #1 state (1=...
Definition: ScriptEvents.h:64
RoR::Actor::ar_physics_paused
bool ar_physics_paused
Sim state.
Definition: Actor.h:484
RoR::Actor::ensureWorkingTuneupDef
void ensureWorkingTuneupDef()
Creates a working tuneup def if it doesn't exist yet.
Definition: Actor.cpp:4724
RoRnet::ActorStreamRegister::time
int32_t time
initial time stamp
Definition: RoRnet.h:158
RoR::DD_CUSTOM_LIGHT8
@ DD_CUSTOM_LIGHT8
custom light 8 on
Definition: DashBoardManager.h:184
RoR::DD_ENGINE_SPEEDO_KPH
@ DD_ENGINE_SPEEDO_KPH
Definition: DashBoardManager.h:87
RoR::Actor::ar_filename
std::string ar_filename
Attribute; filled at spawn.
Definition: Actor.h:425
RoRnet::LIGHTMASK_CUSTOM4
@ LIGHTMASK_CUSTOM4
custom light 4 on
Definition: RoRnet.h:106
RoRnet::VehicleState
< Formerly oob_t
Definition: RoRnet.h:186
MAX_CLIGHTS
static const int MAX_CLIGHTS
See RoRnet::Lightmask and enum events in InputEngine.h.
Definition: SimConstants.h:35
RoR::SHOCK_FLAG_TRG_BLOCKER_A
@ SHOCK_FLAG_TRG_BLOCKER_A
Definition: SimData.h:207
RoRnet::NETMASK_HORN
@ NETMASK_HORN
horn is in use
Definition: RoRnet.h:85
RoR::DD_ACCELERATOR
@ DD_ACCELERATOR
Definition: DashBoardManager.h:103
RoR::App::GetCameraManager
CameraManager * GetCameraManager()
Definition: Application.cpp:275
RoR::DD_SCREW_THROTTLE_0
@ DD_SCREW_THROTTLE_0
ties locked
Definition: DashBoardManager.h:121
RoR::node_t::AbsPosition
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition: SimData.h:294
VehicleAI.h
Simple waypoint AI.
RoR::DD_AEROENGINE_THROTTLE_0
@ DD_AEROENGINE_THROTTLE_0
Definition: DashBoardManager.h:139
RGN_LOGS
#define RGN_LOGS
Definition: Application.h:53
RoRnet::UserInfo
Definition: RoRnet.h:168
RoR::ActorLinkingRequest
Estabilishing a physics linkage between 2 actors modifies a global linkage table and triggers immedia...
Definition: SimData.h:917
RoR::beam_t::p1
node_t * p1
Definition: SimData.h:335
RoR::DD_ENGINE_TURBO
@ DD_ENGINE_TURBO
speedo in miles per hour
Definition: DashBoardManager.h:89
DashBoardManager.h
RoR::Actor::ar_linked_actors
ActorPtrVec ar_linked_actors
BEWARE: Includes indirect links, see DetermineLinkedActors(); Other actors linked using 'hooks/ties/r...
Definition: Actor.h:447
RoR::Actor::getCustomParticleMode
bool getCustomParticleMode()
Definition: Actor.cpp:4551
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:376
z
float z
Definition: (ValueTypes) quaternion.h:7
RoR::Actor::ar_num_nodes
int ar_num_nodes
Definition: Actor.h:283
RoR::Collisions::getSurfaceHeightBelow
float getSurfaceHeightBelow(float x, float z, float height)
Definition: Collisions.cpp:676
RoR::Actor::isNodeWheelTire
bool isNodeWheelTire(int nodeNumber)
Is node marked as wheel tire? Note some wheel models use only tire nodes. See https://docs....
Definition: Actor.cpp:4612
RoR::Actor::ar_bounding_box
Ogre::AxisAlignedBox ar_bounding_box
standard bounding box (surrounds all nodes of an actor)
Definition: Actor.h:306
RoRnet::LIGHTMASK_CUSTOM10
@ LIGHTMASK_CUSTOM10
custom light 10 on
Definition: RoRnet.h:112
RoR::node_t::surface_coef
Ogre::Real surface_coef
Definition: SimData.h:301
RoR::SoundScriptManager::removeInstance
void removeInstance(const SoundScriptInstancePtr &ssi)
Definition: SoundScriptManager.cpp:409
RoR::NODENUM_INVALID
static const NodeNum_t NODENUM_INVALID
Definition: ForwardDeclarations.h:53
RoR::node_t::nd_contactable
bool nd_contactable
Attr; This node will be treated as contacter on inter truck collisions.
Definition: SimData.h:313
RoR::DD_REVERSE_LIGHT
@ DD_REVERSE_LIGHT
Definition: DashBoardManager.h:193
format
Truck file format(technical spec)
RoR::ActorManager::inter_actor_links
std::map< beam_t *, std::pair< ActorPtr, ActorPtr > > inter_actor_links
Definition: ActorManager.h:124
RoR::node_t::nd_override_mass
bool nd_override_mass
User defined attr; mass is user-specified rather than calculated (override the calculation)
Definition: SimData.h:319
AutoPilot.h
RoR::DD_WATER_SPEED
@ DD_WATER_SPEED
Definition: DashBoardManager.h:136
RoR::DD_CUSTOM_LIGHT2
@ DD_CUSTOM_LIGHT2
custom light 2 on
Definition: DashBoardManager.h:178
RoR::HandleGenericException
void HandleGenericException(const std::string &from, BitMask_t flags)
Definition: Application.cpp:369
RoR::ANIM_FLAG_BTHROTTLE
@ ANIM_FLAG_BTHROTTLE
Definition: SimData.h:172
RoRnet::LIGHTMASK_REVERSE
@ LIGHTMASK_REVERSE
reverse light on
Definition: RoRnet.h:119
RoR::ActorLinkingRequest::alr_type
ActorLinkingRequestType alr_type
Definition: SimData.h:920
RoR::DD_CUSTOM_LIGHT5
@ DD_CUSTOM_LIGHT5
custom light 5 on
Definition: DashBoardManager.h:181
RoR::Actor::m_min_camera_radius
Ogre::Real m_min_camera_radius
Definition: Actor.h:552
RoR::ANIM_FLAG_ALTIMETER
@ ANIM_FLAG_ALTIMETER
Definition: SimData.h:145
RoR::TRIGGER_EVENT_ASYNC
void TRIGGER_EVENT_ASYNC(scriptEvents type, int arg1, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
Asynchronously (via MSG_SIM_SCRIPT_EVENT_TRIGGERED) invoke script function eventCallbackEx(),...
Definition: ScriptEngine.h:51
RoR::Actor::getShockSpringRate
float getShockSpringRate(int shock_number)
Definition: Actor.cpp:4738
RoR::ropable_t::attached_ropes
int attached_ropes
State.
Definition: SimData.h:503
RoR::DD_ROLL_CORR
@ DD_ROLL_CORR
Definition: DashBoardManager.h:106
RoR::App::GetOverlayWrapper
OverlayWrapper * GetOverlayWrapper()
Definition: Application.cpp:268
RoRnet::ActorStreamRegister::name
char name[128]
filename
Definition: RoRnet.h:156
RoR::beam_t::strength
float strength
Definition: SimData.h:343
RoR::ANIM_FLAG_AOA
@ ANIM_FLAG_AOA
Definition: SimData.h:146
RoR::LogFormat
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
Definition: Application.cpp:424
RoR::Actor::getDescription
std::vector< std::string > getDescription()
Definition: Actor.cpp:4463
RoR::Turboprop::pitch
float pitch
Definition: TurboProp.h:43
RoR::SS_MOD_AEROENGINE4
@ SS_MOD_AEROENGINE4
Definition: SoundScriptManager.h:130
DynamicCollisions.h
RoRnet::StreamRegister::origin_sourceid
int32_t origin_sourceid
origin sourceid
Definition: RoRnet.h:144
MeshObject.h
RoR::DD_TIES_MODE
@ DD_TIES_MODE
Definition: DashBoardManager.h:118
Console.h
RoR::beam_t::bm_locked_actor
ActorPtr bm_locked_actor
in case p2 is on another actor
Definition: SimData.h:350
RoR::TuneupDef::name
std::string name
Definition: TuneupFileFormat.h:97
RoR::Console::putMessage
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition: Console.cpp:97
BOUNDING_BOX_PADDING
static const Ogre::Vector3 BOUNDING_BOX_PADDING(0.05f, 0.05f, 0.05f)
RoR::PropAnimKeyState
User input state for animated props with 'source:event'.
Definition: SimData.h:658
RoR::Actor::getShockDamping
float getShockDamping(int shock_number)
Definition: Actor.cpp:4747
RoR::DD_ENGINE_CLUTCH
@ DD_ENGINE_CLUTCH
automatic gear
Definition: DashBoardManager.h:100
RoR::LOCKED
@ LOCKED
lock locked.
Definition: SimData.h:85
RoR::EngineSim
A land vehicle engine + transmission.
Definition: EngineSim.h:35
RoR::CacheEntry::resource_group
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
Definition: CacheSystem.h:89
RoRnet::LIGHTMASK_CUSTOM7
@ LIGHTMASK_CUSTOM7
custom light 7 on
Definition: RoRnet.h:109
MovableText.h
This creates a billboarding object that displays a text.
RoRnet::NETMASK_ALB_ACTIVE
@ NETMASK_ALB_ACTIVE
anti lock brake light on?
Definition: RoRnet.h:90
RoR::FlareType
FlareType
Definition: SimData.h:229
TuneupFileFormat.h
The vehicle tuning system; applies addonparts and user overrides to vehicles.
RoR::Actor::getTruckFileResourceGroup
std::string getTruckFileResourceGroup()
Definition: Actor.cpp:4704
Airfoil.h
RoR::DD_AEROENGINE_FAILED_0
@ DD_AEROENGINE_FAILED_0
Definition: DashBoardManager.h:146
RoRnet::NETMASK_ENGINE_MODE_MANUAL
@ NETMASK_ENGINE_MODE_MANUAL
engine mode
Definition: RoRnet.h:96
RoR::Actor::GetGfxActor
GfxActor * GetGfxActor()
Definition: Actor.h:269
RoR::ANIM_FLAG_SPEEDO
@ ANIM_FLAG_SPEEDO
Definition: SimData.h:157
RoR::SS_MOD_AEROENGINE2
@ SS_MOD_AEROENGINE2
Definition: SoundScriptManager.h:128
RoRnet::VehicleState::engine_clutch
float engine_clutch
the clutch value
Definition: RoRnet.h:191
Utils.h
RoRnet::VehicleState::engine_speed
float engine_speed
engine RPM
Definition: RoRnet.h:189
RoR::ActorManager::AreActorsDirectlyLinked
bool AreActorsDirectlyLinked(const ActorPtr &a1, const ActorPtr &a2)
Definition: ActorManager.cpp:724
RoRnet
Definition: ForwardDeclarations.h:232
RoR::Actor::NetUpdate::wheel_data
std::vector< float > wheel_data
Wheel rotations.
Definition: Actor.h:656
Differentials.h
RoR::EngineSim::SetAcceleration
void SetAcceleration(float val)
Definition: EngineSim.cpp:852
Language.h
RoR::Actor::getManagedMaterialInstance
Ogre::MaterialPtr getManagedMaterialInstance(const std::string &orig_name)
Definition: Actor.cpp:4569
RoRnet::ActorStreamRegister::sectionconfig
char sectionconfig[60]
section configuration
Definition: RoRnet.h:160
RefCountingObjectPtr< Actor >
RoR::InputEngine::getEventValue
float getEventValue(int eventID, bool pure=false, InputSourceType valueSource=InputSourceType::IST_ANY)
valueSource: IST_ANY=digital and analog devices, IST_DIGITAL=only digital, IST_ANALOG=only analog
Definition: InputEngine.cpp:958
ActorSpawner.h
Vehicle spawning logic.
RoR::ANIM_FLAG_TORQUE
@ ANIM_FLAG_TORQUE
Definition: SimData.h:164
GUIManager.h
RoR::SE_TRUCK_TIE_TOGGLE
@ SE_TRUCK_TIE_TOGGLE
triggered when the user toggles ties, the argument refers to the actor ID
Definition: ScriptEvents.h:42
RoR::Actor::DetermineLinkedActors
void DetermineLinkedActors()
Definition: Actor.cpp:828
ActorManager.h
RoR::Network::GetUserInfo
bool GetUserInfo(int uid, RoRnet::UserInfo &result)
Definition: Network.cpp:709
Actor.h
RoR::App::GetScriptEngine
ScriptEngine * GetScriptEngine()
Definition: Application.cpp:279
RoRnet::LIGHTMASK_BLINK_LEFT
@ LIGHTMASK_BLINK_LEFT
left blinker on
Definition: RoRnet.h:121
RoR::hydrobeam_t::hb_anim_flags
int hb_anim_flags
Animators (beams updating length based on simulation variables)
Definition: SimData.h:594
RoR::TRG_ENGINE_ACC
@ TRG_ENGINE_ACC
Definition: SimData.h:223
RoR::GfxScene::GetSceneManager
Ogre::SceneManager * GetSceneManager()
Definition: GfxScene.h:64
RoR::ActorSpawnRequest
Definition: SimData.h:832
EngineSim.h
PadBoundingBox
void PadBoundingBox(Ogre::AxisAlignedBox &box)
Definition: Actor.cpp:1162
RoR::SS_MOD_INJECTOR
@ SS_MOD_INJECTOR
Definition: SoundScriptManager.h:132
RoR::SimGearboxMode::AUTO
@ AUTO
Automatic shift.
RoR::beam_t::refL
float refL
reference length
Definition: SimData.h:356
RoR::ANIM_FLAG_RPM
@ ANIM_FLAG_RPM
Definition: SimData.h:152
RoR::SS_TRIG_REVERSE_GEAR
@ SS_TRIG_REVERSE_GEAR
Definition: SoundScriptManager.h:99
RoR::Terrain::GetHeightAt
float GetHeightAt(float x, float z)
Definition: Terrain.cpp:503
RoR::ActorManager::AddStreamMismatch
void AddStreamMismatch(int sourceid, int streamid)
Definition: ActorManager.h:85
RoRnet::NETMASK_ENGINE_MODE_MANUAL_RANGES
@ NETMASK_ENGINE_MODE_MANUAL_RANGES
engine mode
Definition: RoRnet.h:98
Replay.h
RoR::Turboprop::indicated_torque
float indicated_torque
Definition: TurboProp.h:44
RoR::ActorModifyRequest::amr_actor
ActorInstanceID_t amr_actor
Definition: SimData.h:886
RoR::Actor::getReplay
Replay * getReplay()
Definition: Actor.cpp:4561
RoR::SimGearboxMode::MANUAL_STICK
@ MANUAL_STICK
Fully manual: stick shift.
RoR::DD_ODOMETER_USER
@ DD_ODOMETER_USER
Definition: DashBoardManager.h:173
BITMASK_SET_0
#define BITMASK_SET_0(VAR, FLAGS)
Definition: BitFlags.h:16
RoR::DD_FOGLIGHTS
@ DD_FOGLIGHTS
Definition: DashBoardManager.h:190
TurboJet.h
DD_MAX_SCREWPROP
#define DD_MAX_SCREWPROP
Definition: DashBoardManager.h:39
TOSTRING
#define TOSTRING(x)
Definition: Application.h:56
DD_MAX_WING
#define DD_MAX_WING
Definition: DashBoardManager.h:41
RoR::beam_t
Simulation: An edge in the softbody structure.
Definition: SimData.h:330
ScrewProp.h
RoR::DD_ENGINE_GEAR
@ DD_ENGINE_GEAR
clutch warning lamp
Definition: DashBoardManager.h:94
SlideNode.h
RoR::PRELOCK
@ PRELOCK
prelocking, attraction forces in action
Definition: SimData.h:84
RoR::SS_TRIG_PARK
@ SS_TRIG_PARK
Definition: SoundScriptManager.h:84
RoR::CameraManager::GetCamera
Ogre::Camera * GetCamera()
Definition: CameraManager.h:64
RoR::SHOCK_FLAG_TRG_HOOK_LOCK
@ SHOCK_FLAG_TRG_HOOK_LOCK
Definition: SimData.h:209
RoR::Actor::ar_nodes_id
int * ar_nodes_id
Number in truck file, -1 for nodes generated by wheels/cinecam.
Definition: Actor.h:280
RoR::ActorInstanceID_t
int ActorInstanceID_t
Unique sequentially generated ID of an actor in session. Use ActorManager::GetActorById()
Definition: ForwardDeclarations.h:37
RoR::DD_CUSTOM_LIGHT3
@ DD_CUSTOM_LIGHT3
custom light 3 on
Definition: DashBoardManager.h:179
RoR::DD_PARKINGBRAKE
@ DD_PARKINGBRAKE
chassis pitch
Definition: DashBoardManager.h:111
RoR::ropable_t::attached_ties
int attached_ties
State.
Definition: SimData.h:502
RoR::Actor::ar_beams
beam_t * ar_beams
Definition: Actor.h:287
CmdKeyInertia.h
RoRnet::VehicleState::engine_gear
int32_t engine_gear
engine gear
Definition: RoRnet.h:192
RoR::Actor::authors
std::vector< authorinfo_t > authors
Definition: Actor.h:297
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:52
RoR::Actor::removeWorkingTuneupDef
void removeWorkingTuneupDef()
Deletes the working tuneup def object if it exists.
Definition: Actor.cpp:4733
RoR::UNLOCKED
@ UNLOCKED
lock not locked
Definition: SimData.h:83
RoR::Str< 400 >
RoR::Actor::ar_flares
std::vector< flare_t > ar_flares
Definition: Actor.h:303
RoR::beam_t::d
float d
damping factor
Definition: SimData.h:338
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
RoRnet::LIGHTMASK_CUSTOM3
@ LIGHTMASK_CUSTOM3
custom light 3 on
Definition: RoRnet.h:105
RoR::ActorModifyRequest
Definition: SimData.h:870
RoR::Turboprop
Definition: TurboProp.h:38
RoR::Actor::getAuthors
std::vector< authorinfo_t > getAuthors()
Definition: Actor.cpp:4458
RoRnet::NETMASK_ENGINE_CONT
@ NETMASK_ENGINE_CONT
ignition on?
Definition: RoRnet.h:91
RoRnet::NETMASK_ENGINE_MODE_AUTOMATIC
@ NETMASK_ENGINE_MODE_AUTOMATIC
engine mode
Definition: RoRnet.h:94
RoR::DD_ROLL
@ DD_ROLL
Definition: DashBoardManager.h:105
CacheSystem.h
A database of user-installed content alias 'mods' (vehicles, terrains...)
RoR::Actor::NetUpdate::veh_state
std::vector< char > veh_state
Actor properties (engine, brakes, lights, ...)
Definition: Actor.h:654
RoR::Actor::toggleHeadlights
void toggleHeadlights()
Definition: Actor.cpp:2989
RoR::DD_SIDELIGHTS
@ DD_SIDELIGHTS
Definition: DashBoardManager.h:191
ErrorUtils.h
RoR::shock_t::beamid
int beamid
Definition: SimData.h:372
RoR::ANIM_FLAG_CLUTCH
@ ANIM_FLAG_CLUTCH
Definition: SimData.h:155
RoR::Actor::ar_num_collcabs
int ar_num_collcabs
Definition: Actor.h:339
RoR::SE_TRUCK_PARKINGBRAKE_TOGGLE
@ SE_TRUCK_PARKINGBRAKE_TOGGLE
triggered when the user toggles the parking brake, the argument refers to the actor ID
Definition: ScriptEvents.h:43
RoRnet::LIGHTMASK_HEADLIGHT
@ LIGHTMASK_HEADLIGHT
Definition: RoRnet.h:114
RoR::ACTORINSTANCEID_INVALID
static const ActorInstanceID_t ACTORINSTANCEID_INVALID
Definition: ForwardDeclarations.h:38
ScriptEngine.h
BITMASK_SET_1
#define BITMASK_SET_1(VAR, FLAGS)
Definition: BitFlags.h:17
RoR::GameContext::PushMessage
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
Definition: GameContext.cpp:65
RoRnet::NETMASK_ENGINE_MODE_MANUAL_STICK
@ NETMASK_ENGINE_MODE_MANUAL_STICK
engine mode
Definition: RoRnet.h:97
RoR::Replay
Definition: Replay.h:39
RoR::hydrobeam_t
Definition: SimData.h:588
ChatSystem.h
RoR::Str::ToCStr
const char * ToCStr() const
Definition: Str.h:46
RoR::ANIM_FLAG_FLAP
@ ANIM_FLAG_FLAP
Definition: SimData.h:147
RoRnet::VehicleState::lightmask
BitMask_t lightmask
flagmask: LIGHTMASK_*
Definition: RoRnet.h:197
RORNET_MAX_MESSAGE_LENGTH
#define RORNET_MAX_MESSAGE_LENGTH
maximum size of a RoR message. 8192 bytes = 8 kibibytes
Definition: RoRnet.h:31
RoR::node_t::nd_cab_node
bool nd_cab_node
Attr; This node is part of collision triangle.
Definition: SimData.h:309
RoRnet::VehicleState::time
int32_t time
time data
Definition: RoRnet.h:188
RoR::BEAM_VIRTUAL
@ BEAM_VIRTUAL
Excluded from mass calculations, visuals permanently disabled.
Definition: SimData.h:73
RoR::ActorLinkingRequestType
ActorLinkingRequestType
Definition: SimData.h:894
RoR::Turboprop::max_torque
float max_torque
Definition: TurboProp.h:45
RoR::SS_TRIG_AVICHAT01
@ SS_TRIG_AVICHAT01
Definition: SoundScriptManager.h:105
RoR::Terrain::GetCollisions
Collisions * GetCollisions()
Definition: Terrain.h:83
RoR::node_t::pos
NodeNum_t pos
This node's index in Actor::ar_nodes array.
Definition: SimData.h:304
RoR::Actor::m_description
std::vector< std::string > m_description
Definition: Actor.h:597
GfxScene.h
RoR::node_t::nd_tyre_node
bool nd_tyre_node
Attr; This node is part of a tyre (note some wheel types don't use rim nodes at all)
Definition: SimData.h:311
RoR::SS_MOD_WHEELSPEED
@ SS_MOD_WHEELSPEED
Definition: SoundScriptManager.h:131
RoR::BLINK_RIGHT
@ BLINK_RIGHT
Definition: SimData.h:125
RoR::DD_CUSTOM_LIGHT4
@ DD_CUSTOM_LIGHT4
custom light 4 on
Definition: DashBoardManager.h:180
RoR::Actor::WriteDiagnosticDump
void WriteDiagnosticDump(std::string const &filename)
Definition: Actor.cpp:4624
RoRnet::NETMASK_TC_ACTIVE
@ NETMASK_TC_ACTIVE
traction control light on?
Definition: RoRnet.h:89
RoR::ANIM_FLAG_VVI
@ ANIM_FLAG_VVI
Definition: SimData.h:144
RoR::BLINK_WARN
@ BLINK_WARN
Definition: SimData.h:126
SOUND_START
#define SOUND_START(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:35
RefCountingObjectPtr::GetRef
T * GetRef()
Definition: RefCountingObjectPtr.h:50
RoR::beam_t::debug_k
float debug_k
debug shock spring_rate
Definition: SimData.h:363
RoR::Actor::ar_collcabs
int ar_collcabs[MAX_CABS]
Definition: Actor.h:336
RoR::DD_ROLL_CORR_ACTIVE
@ DD_ROLL_CORR_ACTIVE
Definition: DashBoardManager.h:107
RoR::Actor::ar_cabs
int ar_cabs[MAX_CABS *3]
Definition: Actor.h:333
RoR::ANIM_FLAG_AESTATUS
@ ANIM_FLAG_AESTATUS
Definition: SimData.h:163
RoR::SHOCK2
@ SHOCK2
shock2
Definition: SimData.h:109
RoR::DD_SIGNAL_TURNRIGHT
@ DD_SIGNAL_TURNRIGHT
Right blinker is lit.
Definition: DashBoardManager.h:198
RoR::ActorManager::UpdateNetTimeOffset
void UpdateNetTimeOffset(int sourceid, int offset)
Definition: ActorManager.cpp:511
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:270
RoR::Actor::setMass
void setMass(float m)
Definition: Actor.cpp:4468
RoR::SimGearboxMode::SEMI_AUTO
@ SEMI_AUTO
Manual shift with auto clutch.
RoR::SHOCK_FLAG_TRG_CONTINUOUS
@ SHOCK_FLAG_TRG_CONTINUOUS
Definition: SimData.h:210
RoR::SE_TRUCK_RESET
@ SE_TRUCK_RESET
triggered when the user resets the truck, the argument refers to the actor ID of the vehicle
Definition: ScriptEvents.h:50
RoRnet::MSG2_STREAM_REGISTER_RESULT
@ MSG2_STREAM_REGISTER_RESULT
result of a stream creation
Definition: RoRnet.h:64
SoundScriptManager.h
RoR::node_t
Physics: A vertex in the softbody structure.
Definition: SimData.h:286
FlexBody.h
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
RoR::Actor::m_used_actor_entry
CacheEntryPtr m_used_actor_entry
Definition: Actor.h:588
RoR::node_t::friction_coef
Ogre::Real friction_coef
Definition: SimData.h:300
RoR::Actor::NetUpdate::node_data
std::vector< char > node_data
Compressed node positions.
Definition: Actor.h:655
RoR::Actor::m_lightmask
BitMask_t m_lightmask
RoRnet::Lightmask.
Definition: Actor.h:616
RoR::AIRPLANE
@ AIRPLANE
its an airplane
Definition: SimData.h:94
RoR::DD_CUSTOM_LIGHT7
@ DD_CUSTOM_LIGHT7
custom light 7 on
Definition: DashBoardManager.h:183
RoR::Actor::m_dry_mass
float m_dry_mass
Physics attr;.
Definition: Actor.h:586
RoR::node_t::buoyancy
Ogre::Real buoyancy
Definition: SimData.h:299
RoR::Actor::m_prop_anim_key_states
std::vector< PropAnimKeyState > m_prop_anim_key_states
Definition: Actor.h:598
RoR::DD_CUSTOM_LIGHT10
@ DD_CUSTOM_LIGHT10
custom light 10 on
Definition: DashBoardManager.h:186
RoR::Actor::getSteeringAngle
float getSteeringAngle()
Definition: Actor.cpp:4453
RoR::ANIM_FLAG_AETORQUE
@ ANIM_FLAG_AETORQUE
Definition: SimData.h:161
RoR::DD_BEACONS
@ DD_BEACONS
Definition: DashBoardManager.h:194
RoRnet::LIGHTMASK_CUSTOM5
@ LIGHTMASK_CUSTOM5
custom light 5 on
Definition: RoRnet.h:107
RoR::ScriptEngine::triggerEvent
void triggerEvent(scriptEvents eventnum, int arg1=0, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
triggers an event; Not to be used by the end-user.
Definition: ScriptEngine.cpp:712
RoR::BlinkType
BlinkType
< Turn signal
Definition: SimData.h:121
SOUND_STOP
#define SOUND_STOP(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:36
RoR::beam_t::plastic_coef
float plastic_coef
Definition: SimData.h:345
RoR::ANIM_FLAG_ROLL
@ ANIM_FLAG_ROLL
Definition: SimData.h:149
RoR::Actor::ar_nodes_name_top_length
int ar_nodes_name_top_length
For nicely formatted diagnostic output.
Definition: Actor.h:282
RoR::BLINK_LEFT
@ BLINK_LEFT
Definition: SimData.h:124
FlexMesh.h
RoR::SS_MAX_TRIG
@ SS_MAX_TRIG
Definition: SoundScriptManager.h:120
RoR::DD_ENGINE_BATTERY
@ DD_ENGINE_BATTERY
Definition: DashBoardManager.h:91
RoRnet::LIGHTMASK_BRAKES
@ LIGHTMASK_BRAKES
brake lights on
Definition: RoRnet.h:118
RoR::Collisions::getSurfaceHeight
float getSurfaceHeight(float x, float z)
Definition: Collisions.cpp:671
RoR::Actor::ar_num_beams
int ar_num_beams
Definition: Actor.h:288
RoR::Actor::m_replay_handler
Replay * m_replay_handler
Definition: Actor.h:556
RoR::Actor::getUsedActorEntry
CacheEntryPtr & getUsedActorEntry()
The actor entry itself.
Definition: Actor.cpp:4709
RoR::DD_BRAKE
@ DD_BRAKE
Definition: DashBoardManager.h:102
RoR::DD_AIRSPEED
@ DD_AIRSPEED
Definition: DashBoardManager.h:160
RoR::DD_PITCH
@ DD_PITCH
Definition: DashBoardManager.h:109
RoR::DD_ENGINE_RPM
@ DD_ENGINE_RPM
Definition: DashBoardManager.h:86
RoR::SS_TRIG_HORN
@ SS_TRIG_HORN
Definition: SoundScriptManager.h:59
RoRnet::LIGHTMASK_HIGHBEAMS
@ LIGHTMASK_HIGHBEAMS
Definition: RoRnet.h:115
FlexAirfoil.h
RoRnet::StreamRegister::status
int32_t status
initial stream status
Definition: RoRnet.h:143
RoR::Actor::getManagedMaterialNames
std::vector< std::string > getManagedMaterialNames()
Definition: Actor.cpp:4578
RoR::DD_SIGNAL_WARNING
@ DD_SIGNAL_WARNING
The warning-blink indicator is lit.
Definition: DashBoardManager.h:199
RoR::SHOCK_FLAG_TRG_CMD_SWITCH
@ SHOCK_FLAG_TRG_CMD_SWITCH
Definition: SimData.h:205
RoR::ANIM_FLAG_BRAKE
@ ANIM_FLAG_BRAKE
Definition: SimData.h:154
PointColDetector.h
RoR::SimGearboxMode::MANUAL
@ MANUAL
Fully manual: sequential shift.
RoR::SE_TRUCK_LIGHT_TOGGLE
@ SE_TRUCK_LIGHT_TOGGLE
triggered when the main light is toggled, the argument refers to the actor ID
Definition: ScriptEvents.h:41
RoR::ANIM_FLAG_PBRAKE
@ ANIM_FLAG_PBRAKE
Definition: SimData.h:158
SOUND_MODULATE
#define SOUND_MODULATE(_ACTOR_, _MOD_, _VALUE_)
Definition: SoundScriptManager.h:40
RoRnet::LIGHTMASK_CUSTOM8
@ LIGHTMASK_CUSTOM8
custom light 8 on
Definition: RoRnet.h:110
RoR::DD_WING_AOA_0
@ DD_WING_AOA_0
Definition: DashBoardManager.h:162
Skidmark.h
RoRnet::LIGHTMASK_CUSTOM2
@ LIGHTMASK_CUSTOM2
custom light 2 on
Definition: RoRnet.h:104
RoR::beam_t::debug_d
float debug_d
debug shock damping
Definition: SimData.h:364
RoR::App::io_blink_lock_range
CVar * io_blink_lock_range
Definition: Application.cpp:191
RoRnet::NETMASK_ENGINE_MODE_SEMIAUTO
@ NETMASK_ENGINE_MODE_SEMIAUTO
engine mode
Definition: RoRnet.h:95
RoR::node_t::volume_coef
Ogre::Real volume_coef
Definition: SimData.h:302
RoR::DD_SIGNAL_TURNLEFT
@ DD_SIGNAL_TURNLEFT
Left blinker is lit.
Definition: DashBoardManager.h:197
RoR::SHOCK_FLAG_TRG_ENGINE
@ SHOCK_FLAG_TRG_ENGINE
Definition: SimData.h:211
RoR::TRG_ENGINE_SHIFTDOWN
@ TRG_ENGINE_SHIFTDOWN
Definition: SimData.h:226
RoRnet::StreamRegister::name
char name[128]
the actor filename
Definition: RoRnet.h:146
TurboProp.h
RoR::Actor::ar_nodes
node_t * ar_nodes
Definition: Actor.h:279
RoRnet::UserInfo::username
char username[RORNET_MAX_USERNAME_LEN]
the nickname of the user (UTF-8)
Definition: RoRnet.h:175
RoR::Actor::getMinimalCameraRadius
Ogre::Real getMinimalCameraRadius()
Definition: Actor.cpp:4556
RoR::beam_t::debug_v
float debug_v
debug shock velocity
Definition: SimData.h:365
RoR::ANIM_FLAG_AEPITCH
@ ANIM_FLAG_AEPITCH
Definition: SimData.h:162
RoR::node_t::nd_rim_node
bool nd_rim_node
Attr; This node is part of a rim (only wheel types with separate rim nodes)
Definition: SimData.h:310
RoR::SS_MOD_AIRSPEED
@ SS_MOD_AIRSPEED
Definition: SoundScriptManager.h:151
RoR::DD_ENGINE_IGNITION
@ DD_ENGINE_IGNITION
turbo gauge
Definition: DashBoardManager.h:90
RoR::Actor::m_used_skin_entry
CacheEntryPtr m_used_skin_entry
Graphics.
Definition: Actor.h:589
Buoyance.h
RoR::beam_t::k
float k
tensile spring
Definition: SimData.h:337
RoR::MSG_SIM_ACTOR_LINKING_REQUESTED
@ MSG_SIM_ACTOR_LINKING_REQUESTED
Payload = RoR::ActorLinkingRequest* (owner)
Definition: Application.h:128
RoR::Message
Unified game event system - all requests and state changes are reported using a message.
Definition: GameContext.h:51
RoR::Actor::ar_hydro_dir_command
float ar_hydro_dir_command
Definition: Actor.h:403
RoRnet::MSG2_STREAM_DATA_DISCARDABLE
@ MSG2_STREAM_DATA_DISCARDABLE
stream data that is allowed to be discarded
Definition: RoRnet.h:67
RoR::SHOCK_FLAG_TRG_CMD_BLOCKER
@ SHOCK_FLAG_TRG_CMD_BLOCKER
Definition: SimData.h:206
_L
#define _L
Definition: ErrorUtils.cpp:34
PHYSICS_DT
#define PHYSICS_DT
Definition: SimConstants.h:20
RoR::Actor::getNodePosition
Ogre::Vector3 getNodePosition(int nodeNumber)
Returns world position of node.
Definition: Actor.cpp:4588
RoR::Actor::UpdatePropAnimInputEvents
void UpdatePropAnimInputEvents()
Definition: Actor.cpp:4682
RoRnet::LIGHTMASK_BLINK_WARN
@ LIGHTMASK_BLINK_WARN
warn blinker on
Definition: RoRnet.h:123
RoRnet::ActorStreamRegister::type
int32_t type
stream type
Definition: RoRnet.h:152
RoR::Actor::getUsedSkinEntry
CacheEntryPtr & getUsedSkinEntry()
Definition: Actor.cpp:4714
SOUND_PLAY_ONCE
#define SOUND_PLAY_ONCE(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:34
RoRnet::LIGHTMASK_CUSTOM1
@ LIGHTMASK_CUSTOM1
custom light 1 on
Definition: RoRnet.h:103
RoR::BLINK_NONE
@ BLINK_NONE
Definition: SimData.h:123
RoR::SE_TRUCK_TOUCHED_WATER
@ SE_TRUCK_TOUCHED_WATER
triggered when any part of the actor touches water, the argument refers to the actor ID
Definition: ScriptEvents.h:40
RoR::Actor::m_working_tuneup_def
TuneupDefPtr m_working_tuneup_def
Each actor gets unique instance, even if loaded from .tuneup file in modcache.
Definition: Actor.h:590
RoRnet::NETMASK_PARTICLE
@ NETMASK_PARTICLE
custom particles on
Definition: RoRnet.h:87
RoRnet::StreamRegister::origin_streamid
int32_t origin_streamid
origin streamid
Definition: RoRnet.h:145
RoR::App::GetInputEngine
InputEngine * GetInputEngine()
Definition: Application.cpp:271
RoR::ANIM_FLAG_PITCH
@ ANIM_FLAG_PITCH
Definition: SimData.h:150
RoR::TRG_ENGINE_BRAKE
@ TRG_ENGINE_BRAKE
Definition: SimData.h:222
RoR::Actor::getShockVelocity
float getShockVelocity(int shock_number)
Definition: Actor.cpp:4756
RoR::DD_ALTITUDE_STRING
@ DD_ALTITUDE_STRING
Definition: DashBoardManager.h:170
RoR::ActorPtr
RefCountingObjectPtr< Actor > ActorPtr
Definition: ForwardDeclarations.h:194
RoR::SHOCK_FLAG_TRG_HOOK_UNLOCK
@ SHOCK_FLAG_TRG_HOOK_UNLOCK
Definition: SimData.h:208
RoR::ActorLinkingRequest::alr_actor_instance_id
ActorInstanceID_t alr_actor_instance_id
Definition: SimData.h:919
RoR::MSG_SIM_DELETE_ACTOR_REQUESTED
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
Definition: Application.h:121
RoR::DD_SCREW_STEER_0
@ DD_SCREW_STEER_0
Definition: DashBoardManager.h:128
RoR::Terrain::getGravity
float getGravity() const
Definition: Terrain.h:96
RoR::TRG_ENGINE_CLUTCH
@ TRG_ENGINE_CLUTCH
Definition: SimData.h:221
RoR::DD_CUSTOM_LIGHT6
@ DD_CUSTOM_LIGHT6
custom light 6 on
Definition: DashBoardManager.h:182
RoR::Actor::setCustomLightVisible
void setCustomLightVisible(int number, bool visible)
Definition: Actor.cpp:4495
RoR::DD_TRACTIONCONTROL_MODE
@ DD_TRACTIONCONTROL_MODE
low pressure
Definition: DashBoardManager.h:116
RoR::beam_t::p2
node_t * p2
Definition: SimData.h:336
RoRnet::VehicleState::wheelspeed
float wheelspeed
the wheel speed value
Definition: RoRnet.h:195
RoR::SE_TRUCK_BEACONS_TOGGLE
@ SE_TRUCK_BEACONS_TOGGLE
triggered when the user toggles beacons, the argument refers to the actor ID
Definition: ScriptEvents.h:44
RoR::Replay::isValid
bool isValid()
Definition: Replay.h:55
Terrain.h
RoR::ANIM_FLAG_TURBO
@ ANIM_FLAG_TURBO
Definition: SimData.h:159
RoR::hydrobeam_t::hb_anim_param
float hb_anim_param
Animators (beams updating length based on simulation variables)
Definition: SimData.h:595
RoRnet::NETMASK_PBRAKE
@ NETMASK_PBRAKE
parking brake
Definition: RoRnet.h:88
BitMask_t
uint32_t BitMask_t
Definition: BitFlags.h:7
RoR::SimGearboxMode::MANUAL_RANGES
@ MANUAL_RANGES
Fully manual: stick shift with ranges.
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
RoRnet::VehicleState::engine_force
float engine_force
engine acceleration
Definition: RoRnet.h:190
FLAP_ANGLES
static const float FLAP_ANGLES[6]
Definition: SimConstants.h:82
RoR::CVar::getFloat
float getFloat() const
Definition: CVar.h:96
Ogre
Definition: ExtinguishableFireAffector.cpp:35
GfxActor.h
Manager for all visuals belonging to a single actor.
RoR::SS_TRIG_TURN_SIGNAL
@ SS_TRIG_TURN_SIGNAL
Definition: SoundScriptManager.h:100
RoR::Actor::countFlaresByType
int countFlaresByType(FlareType type)
Definition: Actor.cpp:4532
RoR::Actor::countCustomLights
int countCustomLights(int control_number)
Definition: Actor.cpp:4515
RoR::Actor::ar_shocks
shock_t * ar_shocks
Shock absorbers.
Definition: Actor.h:290
RoRnet::VehicleState::brake
float brake
the brake value
Definition: RoRnet.h:194
RoR::ANIM_FLAG_DIFFLOCK
@ ANIM_FLAG_DIFFLOCK
Definition: SimData.h:166
RigDef::DocumentPtr
std::shared_ptr< Document > DocumentPtr
Definition: RigDef_Prerequisites.h:38
RoR::SHOCK1
@ SHOCK1
shock1
Definition: SimData.h:108
RoR::SS_MOD_ENGINE
@ SS_MOD_ENGINE
Definition: SoundScriptManager.h:125
FlexObj.h
RoRnet::LIGHTMASK_BEACONS
@ LIGHTMASK_BEACONS
beacons on
Definition: RoRnet.h:120
RoR::SHOCK_FLAG_ISTRIGGER
@ SHOCK_FLAG_ISTRIGGER
Definition: SimData.h:203
RoR::Actor::getBlinkType
BlinkType getBlinkType()
Definition: Actor.cpp:4543
RoRnet::ActorStreamRegister::status
int32_t status
initial stream status
Definition: RoRnet.h:153
RoR::ANIM_FLAG_AIRSPEED
@ ANIM_FLAG_AIRSPEED
Definition: SimData.h:143
RoR::DD_ANTILOCKBRAKE_MODE
@ DD_ANTILOCKBRAKE_MODE
Definition: DashBoardManager.h:117
RoR::SHOCK_FLAG_TRG_BLOCKER
@ SHOCK_FLAG_TRG_BLOCKER
Definition: SimData.h:204
AirBrake.h
RoR::Actor::m_custom_particles_enabled
bool m_custom_particles_enabled
Gfx state.
Definition: Actor.h:628
Collisions.h
RoR::DD_CUSTOM_LIGHT1
@ DD_CUSTOM_LIGHT1
custom light 1 on
Definition: DashBoardManager.h:177
RoR::DD_LOCKED
@ DD_LOCKED
parking brake status
Definition: DashBoardManager.h:112
ErrorUtils::ShowError
static int ShowError(Ogre::UTFString title, Ogre::UTFString message)
shows a simple error message box
Definition: ErrorUtils.cpp:43
RoR::node_t::mass
Ogre::Real mass
Definition: SimData.h:298
RoR::ANIM_FLAG_TACHO
@ ANIM_FLAG_TACHO
Definition: SimData.h:156
RoR::Network::AddLocalStream
void AddLocalStream(RoRnet::StreamRegister *reg, int size)
Definition: Network.cpp:660
RoRnet::NETMASK_ENGINE_RUN
@ NETMASK_ENGINE_RUN
engine running?
Definition: RoRnet.h:92
RoR::ANIM_FLAG_THROTTLE
@ ANIM_FLAG_THROTTLE
Definition: SimData.h:151
RoR::TRG_ENGINE_RPM
@ TRG_ENGINE_RPM
Definition: SimData.h:224
RoR::DD_BRAKE_LIGHTS
@ DD_BRAKE_LIGHTS
Definition: DashBoardManager.h:192
RoR::GameContext::GetPlayerActor
const ActorPtr & GetPlayerActor()
Definition: GameContext.h:134
RoR::DD_LOW_PRESSURE
@ DD_LOW_PRESSURE
locked lamp
Definition: DashBoardManager.h:114
SOUND_GET_STATE
#define SOUND_GET_STATE(_ACTOR_, _TRIG_)
Definition: SoundScriptManager.h:39
RoR::DD_ENGINE_NUM_GEAR
@ DD_ENGINE_NUM_GEAR
current gear
Definition: DashBoardManager.h:95
RoR::SS_TRIG_NONE
@ SS_TRIG_NONE
Definition: SoundScriptManager.h:53
RoR::ropable_t
Definition: SimData.h:497
RoR::Actor::getShockNode1
int getShockNode1(int shock_number)
Definition: Actor.cpp:4765
RoR::SS_MOD_AEROENGINE3
@ SS_MOD_AEROENGINE3
Definition: SoundScriptManager.h:129
RoR::DD_ENGINE_AUTO_GEAR
@ DD_ENGINE_AUTO_GEAR
string like "P R N G"
Definition: DashBoardManager.h:98
RoR::Network::AddPacket
void AddPacket(int streamid, int type, int len, const char *content)
Definition: Network.cpp:606
RoR::ActorModifyRequest::amr_type
Type amr_type
Definition: SimData.h:887
RoR::DD_ENGINE_AUTOGEAR_STRING
@ DD_ENGINE_AUTOGEAR_STRING
string like "<current gear>/<max gear>"
Definition: DashBoardManager.h:97
RoR::Actor::getWorkingTuneupDef
TuneupDefPtr & getWorkingTuneupDef()
Definition: Actor.cpp:4719
RoR::TRG_ENGINE_SHIFTUP
@ TRG_ENGINE_SHIFTUP
Definition: SimData.h:225
RoR::DD_ENGINE_CLUTCH_WARNING
@ DD_ENGINE_CLUTCH_WARNING
battery lamp
Definition: DashBoardManager.h:92
RoRnet::VehicleState::flagmask
BitMask_t flagmask
flagmask: NETMASK_*
Definition: RoRnet.h:196
RoR::Actor::ar_num_shocks
int ar_num_shocks
Number of shock absorbers.
Definition: Actor.h:291
RoR
Definition: AppContext.h:36
RoR::HANDLEGENERICEXCEPTION_LOGFILE
@ HANDLEGENERICEXCEPTION_LOGFILE
Definition: Application.h:549
Network.h
RoR::ActorManager::GetActorById
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
Definition: ActorManager.cpp:1146
RoR::DD_WATER_DEPTH
@ DD_WATER_DEPTH
Definition: DashBoardManager.h:135
RoR::App::sim_spawn_running
CVar * sim_spawn_running
Definition: Application.cpp:100
x
float x
Definition: (ValueTypes) quaternion.h:5
RoRnet::LIGHTMASK_BLINK_RIGHT
@ LIGHTMASK_BLINK_RIGHT
right blinker on
Definition: RoRnet.h:122
RoR::App::gfx_reduce_shadows
CVar * gfx_reduce_shadows
Definition: Application.cpp:248
RoR::beam_t::default_beam_deform
float default_beam_deform
for reset
Definition: SimData.h:361
RoR::DD_HEADLIGHTS
@ DD_HEADLIGHTS
Definition: DashBoardManager.h:188
Water.h
RoRnet::ActorStreamRegister::skin
char skin[60]
skin
Definition: RoRnet.h:159
RoR::DD_ALTITUDE
@ DD_ALTITUDE
Definition: DashBoardManager.h:169
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:276
RoR::GameContext::GetActorManager
ActorManager * GetActorManager()
Definition: GameContext.h:127
RoR::Actor::ar_managed_materials
std::map< std::string, Ogre::MaterialPtr > ar_managed_materials
Definition: Actor.h:318
RoR::DD_ENGINE_GEAR_STRING
@ DD_ENGINE_GEAR_STRING
Definition: DashBoardManager.h:96
RoR::Airbrake
Definition: AirBrake.h:35
RoRnet::LIGHTMASK_CUSTOM6
@ LIGHTMASK_CUSTOM6
custom light 6 on
Definition: RoRnet.h:108
RoR::EngineTriggerType
EngineTriggerType
Definition: SimData.h:219
RoR::ANIM_FLAG_AIRBRAKE
@ ANIM_FLAG_AIRBRAKE
Definition: SimData.h:148
RoR::Actor::getCustomLightVisible
bool getCustomLightVisible(int number)
Definition: Actor.cpp:4473
RoR::DD_LIGHTS_LEGACY
@ DD_LIGHTS_LEGACY
Alias of 'sidelights'.
Definition: DashBoardManager.h:195
RoR::SE_TRUCK_CPARTICLES_TOGGLE
@ SE_TRUCK_CPARTICLES_TOGGLE
triggered when the user toggles custom particles, the argument refers to the actor ID
Definition: ScriptEvents.h:45
RoR::DD_ODOMETER_TOTAL
@ DD_ODOMETER_TOTAL
Definition: DashBoardManager.h:172
RoR::GameContext::GetTerrain
const TerrainPtr & GetTerrain()
Definition: GameContext.h:117
RoRnet::StreamRegister
< Sent from the client to server and vice versa, to broadcast a new stream
Definition: RoRnet.h:140
RoR::DD_AEROENGINE_RPM_0
@ DD_AEROENGINE_RPM_0
Definition: DashBoardManager.h:153
RoR::ANIM_FLAG_SHIFTER
@ ANIM_FLAG_SHIFTER
Definition: SimData.h:160
RoR::DD_CUSTOM_LIGHT9
@ DD_CUSTOM_LIGHT9
custom light 9 on
Definition: DashBoardManager.h:185