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