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