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