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