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