Rigs of Rods 2026.01
Soft-body Physics Simulation
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
2999{
3001 {
3003 ar_sleep_counter = 0.0f;
3004 }
3005}
3006
3014
3015float Actor::getEventValue(int eventID, bool pure, InputSourceType valueSource)
3016{
3017 std::map<int, float>::const_iterator simulated_value_info =
3018 ar_actor_event_simulated_values.find(eventID);
3019
3020 float value = 0;
3021 if (simulated_value_info != ar_actor_event_simulated_values.end())
3022 value = simulated_value_info->second;
3024 value = App::GetInputEngine()->getEventValue(eventID, pure, valueSource);
3025
3026 return value;
3027}
3028
3030{
3031 return (getEventValue(eventID) > 0.5f);
3032}
3033
3034bool Actor::getEventBoolValueBounce(int eventID, float time)
3035{
3036 std::map<int, float>::const_iterator simulated_value_info =
3037 ar_actor_event_simulated_values.find(eventID);
3038
3039 float value = 0;
3040 if (simulated_value_info != ar_actor_event_simulated_values.end())
3041 value = simulated_value_info->second > 0.5f;
3043 value = App::GetInputEngine()->getEventBoolValueBounce(eventID, time);
3044
3045 return value;
3046}
3047
3052
3054{
3055 std::map<int, float>::const_iterator simulated_value_info =
3056 ar_actor_event_simulated_values.find(eventID);
3057
3058 return simulated_value_info != ar_actor_event_simulated_values.end();
3059}
3060
3062{
3063 std::map<int, float>::const_iterator simulated_value_info =
3064 ar_actor_event_simulated_values.find(eventID);
3065
3066 float simulated = 0;
3067 if (simulated_value_info != ar_actor_event_simulated_values.end())
3068 simulated = simulated_value_info->second;
3069
3070 return simulated;
3071}
3072
3073void Actor::setEventSimulatedValue(int eventID, float value)
3074{
3075 // Wake up if sleeping.
3077 {
3079 ar_sleep_counter = 0.0f;
3080 }
3081
3082 // Double check this actor can be controlled.
3084 {
3085 ar_actor_event_simulated_values[eventID] = value;
3086 }
3087}
3088
3090{
3091 std::map<int, float>::const_iterator simulated_value_info =
3092 ar_actor_event_simulated_values.find(eventID);
3093
3094 if (simulated_value_info != ar_actor_event_simulated_values.end())
3095 ar_actor_event_simulated_values.erase(simulated_value_info);
3096}
3097
3099{
3100 // Inputs from InputEngine are disabled for actors when:
3101 // - this actor is not the player actor
3102 // - camera behaviour is "Free"
3103 ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3104 bool is_player_actor = player_actor != nullptr && ar_instance_id == player_actor->getInstanceId();
3105 return is_player_actor &&
3107}
3108
3109bool Actor::isEventAnalog(int eventID)
3110{
3111 // Simulated values are analog, so we'll return true.
3112 bool is_analog = true;
3114 is_analog = App::GetInputEngine()->isEventAnalog(eventID);
3115
3116 return is_analog;
3117}
3118
3119bool Actor::isEventDefined(int eventID)
3120{
3122}
3123
3125{
3126 // Simulated values don't have bounce times.
3127 float bounce_time = 0;
3129 bounce_time = App::GetInputEngine()->getEventBounceTime(eventID);
3130
3131 return bounce_time;
3132}
3133
3134// call this once per frame in order to update the skidmarks
3136{
3137 for (int i = 0; i < ar_num_wheels; i++)
3138 {
3139 if (!m_skid_trails[i])
3140 continue;
3141
3142 for (int j = 0; j < ar_wheels[i].wh_num_nodes; j++)
3143 {
3144 auto n = ar_wheels[i].wh_nodes[j];
3145 if (!n || !n->nd_has_ground_contact || n->nd_last_collision_gm == nullptr ||
3146 n->nd_last_collision_gm->fx_type != Collisions::FX_HARD)
3147 {
3148 continue;
3149 }
3150 if (n->nd_avg_collision_slip > 6.f && n->nd_last_collision_slip.squaredLength() > 9.f)
3151 {
3152 m_skid_trails[i]->update(n->AbsPosition, j, n->nd_avg_collision_slip, n->nd_last_collision_gm->name);
3153 return;
3154 }
3155 }
3156 }
3157}
3158
3159void Actor::prepareInside(bool inside)
3160{
3161 // TODO: this whole function belongs to GfxActor ~ 08/2018
3162 if (inside)
3163 {
3164 App::GetCameraManager()->GetCamera()->setNearClipDistance(0.1f);
3165
3166 // enable transparent seat
3167 MaterialPtr seatmat = (MaterialPtr)(MaterialManager::getSingleton().getByName("driversseat"));
3168 seatmat->setDepthWriteEnabled(false);
3169 seatmat->setSceneBlending(SBT_TRANSPARENT_ALPHA);
3170 }
3171 else
3172 {
3173 if (ar_dashboard)
3174 {
3175 ar_dashboard->setVisible(false);
3176 }
3177
3178 App::GetCameraManager()->GetCamera()->setNearClipDistance(0.5f);
3179
3180 // disable transparent seat
3181 MaterialPtr seatmat = (MaterialPtr)(MaterialManager::getSingleton().getByName("driversseat"));
3182 seatmat->setDepthWriteEnabled(true);
3183 seatmat->setSceneBlending(SBT_REPLACE);
3184 }
3185
3186 // TEMPORARY - until this function is moved to GfxActor ~ 08/2018
3187 // if (m_cab_scene_node != nullptr)
3188 // {
3189 // m_gfx_actor->GetCabTransMaterial()->setReceiveShadows(!inside);
3190 // }
3191
3192 if (App::gfx_reduce_shadows->getBool())
3193 {
3194 m_gfx_actor->SetCastShadows(!inside);
3195 }
3196}
3197
3199{
3200 // export light command
3201 ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3202 if (ar_state == ActorState::LOCAL_SIMULATED && this == player_actor.GetRef() && ar_forward_commands)
3203 {
3205 {
3206 if (actor->ar_state == ActorState::LOCAL_SIMULATED && this != actor.GetRef() && actor->ar_import_commands)
3207 actor->toggleHeadlights();
3208 }
3209 }
3210
3211 // flip the flag
3213
3214 // sync cab light state
3215 m_gfx_actor->SetCabLightsActive(this->getHeadlightsVisible());
3216
3218}
3219
3221{
3222 for (size_t i = 0; i < ar_flares.size(); i++)
3223 {
3224 ar_flares[i].snode->setVisible(false);
3225 }
3226}
3227
3229{
3230 if (m_flares_mode == GfxFlaresMode::NONE) { return; }
3231
3232 for (size_t i = 0; i < this->ar_flares.size(); i++)
3233 {
3234 // let the light blink
3235 if (ar_flares[i].blinkdelay != 0)
3236 {
3237 ar_flares[i].blinkdelay_curr -= dt;
3238 if (ar_flares[i].blinkdelay_curr <= 0)
3239 {
3240 ar_flares[i].blinkdelay_curr = ar_flares[i].blinkdelay;
3241 ar_flares[i].blinkdelay_state = !ar_flares[i].blinkdelay_state;
3242 }
3243 }
3244 else
3245 {
3246 ar_flares[i].blinkdelay_state = true;
3247 }
3248
3249 // manage light states
3250 bool isvisible = false;
3251 switch (ar_flares[i].fl_type)
3252 {
3258 case FlareType::BRAKE_LIGHT: isvisible = (m_lightmask & RoRnet::LIGHTMASK_BRAKES); break;
3262 case FlareType::DASHBOARD: isvisible = ar_dashboard->_getBool(ar_flares[i].dashboard_link); break;
3263 case FlareType::USER: isvisible = this->getCustomLightVisible(ar_flares[i].controlnumber); break;
3264 }
3265
3266 // apply blinking
3267 isvisible = isvisible && ar_flares[i].blinkdelay_state;
3268
3269 // update turn signal state
3270 switch (ar_flares[i].fl_type)
3271 {
3272 case FlareType::BLINKER_LEFT: m_blinker_left_lit = isvisible; break;
3273 case FlareType::BLINKER_RIGHT: m_blinker_right_lit = isvisible; break;
3274 default:;
3275 }
3276
3277 // update light intensity
3278 if (ar_flares[i].uses_inertia)
3279 {
3280 ar_flares[i].intensity = ar_flares[i].inertia.CalcSimpleDelay(isvisible, dt);
3281 }
3282 else
3283 {
3284 ar_flares[i].intensity = (float)isvisible;
3285 }
3286 }
3287}
3288
3290{
3291 if (this->getBlinkType() == blink)
3293 else
3294 setBlinkType(blink);
3295}
3296
3330
3332{
3333 // TODO: make this set-able per actor
3334 const float blink_lock_range = App::io_blink_lock_range->getFloat();
3335
3336 if (this->getBlinkType() == BLINK_LEFT && ar_hydro_dir_state < -blink_lock_range)
3337 {
3338 // passed the threshold: the turn signal gets locked
3339 m_blinker_autoreset = true;
3340 }
3341
3342 if (this->getBlinkType() == BLINK_LEFT && m_blinker_autoreset && ar_hydro_dir_state > -blink_lock_range)
3343 {
3344 // steering wheel turned back: turn signal gets automatically unlocked
3346 m_blinker_autoreset = false;
3347 }
3348
3349 // same for the right turn signal
3350 if (this->getBlinkType() == BLINK_RIGHT && ar_hydro_dir_state > blink_lock_range)
3351 m_blinker_autoreset = true;
3352
3353 if (this->getBlinkType() == BLINK_RIGHT && m_blinker_autoreset && ar_hydro_dir_state < blink_lock_range)
3354 {
3356 m_blinker_autoreset = false;
3357 }
3358}
3359
3361{
3362 using namespace RoRnet;
3363
3364 // Perform any special toggling logic where needed.
3365 if ((m_lightmask & LIGHTMASK_HEADLIGHT) != (lightmask & LIGHTMASK_HEADLIGHT))
3366 this->toggleHeadlights();
3367 if ((m_lightmask & LIGHTMASK_BEACONS) != (lightmask & LIGHTMASK_BEACONS))
3368 this->beaconsToggle();
3369
3370 BlinkType btype = BLINK_NONE;
3371 if ((lightmask & LIGHTMASK_BLINK_LEFT) != 0)
3372 btype = BLINK_LEFT;
3373 else if ((lightmask & LIGHTMASK_BLINK_RIGHT) != 0)
3374 btype = BLINK_RIGHT;
3375 else if ((lightmask & LIGHTMASK_BLINK_WARN) != 0)
3376 btype = BLINK_WARN;
3377 this->setBlinkType(btype);
3378
3379 // Update all lights at once (this overwrites the toggled lights with the same value, so it's harmless).
3380 m_lightmask = lightmask;
3381}
3382
3384{
3385 // Override incoming '0' bits where "no import" is set.
3387 // Override incoming '1' bits where "no import" is set.
3389
3390 this->setLightStateMask(lightmask);
3391}
3392
3394{
3396 return;
3397
3399
3400 //ScriptEvent - Particle Toggle
3402}
3403
3405{
3406#ifdef USE_OPENAL
3407 if (App::GetSoundScriptManager()->isDisabled())
3408 return;
3409 for (int i = 0; i < ar_num_soundsources; i++)
3410 {
3411 // TODO: Investigate segfaults after terrain reloads ~ ulteq 11/2018
3412 ar_soundsources[i].ssi->setPosition(ar_nodes[ar_soundsources[i].nodenum].AbsPosition);
3413 ar_soundsources[i].ssi->setVelocity(ar_nodes[ar_soundsources[i].nodenum].Velocity);
3414 }
3415 //also this, so it is updated always, and for any vehicle
3416 SOUND_MODULATE(ar_instance_id, SS_MOD_AIRSPEED, ar_nodes[0].Velocity.length() * 1.9438);
3418#endif //OPENAL
3419}
3420
3422{
3423 Vector3 ref(Vector3::UNIT_Y);
3426
3427#ifdef USE_OPENAL
3428 //airplane radio chatter
3430 {
3431 // play random chatter at random time
3434 {
3435 SOUND_PLAY_ONCE(ar_instance_id, SS_TRIG_AVICHAT01 + Math::RangeRandom(0, 12));
3436 m_avionic_chatter_timer = Math::RangeRandom(11, 30);
3437 }
3438 }
3439#endif //openAL
3440
3441 // Wings (only physics, graphics are updated in GfxActor)
3442 float autoaileron = 0;
3443 float autorudder = 0;
3444 float autoelevator = 0;
3445 if (ar_autopilot)
3446 {
3448 autoaileron = ar_autopilot->getAilerons();
3449 autorudder = ar_autopilot->getRudder();
3450 autoelevator = ar_autopilot->getElevator();
3452 }
3453 autoaileron += ar_aileron;
3454 autorudder += ar_rudder;
3455 autoelevator += ar_elevator;
3456 if (autoaileron < -1.0)
3457 autoaileron = -1.0;
3458 if (autoaileron > 1.0)
3459 autoaileron = 1.0;
3460 if (autorudder < -1.0)
3461 autorudder = -1.0;
3462 if (autorudder > 1.0)
3463 autorudder = 1.0;
3464 if (autoelevator < -1.0)
3465 autoelevator = -1.0;
3466 if (autoelevator > 1.0)
3467 autoelevator = 1.0;
3468 for (int i = 0; i < ar_num_wings; i++)
3469 {
3470 if (ar_wings[i].fa->type == 'a')
3471 ar_wings[i].fa->setControlDeflection(autoaileron);
3472 if (ar_wings[i].fa->type == 'b')
3473 ar_wings[i].fa->setControlDeflection(-autoaileron);
3474 if (ar_wings[i].fa->type == 'r')
3475 ar_wings[i].fa->setControlDeflection(autorudder);
3476 if (ar_wings[i].fa->type == 'e' || ar_wings[i].fa->type == 'S' || ar_wings[i].fa->type == 'T')
3477 ar_wings[i].fa->setControlDeflection(autoelevator);
3478 if (ar_wings[i].fa->type == 'f')
3480 if (ar_wings[i].fa->type == 'c' || ar_wings[i].fa->type == 'V')
3481 ar_wings[i].fa->setControlDeflection((autoaileron + autoelevator) / 2.0);
3482 if (ar_wings[i].fa->type == 'd' || ar_wings[i].fa->type == 'U')
3483 ar_wings[i].fa->setControlDeflection((-autoaileron + autoelevator) / 2.0);
3484 if (ar_wings[i].fa->type == 'g')
3485 ar_wings[i].fa->setControlDeflection((autoaileron + FLAP_ANGLES[ar_aerial_flap]) / 2.0);
3486 if (ar_wings[i].fa->type == 'h')
3487 ar_wings[i].fa->setControlDeflection((-autoaileron + FLAP_ANGLES[ar_aerial_flap]) / 2.0);
3488 if (ar_wings[i].fa->type == 'i')
3489 ar_wings[i].fa->setControlDeflection((-autoelevator + autorudder) / 2.0);
3490 if (ar_wings[i].fa->type == 'j')
3491 ar_wings[i].fa->setControlDeflection((autoelevator + autorudder) / 2.0);
3492 ar_wings[i].fa->updateVerticesPhysics(); // Actual graphics update moved to GfxActor
3493 }
3494 //setup commands for hydros
3495 ar_hydro_aileron_command = autoaileron;
3496 ar_hydro_rudder_command = autorudder;
3497 ar_hydro_elevator_command = autoelevator;
3498}
3499
3501{
3502 // We can't assert the beam setup here because ropes do it differently (not actually using inter-beams, just exhibiting the same gamelogic).
3503 beam->bm_locked_actor = other; // This isn't entirely valid for 'ropes' either, but for compatibility I won't touch it now ~ ohlidalp, 2024
3504
3505 auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam);
3506 ROR_ASSERT(pos == ar_inter_beams.end());
3507 if (pos == ar_inter_beams.end())
3508 {
3509 ar_inter_beams.push_back(beam);
3510 }
3511
3512 const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3513 ROR_ASSERT(App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam) == App::GetGameContext()->GetActorManager()->inter_actor_links.end());
3514 std::pair<ActorPtr, ActorPtr> actor_pair(this, other);
3515 App::GetGameContext()->GetActorManager()->inter_actor_links[beam] = actor_pair;
3516 const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3517
3518 if (linked_before != linked_now)
3519 {
3520 // Update lists of directly/indirectly linked actors.
3521 this->DetermineLinkedActors();
3522 for (ActorPtr& actor : this->ar_linked_actors)
3523 actor->DetermineLinkedActors();
3524
3525 other->DetermineLinkedActors();
3526 for (ActorPtr& actor : other->ar_linked_actors)
3527 actor->DetermineLinkedActors();
3528
3529 // Forward toggled states.
3530 for (ActorPtr& actor : this->ar_linked_actors)
3531 {
3532 actor->ar_physics_paused = this->ar_physics_paused;
3533 actor->GetGfxActor()->SetDebugView(this->GetGfxActor()->GetDebugView());
3534 }
3535
3536 // Let scripts know.
3538 }
3539}
3540
3542{
3545 ActorPtr other = beam->bm_locked_actor;
3546 beam->bm_locked_actor = nullptr;
3547
3548 auto pos = std::find(ar_inter_beams.begin(), ar_inter_beams.end(), beam);
3549 ROR_ASSERT(pos != ar_inter_beams.end());
3550 if (pos != ar_inter_beams.end())
3551 {
3552 ar_inter_beams.erase(pos);
3553 }
3554
3555 const bool linked_before = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3556 auto it = App::GetGameContext()->GetActorManager()->inter_actor_links.find(beam);
3557 ROR_ASSERT(it != App::GetGameContext()->GetActorManager()->inter_actor_links.end());
3558 if (it != App::GetGameContext()->GetActorManager()->inter_actor_links.end())
3559 {
3561 }
3562 const bool linked_now = App::GetGameContext()->GetActorManager()->AreActorsDirectlyLinked(this, other);
3563
3564 if (linked_before != linked_now)
3565 {
3566 // Update lists of directly/indirectly linked actors.
3567 this->DetermineLinkedActors();
3568 for (ActorPtr& actor : this->ar_linked_actors)
3569 actor->DetermineLinkedActors();
3570
3571 // This should never fail (a disposing actor should disconnect everything first)
3572 // but there seems to be a bug at the moment.
3573 if (other->ar_state != ActorState::DISPOSED)
3574 {
3575 other->DetermineLinkedActors();
3576 for (ActorPtr& actor : other->ar_linked_actors)
3577 actor->DetermineLinkedActors();
3578
3579 // Reset toggled states.
3580 other->ar_physics_paused = false;
3582 for (ActorPtr& actor : other->ar_linked_actors)
3583 {
3584 actor->ar_physics_paused = false;
3585 actor->GetGfxActor()->SetDebugView(DebugViewType::DEBUGVIEW_NONE);
3586 }
3587 }
3588
3589 // Let scripts know.
3591 }
3592}
3593
3595{
3596 // Helper for `MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`, do not invoke otherwise!
3597 // Removes all (both ways) inter-actor connections from this actor.
3598 // Note the repetitive 'OK to be invoked...' comments are for fulltext search results.
3599 // ------------------------------------------------------------------
3600
3601 // Remove all inter-linking beams which belong to this actor.
3602 this->hookToggle(-1, ActorLinkingRequestType::HOOK_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3603 this->ropeToggle(-1, ActorLinkingRequestType::ROPE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3604 this->tieToggle(-1, ActorLinkingRequestType::TIE_RESET); // OK to be invoked here - DisjoinInterActorBeams() - `processing MSG_SIM_MODIFY/DELETE_ACTOR_REQUESTED`
3605
3606 // Remove any possible links from other actors to this actor.
3607 for (ActorPtr& other_actor : App::GetGameContext()->GetActorManager()->GetActors())
3608 {
3609 if (other_actor->ar_state != ActorState::LOCAL_SIMULATED)
3610 continue;
3611
3612 // Use the new `unlock_filter` param to only unlock the links to this actor (brute force but safe approach).
3613 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`
3614 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`
3615 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`
3616 }
3617}
3618
3619void Actor::tieToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter)
3620{
3621 ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3622
3623 // untie all ties if one is tied
3624 bool istied = false;
3625
3626 for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
3627 {
3628 // only handle ties with correct group
3629 if (group != -1 && (it->ti_group != -1 && it->ti_group != group))
3630 continue;
3631
3632 // When RESET-ing, filter by the locked actor, if specified.
3634 && forceunlock_filter != ACTORINSTANCEID_INVALID && it->ti_locked_actor && it->ti_locked_actor->ar_instance_id != forceunlock_filter)
3635 {
3636 continue;
3637 }
3638
3639 // if tied, untie it.
3640 if (it->ti_tied)
3641 {
3642 istied = !it->ti_beam->bm_disabled;
3643
3644 // tie is locked and should get unlocked and stop tying
3645 it->ti_tied = false;
3646 it->ti_tying = false;
3647 if (it->ti_locked_ropable)
3648 it->ti_locked_ropable->attached_ties--;
3649 // disable the ties beam
3650 it->ti_beam->p2 = &ar_nodes[0];
3651 it->ti_beam->bm_inter_actor = false;
3652 it->ti_beam->bm_disabled = true;
3653 if (it->ti_locked_actor != this)
3654 {
3655 this->RemoveInterActorBeam(it->ti_beam, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3656 }
3657 it->ti_locked_actor = nullptr;
3658 }
3659 }
3660
3661 // iterate over all ties
3662 if (!istied && mode == ActorLinkingRequestType::TIE_TOGGLE)
3663 {
3664 for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
3665 {
3666 // only handle ties with correct group
3667 if (group != -1 && (it->ti_group != -1 && it->ti_group != group))
3668 continue;
3669
3670 if (!it->ti_tied)
3671 {
3672 // tie is unlocked and should get locked, search new remote ropable to lock to
3673 float mindist = it->ti_beam->refL;
3674 node_t* nearest_node = 0;
3675 ActorPtr nearest_actor = 0;
3676 ropable_t* locktedto = 0;
3677 // iterate over all actors
3679 {
3680 if (actor->ar_state == ActorState::LOCAL_SLEEPING ||
3681 (actor == this && it->ti_no_self_lock))
3682 {
3683 continue;
3684 }
3685
3686 // and their ropables
3687 for (std::vector<ropable_t>::iterator itr = actor->ar_ropables.begin(); itr != actor->ar_ropables.end(); itr++)
3688 {
3689 // if the ropable is not multilock and used, then discard this ropable
3690 if (!itr->multilock && itr->attached_ties > 0)
3691 continue;
3692
3693 // skip if tienode is ropable too (no selflock)
3694 if (this == actor.GetRef() && itr->node->pos == it->ti_beam->p1->pos)
3695 continue;
3696
3697 // calculate the distance and record the nearest ropable
3698 float dist = (it->ti_beam->p1->AbsPosition - itr->node->AbsPosition).length();
3699 if (dist < mindist)
3700 {
3701 mindist = dist;
3702 nearest_node = itr->node;
3703 nearest_actor = actor;
3704 locktedto = &(*itr);
3705 }
3706 }
3707 }
3708 // if we found a ropable, then tie towards it
3709 if (nearest_node)
3710 {
3711 // enable the beam and visually display the beam
3712 it->ti_beam->bm_disabled = false;
3713 // now trigger the tying action
3714 it->ti_locked_actor = nearest_actor;
3715 it->ti_beam->p2 = nearest_node;
3716 it->ti_beam->bm_inter_actor = nearest_actor != this;
3717 it->ti_beam->stress = 0;
3718 it->ti_beam->L = it->ti_beam->refL;
3719 it->ti_tied = true;
3720 it->ti_tying = true;
3721 it->ti_locked_ropable = locktedto;
3722 it->ti_locked_ropable->attached_ties++;
3723 if (it->ti_beam->bm_inter_actor)
3724 {
3725 this->AddInterActorBeam(it->ti_beam, nearest_actor, mode); // OK to invoke here - tieToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3726 }
3727 }
3728 }
3729 }
3730 }
3731
3732 //ScriptEvent - Tie toggle
3734}
3735
3736void Actor::ropeToggle(int group, ActorLinkingRequestType mode, ActorInstanceID_t forceunlock_filter)
3737{
3738 ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
3739
3740 // iterate over all ropes
3741 for (std::vector<rope_t>::iterator it = ar_ropes.begin(); it != ar_ropes.end(); it++)
3742 {
3743 // only handle ropes with correct group
3744 if (group != -1 && (it->rp_group != -1 && it->rp_group != group))
3745 continue;
3746
3747 // When RESET-ing, filter by the locked actor, if specified.
3749 && forceunlock_filter != ACTORINSTANCEID_INVALID && it->rp_locked_actor && it->rp_locked_actor->ar_instance_id != forceunlock_filter)
3750 {
3751 continue;
3752 }
3753
3754 if (it->rp_locked == LOCKED || it->rp_locked == PRELOCK) // Do this for both `ROPE_TOGGLE` and `ROPE_RESET`
3755 {
3756 // we unlock ropes
3757 it->rp_locked = UNLOCKED;
3758 // remove node locking
3759 if (it->rp_locked_ropable)
3760 it->rp_locked_ropable->attached_ropes--;
3761 if (it->rp_locked_actor != this)
3762 {
3763 this->RemoveInterActorBeam(it->rp_beam, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3764 }
3765 it->rp_locked_actor = nullptr;
3766 it->rp_locked_ropable = nullptr;
3767 }
3768 else if (mode == ActorLinkingRequestType::ROPE_TOGGLE) // Do this only for `ROPE_TOGGLE`
3769 {
3770 //we lock ropes
3771 // search new remote ropable to lock to
3772 float mindist = it->rp_beam->L;
3773 ActorPtr nearest_actor = nullptr;
3774 ropable_t* rop = 0;
3775 // iterate over all actor_slots
3777 {
3778 if (actor->ar_state == ActorState::LOCAL_SLEEPING)
3779 continue;
3780 // and their ropables
3781 for (std::vector<ropable_t>::iterator itr = actor->ar_ropables.begin(); itr != actor->ar_ropables.end(); itr++)
3782 {
3783 // if the ropable is not multilock and used, then discard this ropable
3784 if (!itr->multilock && itr->attached_ropes > 0)
3785 continue;
3786
3787 // calculate the distance and record the nearest ropable
3788 float dist = (it->rp_beam->p1->AbsPosition - itr->node->AbsPosition).length();
3789 if (dist < mindist)
3790 {
3791 mindist = dist;
3792 nearest_actor = actor;
3793 rop = &(*itr);
3794 }
3795 }
3796 }
3797 // if we found a ropable, then lock it
3798 if (nearest_actor)
3799 {
3800 //okay, we have found a rope to tie
3801 it->rp_locked = LOCKED;
3802 it->rp_locked_ropable = rop;
3803 it->rp_locked_ropable->attached_ropes++;
3804 if (nearest_actor != this)
3805 {
3806 this->AddInterActorBeam(it->rp_beam, nearest_actor, mode); // OK to invoke here - ropeToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3807 }
3808 }
3809 }
3810 }
3811}
3812
3813void Actor::hookToggle(int group, ActorLinkingRequestType mode, NodeNum_t mousenode /*=NODENUM_INVALID*/, ActorInstanceID_t forceunlock_filter)
3814{
3818
3819 // iterate over all hooks
3820 for (std::vector<hook_t>::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++)
3821 {
3822 if (mode == ActorLinkingRequestType::HOOK_MOUSE_TOGGLE && it->hk_hook_node->pos != mousenode)
3823 {
3824 //skip all other nodes except the one manually toggled by mouse
3825 continue;
3826 }
3827 if (mode == ActorLinkingRequestType::HOOK_TOGGLE && group == -1)
3828 {
3829 //manually triggerd (EV_COMMON_LOCK). Toggle all hooks groups with group#: -1, 0, 1 ++
3830 if (it->hk_group <= -2)
3831 continue;
3832 }
3833 if (mode == ActorLinkingRequestType::HOOK_LOCK && group == -2)
3834 {
3835 //automatic lock attempt (cyclic with doupdate). Toggle all hooks groups with group#: -2, -3, -4 --, skip the ones which are not autolock (triggered only)
3836 if (it->hk_group >= -1 || !it->hk_autolock)
3837 continue;
3838 }
3839 if (mode == ActorLinkingRequestType::HOOK_UNLOCK && group == -2)
3840 {
3841 //manual unlock ALL autolock and triggerlock, do not unlock standard hooks (EV_COMMON_AUTOLOCK)
3842 if (it->hk_group >= -1 || !it->hk_autolock)
3843 continue;
3844 }
3845 if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group <= -3)
3846 {
3847 //trigger beam lock or unlock. Toggle one hook group with group#: group
3848 if (it->hk_group != group)
3849 continue;
3850 }
3851 if ((mode == ActorLinkingRequestType::HOOK_LOCK || mode == ActorLinkingRequestType::HOOK_UNLOCK) && group >= -1)
3852 {
3853 continue;
3854 }
3855 if (mode == ActorLinkingRequestType::HOOK_LOCK && it->hk_timer > 0.0f)
3856 {
3857 //check relock delay timer for autolock nodes and skip if not 0
3858 continue;
3859 }
3860
3861 // When RESET-ing, filter by the locked actor, if specified.
3863 && forceunlock_filter != ACTORINSTANCEID_INVALID && it->hk_locked_actor && it->hk_locked_actor->ar_instance_id != forceunlock_filter)
3864 {
3865 continue;
3866 }
3867
3868 ActorPtr prev_locked_actor = it->hk_locked_actor; // memorize current value
3869
3870 // do this only for toggle or lock attempts, skip prelocked or locked nodes for performance
3871 if ((mode != ActorLinkingRequestType::HOOK_UNLOCK && mode != ActorLinkingRequestType::HOOK_RESET) && it->hk_locked == UNLOCKED)
3872 {
3873 // we lock hooks
3874 // search new remote ropable to lock to
3875 float mindist = it->hk_lockrange;
3876 float distance = 100000000.0f;
3877 // iterate over all actor_slots
3879 {
3880 if (actor->ar_state == ActorState::LOCAL_SLEEPING)
3881 continue;
3882 if (this == actor.GetRef() && !it->hk_selflock)
3883 continue; // don't lock to self
3884
3885 node_t* nearest_node = nullptr;
3886 for (int i = 0; i < actor->ar_num_nodes; i++)
3887 {
3888 // skip all nodes with lockgroup 9999 (deny lock)
3889 if (actor->ar_nodes[i].nd_lockgroup == 9999)
3890 continue;
3891
3892 // exclude this truck and its current hooknode from the locking search
3893 if (this == actor.GetRef() && i == it->hk_hook_node->pos)
3894 continue;
3895
3896 // a lockgroup for this hooknode is set -> skip all nodes that do not have the same lockgroup (-1 = default(all nodes))
3897 if (it->hk_lockgroup != -1 && it->hk_lockgroup != actor->ar_nodes[i].nd_lockgroup)
3898 continue;
3899
3900 // measure distance
3901 float n2n_distance = (it->hk_hook_node->AbsPosition - actor->ar_nodes[i].AbsPosition).length();
3902 if (n2n_distance < mindist)
3903 {
3904 if (distance >= n2n_distance)
3905 {
3906 // located a node that is closer
3907 distance = n2n_distance;
3908 nearest_node = &actor->ar_nodes[i];
3909 }
3910 }
3911 }
3912 if (nearest_node)
3913 {
3914 // we found a node, lock to it
3915 it->hk_lock_node = nearest_node;
3916 it->hk_locked_actor = actor;
3917 it->hk_locked = PRELOCK;
3918 //enable beam if not enabled yet between those 2 nodes
3919 if (it->hk_beam->bm_disabled)
3920 {
3921 it->hk_beam->p2 = it->hk_lock_node;
3922 it->hk_beam->bm_inter_actor = (it->hk_locked_actor != nullptr);
3923 it->hk_beam->L = (it->hk_hook_node->AbsPosition - it->hk_lock_node->AbsPosition).length();
3924 it->hk_beam->bm_disabled = false;
3925 this->AddInterActorBeam(it->hk_beam, it->hk_locked_actor, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3926 }
3927 }
3928 }
3929 }
3930 // this is a locked or prelocked hook and its not a locking attempt or the locked actor was removed (bm_inter_actor == false)
3931 else if ((it->hk_locked == LOCKED || it->hk_locked == PRELOCK) && (mode != ActorLinkingRequestType::HOOK_LOCK || !it->hk_beam->bm_inter_actor))
3932 {
3933 // we unlock ropes immediatelly
3934 it->hk_locked = UNLOCKED;
3935 this->RemoveInterActorBeam(it->hk_beam, mode); // OK to invoke here - hookToggle() - processing `MSG_SIM_ACTOR_LINKING_REQUESTED`
3936 if (it->hk_group <= -2)
3937 {
3938 // autolock timer: if we're hard-resetting the actor, force immediate relock, otherwise restart the countdown.
3939 it->hk_timer = (mode == ActorLinkingRequestType::HOOK_RESET) ? 0.f : it->hk_timer_preset;
3940 }
3941 it->hk_lock_node = 0;
3942 it->hk_locked_actor = 0;
3943 //disable hook-assistance beam
3944 it->hk_beam->p2 = &ar_nodes[0];
3945 it->hk_beam->bm_inter_actor = false;
3946 it->hk_beam->L = (ar_nodes[0].AbsPosition - it->hk_hook_node->AbsPosition).length();
3947 it->hk_beam->bm_disabled = true;
3948 }
3949 }
3950}
3951
3966
3968{
3970 return;
3971
3972 if (!alb_notoggle)
3973 alb_mode = !alb_mode;
3974}
3975
3977{
3979 return;
3980
3981 if (!tc_notoggle)
3982 tc_mode = !tc_mode;
3983}
3984
3986{
3988 return;
3989
3991 {
3992 return;
3993 }
3994 // flip the flag
3996
3997 //ScriptEvent - Beacon toggle
3999}
4000
4002{
4003#ifdef USE_OPENAL
4005 return;
4006
4007 for (int i = 0; i < ar_num_soundsources; i++)
4008 {
4009 if (ar_soundsources[i].ssi)
4010 ar_soundsources[i].ssi->setEnabled(false);
4011 }
4012#endif // USE_OPENAL
4013}
4014
4016{
4017#ifdef USE_OPENAL
4019 return;
4020
4021 for (int i = 0; i < ar_num_soundsources; i++)
4022 {
4023 bool enabled = (ar_soundsources[i].type == -2 || ar_soundsources[i].type == ar_current_cinecam);
4024 ar_soundsources[i].ssi->setEnabled(enabled);
4025 }
4026#endif // USE_OPENAL
4027}
4028
4030{
4031 // change sound setup
4032#ifdef USE_OPENAL
4034 return;
4035
4036 for (int i = 0; i < ar_num_soundsources; i++)
4037 {
4038 bool enabled = (ar_soundsources[i].type == -2 || ar_soundsources[i].type == ar_current_cinecam);
4039 ar_soundsources[i].ssi->setEnabled(enabled);
4040 }
4041#endif // USE_OPENAL
4042
4043 // NOTE: Prop visibility now updated in GfxActor::UpdateProps() ~ only_a_ptr, 06/2018
4044
4045
4046}
4047
4048//Returns the number of active (non bounded) beams connected to a node
4050{
4051 int totallivebeams = 0;
4052 for (unsigned int ni = 0; ni < ar_node_to_beam_connections[nodeid].size(); ++ni)
4053 {
4054 if (!ar_beams[ar_node_to_beam_connections[nodeid][ni]].bm_disabled && !ar_beams[ar_node_to_beam_connections[nodeid][ni]].bounded)
4055 totallivebeams++;
4056 }
4057 return totallivebeams;
4058}
4059
4061{
4062 for (std::vector<tie_t>::iterator it = ar_ties.begin(); it != ar_ties.end(); it++)
4063 if (it->ti_tied)
4064 return true;
4065 return false;
4066}
4067
4069{
4070 for (std::vector<hook_t>::iterator it = ar_hooks.begin(); it != ar_hooks.end(); it++)
4071 if (it->hk_locked == LOCKED)
4072 return true;
4073 return false;
4074}
4075
4077{
4078 if (!ar_dashboard)
4079 return;
4080 // some temp vars
4081 Vector3 dir;
4082
4083 // engine and gears
4084 if (ar_engine)
4085 {
4086 // gears first
4087 int gear = ar_engine->getGear();
4089
4090 int numGears = (int)ar_engine->getNumGears();
4092
4093 String str = String();
4094
4095 // now construct that classic gear string
4096 if (gear > 0)
4097 str = TOSTRING(gear) + "/" + TOSTRING(numGears);
4098 else if (gear == 0)
4099 str = String("N");
4100 else
4101 str = String("R");
4102
4104
4105 // R N D 2 1 String
4106 int cg = ar_engine->getAutoShift();
4107 if (cg != Engine::MANUALMODE)
4108 {
4109 str = ((cg == Engine::REAR) ? "#ffffff" : "#868686") + String("R\n");
4110 str += ((cg == Engine::NEUTRAL) ? "#ff0012" : "#8a000a") + String("N\n");
4111 str += ((cg == Engine::DRIVE) ? "#12ff00" : "#248c00") + String("D\n");
4112 str += ((cg == Engine::TWO) ? "#ffffff" : "#868686") + String("2\n");
4113 str += ((cg == Engine::ONE) ? "#ffffff" : "#868686") + String("1");
4114 }
4115 else
4116 {
4117 //str = "#b8b8b8M\na\nn\nu\na\nl";
4118 str = "#b8b8b8M\na\nn\nu";
4119 }
4121
4122 // autogears
4123 int autoGear = ar_engine->getAutoShift();
4125
4126 // clutch
4127 float clutch = ar_engine->getClutch();
4129
4130 // accelerator
4131 float acc = ar_engine->getAcc();
4133
4134 // RPM
4135 float rpm = ar_engine->getRPM();
4138
4139 // engine running
4140 bool engRun = ar_engine->isRunning();
4142
4143 // engine off
4144 bool engOff = !ar_engine->isRunning();
4146
4147 // turbo
4148 float turbo = ar_engine->getTurboPSI() * 3.34f; // MAGIC :/
4150
4151 // ignition
4152 bool ign = (ar_engine->hasContact() && !ar_engine->isRunning());
4154
4155 // battery
4156 bool batt = (ar_engine->hasContact() && !ar_engine->isRunning());
4158
4159 // clutch warning
4160 bool cw = (fabs(ar_engine->getTorque()) >= ar_engine->getClutchForce() * 10.0f);
4162 }
4163
4164 // brake
4166
4167 Vector3 cam_dir = this->GetCameraDir();
4168 Vector3 cam_roll = this->GetCameraRoll();
4169
4170 // speedo
4171 float velocity = ar_nodes[0].Velocity.length();
4172 if (cam_dir != Vector3::ZERO)
4173 {
4174 velocity = cam_dir.dotProduct(ar_nodes[0].Velocity);
4175 }
4176
4177 // KPH
4178 float cur_speed_kph = ar_wheel_speed * 3.6f;
4179 float smooth_kph = (cur_speed_kph * 0.3) + (ar_dashboard->_getFloat(DD_ENGINE_SPEEDO_KPH) * 0.7);
4182
4183 // MPH
4184 float cur_speed_mph = ar_wheel_speed * 2.23693629f;
4185 float smooth_mph = (cur_speed_mph * 0.3) + (ar_dashboard->_getFloat(DD_ENGINE_SPEEDO_MPH) * 0.7);
4187
4188 // roll
4189 if (cam_roll != Vector3::ZERO)
4190 {
4191 float angle = asin(cam_roll.dotProduct(Vector3::UNIT_Y));
4192
4193 float f = Radian(angle).valueDegrees();
4195 }
4196
4197 // active shocks / roll correction
4198 if (this->ar_has_active_shocks)
4199 {
4200 // TOFIX: certainly not working:
4201 float roll_corr = - m_stabilizer_shock_ratio * 10.0f;
4202 ar_dashboard->setFloat(DD_ROLL_CORR, roll_corr);
4203
4204 bool corr_active = (m_stabilizer_shock_request > 0);
4206 }
4207
4208 // pitch
4209 if (cam_dir != Vector3::ZERO)
4210 {
4211 float angle = asin(cam_dir.dotProduct(Vector3::UNIT_Y));
4212
4213 float f = Radian(angle).valueDegrees();
4215 }
4216
4217 // parking brake
4219
4220 // locked lamp
4221 bool locked = isLocked();
4222 ar_dashboard->setBool(DD_LOCKED, locked);
4223
4224 // low pressure lamp
4225 bool low_pres = !ar_engine_hydraulics_ready;
4227
4228 // turn signals already done by `updateFlareStates()` and `setBlinkType()`
4229
4230 // Traction Control
4231 if (!tc_nodash)
4232 {
4233 int dash_tc_mode = 1; // 0 = not present, 1 = off, 2 = on, 3 = active
4234 if (tc_mode)
4235 {
4237 dash_tc_mode = 3;
4238 else
4239 dash_tc_mode = 2;
4240 }
4242 }
4243
4244 // Anti Lock Brake
4245 if (!alb_nodash)
4246 {
4247 int dash_alb_mode = 1; // 0 = not present, 1 = off, 2 = on, 3 = active
4248 if (alb_mode)
4249 {
4250 if (m_antilockbrake)
4251 dash_alb_mode = 3;
4252 else
4253 dash_alb_mode = 2;
4254 }
4255 ar_dashboard->setInt(DD_ANTILOCKBRAKE_MODE, dash_alb_mode);
4256 }
4257
4258 // load secured lamp
4259 int ties_mode = 0; // 0 = not locked, 1 = prelock, 2 = lock
4260 if (isTied())
4261 {
4262 if (fabs(ar_command_key[0].commandValue) > 0.000001f)
4263 ties_mode = 1;
4264 else
4265 ties_mode = 2;
4266 }
4267 ar_dashboard->setInt(DD_TIES_MODE, ties_mode);
4268
4269 // Cruise control lamp
4271
4272 // Boat things now: screwprops and alike
4274 {
4275 // the throttle and rudder
4276 for (int i = 0; i < ar_num_screwprops && i < DD_MAX_SCREWPROP; i++)
4277 {
4278 float throttle = ar_screwprops[i]->getThrottle();
4280
4281 float steering = ar_screwprops[i]->getRudder();
4282 ar_dashboard->setFloat(DD_SCREW_STEER_0 + i, steering);
4283 }
4284
4285 // water depth display, only if we have a screw prop at least
4286 float depth = this->getHeightAboveGround();
4288
4289 // water speed
4290 Vector3 hdir = this->GetCameraDir();
4291 float knots = hdir.dotProduct(ar_nodes[ar_main_camera_node_pos].Velocity) * 1.9438f; // 1.943 = m/s in knots/s
4293 }
4294
4295 // now airplane things, aeroengines, etc.
4297 {
4298 for (int i = 0; i < ar_num_aeroengines && i < DD_MAX_AEROENGINE; i++)
4299 {
4300 float throttle = ar_aeroengines[i]->getThrottle();
4302
4303 bool failed = ar_aeroengines[i]->isFailed();
4305
4306 float pcent = ar_aeroengines[i]->getRPMpc();
4308 }
4309 }
4310
4311 // wings stuff, you dont need an aeroengine
4312 if (ar_num_wings)
4313 {
4314 for (int i = 0; i < ar_num_wings && i < DD_MAX_WING; i++)
4315 {
4316 // Angle of Attack (AOA)
4317 float aoa = ar_wings[i].fa->aoa;
4319 }
4320 }
4321
4322 // some things only activate when a wing or an aeroengine is present
4324 {
4325 //airspeed
4326 {
4327 float ground_speed_kt = ar_nodes[0].Velocity.length() * 1.9438f; // 1.943 = m/s in knots/s
4328
4329 //tropospheric model valid up to 11.000m (33.000ft)
4330 float altitude = ar_nodes[0].AbsPosition.y;
4331 //float sea_level_temperature = 273.15 + 15.0; //in Kelvin // MAGICs D:
4332 float sea_level_pressure = 101325; //in Pa
4333 //float airtemperature = sea_level_temperature - altitude * 0.0065f; //in Kelvin
4334 float airpressure = sea_level_pressure * pow(1.0f - 0.0065f * altitude / 288.15f, 5.24947f); //in Pa
4335 float airdensity = airpressure * 0.0000120896f; //1.225 at sea level
4336
4337 float knots = ground_speed_kt * sqrt(airdensity / 1.225f); //KIAS
4339 }
4340
4341 // altimeter (height above ground)
4342 {
4343 float alt = ar_nodes[0].AbsPosition.y * 1.1811f; // MAGIC
4345
4346 char altc[11];
4347 sprintf(altc, "%03u", (int)(ar_nodes[0].AbsPosition.y / 30.48f)); // MAGIC
4349 }
4350 }
4351
4354
4355 // set the features of this vehicle once
4356 if (!m_hud_features_ok)
4357 {
4358 bool hasEngine = (ar_engine != nullptr);
4359 bool hasturbo = false;
4360 bool autogearVisible = false;
4361
4362 if (hasEngine)
4363 {
4364 hasturbo = ar_engine->hasTurbo();
4365 autogearVisible = (ar_engine->getAutoShift() != Engine::MANUALMODE);
4366 }
4367
4379
4384
4386
4388 m_hud_features_ok = true;
4389 }
4390
4391 // Lights (all kinds)
4392 // PLEASE maintain the same order as in 'DashBoardManager.h'
4393
4404
4413
4417
4418 // TODO: compass value
4419
4420#if 0
4421 // ADI - attitude director indicator
4422 //roll
4423 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;
4424 rollv.normalise();
4425 float rollangle=asin(rollv.dotProduct(Vector3::UNIT_Y));
4426
4427 //pitch
4428 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;
4429 dirv.normalise();
4430 float pitchangle=asin(dirv.dotProduct(Vector3::UNIT_Y));
4431 Vector3 upv=dirv.crossProduct(-rollv);
4432 if (upv.y<0) rollangle=3.14159-rollangle;
4433 RoR::App::GetOverlayWrapper()->adibugstexture->setTextureRotate(Radian(-rollangle));
4434 RoR::App::GetOverlayWrapper()->aditapetexture->setTextureVScroll(-pitchangle*0.25);
4435 RoR::App::GetOverlayWrapper()->aditapetexture->setTextureRotate(Radian(-rollangle));
4436
4437 // HSI - Horizontal Situation Indicator
4438 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;
4439 // idir.normalise();
4440 float dirangle=atan2(idir.dotProduct(Vector3::UNIT_X), idir.dotProduct(-Vector3::UNIT_Z));
4441 RoR::App::GetOverlayWrapper()->hsirosetexture->setTextureRotate(Radian(dirangle));
4442 if (curr_truck->autopilot)
4443 {
4444 RoR::App::GetOverlayWrapper()->hsibugtexture->setTextureRotate(Radian(dirangle)-Degree(curr_truck->autopilot->heading));
4445 float vdev=0;
4446 float hdev=0;
4447 curr_truck->autopilot->getRadioFix(localizers, free_localizer, &vdev, &hdev);
4448 if (hdev>15) hdev=15;
4449 if (hdev<-15) hdev=-15;
4450 RoR::App::GetOverlayWrapper()->hsivtexture->setTextureUScroll(-hdev*0.02);
4451 if (vdev>15) vdev=15;
4452 if (vdev<-15) vdev=-15;
4453 RoR::App::GetOverlayWrapper()->hsihtexture->setTextureVScroll(-vdev*0.02);
4454 }
4455
4456 // VVI - Vertical Velocity Indicator
4457 float vvi=curr_truck->ar_nodes[0].Velocity.y*196.85;
4458 if (vvi<1000.0 && vvi>-1000.0) angle=vvi*0.047;
4459 if (vvi>1000.0 && vvi<6000.0) angle=47.0+(vvi-1000.0)*0.01175;
4460 if (vvi>6000.0) angle=105.75;
4461 if (vvi<-1000.0 && vvi>-6000.0) angle=-47.0+(vvi+1000.0)*0.01175;
4462 if (vvi<-6000.0) angle=-105.75;
4463 RoR::App::GetOverlayWrapper()->vvitexture->setTextureRotate(Degree(-angle+90.0));
4464
4465
4466 if (curr_truck->aeroengines[0]->getType() == AeroEngineType::AE_XPROP)
4467 {
4468 Turboprop *tp=(Turboprop*)curr_truck->aeroengines[0];
4469 //pitch
4470 RoR::App::GetOverlayWrapper()->airpitch1texture->setTextureRotate(Degree(-tp->pitch*2.0));
4471 //torque
4472 pcent=100.0*tp->indicated_torque/tp->max_torque;
4473 if (pcent<60.0) angle=-5.0+pcent*1.9167;
4474 else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4475 else angle=314.0;
4476 RoR::App::GetOverlayWrapper()->airtorque1texture->setTextureRotate(Degree(-angle));
4477 }
4478
4479 if (ftp>1 && curr_truck->aeroengines[1]->getType() == AeroEngineType::AE_XPROP)
4480 {
4481 Turboprop *tp=(Turboprop*)curr_truck->aeroengines[1];
4482 //pitch
4483 RoR::App::GetOverlayWrapper()->airpitch2texture->setTextureRotate(Degree(-tp->pitch*2.0));
4484 //torque
4485 pcent=100.0*tp->indicated_torque/tp->max_torque;
4486 if (pcent<60.0) angle=-5.0+pcent*1.9167;
4487 else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4488 else angle=314.0;
4489 RoR::App::GetOverlayWrapper()->airtorque2texture->setTextureRotate(Degree(-angle));
4490 }
4491
4492 if (ftp>2 && curr_truck->aeroengines[2]->getType() == AeroEngineType::AE_XPROP)
4493 {
4494 Turboprop *tp=(Turboprop*)curr_truck->aeroengines[2];
4495 //pitch
4496 RoR::App::GetOverlayWrapper()->airpitch3texture->setTextureRotate(Degree(-tp->pitch*2.0));
4497 //torque
4498 pcent=100.0*tp->indicated_torque/tp->max_torque;
4499 if (pcent<60.0) angle=-5.0+pcent*1.9167;
4500 else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4501 else angle=314.0;
4502 RoR::App::GetOverlayWrapper()->airtorque3texture->setTextureRotate(Degree(-angle));
4503 }
4504
4505 if (ftp>3 && curr_truck->aeroengines[3]->getType() == AeroEngineType::AE_XPROP)
4506 {
4507 Turboprop *tp=(Turboprop*)curr_truck->aeroengines[3];
4508 //pitch
4509 RoR::App::GetOverlayWrapper()->airpitch4texture->setTextureRotate(Degree(-tp->pitch*2.0));
4510 //torque
4511 pcent=100.0*tp->indicated_torque/tp->max_torque;
4512 if (pcent<60.0) angle=-5.0+pcent*1.9167;
4513 else if (pcent<110.0) angle=110.0+(pcent-60.0)*4.075;
4514 else angle=314.0;
4515 RoR::App::GetOverlayWrapper()->airtorque4texture->setTextureRotate(Degree(-angle));
4516 }
4517
4518 //starters
4519 if (curr_truck->aeroengines[0]->getIgnition()) RoR::App::GetOverlayWrapper()->engstarto1->setMaterialName("tracks/engstart-on"); else RoR::App::GetOverlayWrapper()->engstarto1->setMaterialName("tracks/engstart-off");
4520 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");
4521 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");
4522 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");
4523}
4524
4525#endif //0
4526 ar_dashboard->update(dt);
4527}
4528
4530{
4531 Vector3 cam_dir = this->GetCameraDir();
4532 Vector3 cam_roll = this->GetCameraRoll();
4533 Vector3 cam_up = cam_dir.crossProduct(cam_roll);
4534
4535 float gravity = App::GetGameContext()->GetTerrain()->getGravity();
4536
4537 float vertacc = m_camera_gforces.dotProduct(cam_up) + gravity;
4538 float longacc = m_camera_gforces.dotProduct(cam_dir);
4539 float latacc = m_camera_gforces.dotProduct(cam_roll);
4540
4541 m_camera_local_gforces_cur = Vector3(vertacc, longacc, latacc) / gravity;
4542
4543 // Let it settle before we start recording the maximum forces
4544 if (m_reset_timer.getMilliseconds() > 500)
4545 {
4548 }
4549}
4550
4551void Actor::engineTriggerHelper(int engineNumber, EngineTriggerType type, float triggerValue)
4552{
4553 // engineNumber = placeholder: actors do not have multiple engines yet
4554
4555 switch (type)
4556 {
4557 case TRG_ENGINE_CLUTCH:
4558 if (ar_engine)
4559 ar_engine->setClutch(triggerValue);
4560 break;
4561 case TRG_ENGINE_BRAKE:
4562 ar_brake = triggerValue;
4563 break;
4564 case TRG_ENGINE_ACC:
4565 if (ar_engine)
4566 ar_engine->setAcc(triggerValue);
4567 break;
4568 case TRG_ENGINE_RPM:
4569 // TODO: Implement setTargetRPM in the Engine.cpp
4570 break;
4571 case TRG_ENGINE_SHIFTUP:
4572 if (ar_engine)
4573 ar_engine->shift(1);
4574 break;
4576 if (ar_engine)
4577 ar_engine->shift(-1);
4578 break;
4579 default:
4580 break;
4581 }
4582}
4583
4585 ActorInstanceID_t actor_id,
4586 unsigned int vector_index,
4589)
4590 // Special values
4591 : ar_nb_optimum(7, std::numeric_limits<float>::max())
4592 , ar_nb_reference(7, std::numeric_limits<float>::max())
4593 , m_tyre_pressure(this)
4594 , ar_nb_beams_scale(std::make_pair(1.0f, 1.0f))
4595 , ar_nb_shocks_scale(std::make_pair(1.0f, 1.0f))
4596 , ar_nb_wheels_scale(std::make_pair(1.0f, 1.0f))
4597 , ar_nb_beams_k_interval(std::make_pair(0.1f, 2.0f))
4598 , ar_nb_beams_d_interval(std::make_pair(0.1f, 2.0f))
4599 , ar_nb_shocks_k_interval(std::make_pair(0.1f, 8.0f))
4600 , ar_nb_shocks_d_interval(std::make_pair(0.1f, 8.0f))
4601 , ar_nb_wheels_k_interval(std::make_pair(1.0f, 1.0f))
4602 , ar_nb_wheels_d_interval(std::make_pair(1.0f, 1.0f))
4603
4604 // Constructor parameters
4605 , m_avg_node_position_prev(rq.asr_position)
4606 , m_preloaded_with_terrain(rq.asr_origin == RoR::ActorSpawnRequest::Origin::TERRN_DEF)
4607 , m_avg_node_position(rq.asr_position)
4608 , ar_instance_id(actor_id)
4609 , ar_vector_index(vector_index)
4610 , m_avg_proped_wheel_radius(0.2f)
4611 , ar_filename(rq.asr_cache_entry->fname)
4612 , m_section_config(rq.asr_config)
4613 , m_used_actor_entry(rq.asr_cache_entry)
4614 , m_used_skin_entry(rq.asr_skin_entry)
4615 , m_working_tuneup_def(rq.asr_working_tuneup)
4616
4617 // Public bit flags
4618 , ar_update_physics(false)
4619 , ar_disable_aerodyn_turbulent_drag(false)
4620 , ar_engine_hydraulics_ready(true) // !!
4621 , ar_guisettings_use_engine_max_rpm(false)
4622 , ar_hydro_speed_coupling_active(false)
4623 , ar_hydro_speed_coupling_enabled(false)
4624 , ar_arcade_controls(false)
4625 , ar_collision_relevant(false)
4626 , ar_is_police(false)
4627 , ar_rescuer_flag(false)
4628 , ar_forward_commands(false)
4629 , ar_import_commands(false)
4630 , ar_toggle_ropes(false)
4631 , ar_toggle_ties(false)
4632
4633 // Private bit flags
4634 , m_hud_features_ok(false)
4635 , m_slidenodes_locked(false)
4636 , m_net_initialized(false)
4637 , m_water_contact(false)
4638 , m_water_contact_old(false)
4639 , m_has_command_beams(false)
4640 , ar_cparticles_active(false)
4641 , m_beam_break_debug_enabled(false)
4642 , m_beam_deform_debug_enabled(false)
4643 , m_trigger_debug_enabled(false)
4644 , m_disable_default_sounds(false)
4645 , m_disable_smoke(false)
4646{
4647}
4648
4650{
4651 return ar_hydro_dir_command;
4652}
4653
4654std::vector<authorinfo_t> Actor::getAuthors()
4655{
4656 return authors;
4657}
4658
4659std::vector<std::string> Actor::getDescription()
4660{
4661 return m_description;
4662}
4663
4664void Actor::setMass(float m)
4665{
4666 ar_dry_mass = m;
4667}
4668
4670{
4671 ar_load_mass = m;
4672}
4673
4674void Actor::setNodeMass(int nodeNumber, float m)
4675{
4676 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4677 {
4678 ar_nodes_override_loadweights[nodeNumber] = m;
4679 }
4680}
4681
4682void Actor::setNodeMassOptions(int nodeNumber, bool loaded, bool overrideMass)
4683{
4684 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4685 {
4686 ar_nodes[nodeNumber].nd_loaded_mass = loaded;
4687 ar_nodes[nodeNumber].nd_override_mass = overrideMass;
4688 }
4689}
4690
4692{
4693 if (number < 0 || number >= MAX_CLIGHTS)
4694 {
4695 LOG(fmt::format("invalid custom-light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4696 return false;
4697 }
4698
4699 if (number == 0) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM1);
4700 if (number == 1) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM2);
4701 if (number == 2) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM3);
4702 if (number == 3) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM4);
4703 if (number == 4) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM5);
4704 if (number == 5) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM6);
4705 if (number == 6) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM7);
4706 if (number == 7) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM8);
4707 if (number == 8) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM9);
4708 if (number == 9) return (m_lightmask & RoRnet::LIGHTMASK_CUSTOM10);
4709
4710 return false;
4711}
4712
4713void Actor::setCustomLightVisible(int number, bool visible)
4714{
4715 if (number < 0 || number >= MAX_CLIGHTS)
4716 {
4717 LOG(fmt::format("invalid Light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4718 return;
4719 }
4720
4721 if (number == 0) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM1 , visible);
4722 if (number == 1) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM2 , visible);
4723 if (number == 2) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM3 , visible);
4724 if (number == 3) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM4 , visible);
4725 if (number == 4) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM5 , visible);
4726 if (number == 5) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM6 , visible);
4727 if (number == 6) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM7 , visible);
4728 if (number == 7) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM8 , visible);
4729 if (number == 8) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM9 , visible);
4730 if (number == 9) BITMASK_SET(m_lightmask, RoRnet::LIGHTMASK_CUSTOM10, visible);
4731}
4732
4734{
4735 if (number < 0 || number >= MAX_CLIGHTS)
4736 {
4737 LOG(fmt::format("invalid custom-light ID {}, allowed range is 0-{}", number, MAX_CLIGHTS-1));
4738 return -1;
4739 }
4740
4741 int count = 0;
4742 for (int i = 0; i < ar_flares.size(); i++)
4743 {
4744 if (ar_flares[i].controlnumber == number)
4745 count++;
4746 }
4747 return count;
4748}
4749
4751{
4752 int count = 0;
4753 for (int i = 0; i < ar_flares.size(); i++)
4754 {
4755 if (ar_flares[i].fl_type == type)
4756 count++;
4757 }
4758 return count;
4759}
4760
4768
4773
4775{
4776 return m_min_camera_radius;
4777}
4778
4780{
4781 return (index >= 0 && index < ar_num_aeroengines) ? ar_aeroengines[index] : nullptr;
4782}
4783
4785{
4786 if (index >= 0 && index < ar_num_aeroengines &&
4787 ar_aeroengines[index]->getType() == AeroEngineType::AE_TURBOJET)
4788 {
4789 return ar_aeroengines[index];
4790 }
4791 else
4792 {
4793 return nullptr;
4794 }
4795}
4796
4798{
4799 if (index >= 0 && index < ar_num_aeroengines &&
4800 ar_aeroengines[index]->getType() == AeroEngineType::AE_XPROP)
4801 {
4802 return ar_aeroengines[index];
4803 }
4804 else
4805 {
4806 return nullptr;
4807 }
4808}
4809
4811{
4812 return (index >= 0 && index < ar_num_screwprops) ? ar_screwprops[index] : nullptr;
4813}
4814
4816{
4818 return m_replay_handler;
4819 else
4820 return nullptr;
4821}
4822
4823Ogre::MaterialPtr Actor::getManagedMaterialInstance(const std::string& orig_name)
4824{
4825 auto it = ar_managed_materials.find(orig_name);
4826 if (it != ar_managed_materials.end())
4827 return it->second;
4828 else
4829 return Ogre::MaterialPtr(); // null
4830}
4831
4832std::vector<std::string> Actor::getManagedMaterialNames()
4833{
4834 std::vector<std::string> names;
4835 for (auto it = ar_managed_materials.begin(); it != ar_managed_materials.end(); ++it)
4836 {
4837 names.push_back(it->first);
4838 }
4839 return names;
4840}
4841
4842Vector3 Actor::getNodePosition(int nodeNumber)
4843{
4844 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4845 {
4846 return ar_nodes[nodeNumber].AbsPosition;
4847 }
4848 else
4849 {
4850 return Ogre::Vector3::ZERO;
4851 }
4852}
4853
4854float Actor::getNodeInitialMass(int nodeNumber)
4855{
4856 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4857 {
4858 return ar_initial_node_masses[nodeNumber];
4859 }
4860 else
4861 {
4862 return 0;
4863 }
4864}
4865
4866float Actor::getNodeMass(int nodeNumber)
4867{
4868 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4869 {
4870 return ar_nodes[nodeNumber].mass;
4871 }
4872 else
4873 {
4874 return 0;
4875 }
4876}
4877
4878Vector3 Actor::getNodeVelocity(int nodeNumber)
4879{
4880 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4881 {
4882 return ar_nodes[nodeNumber].Velocity;
4883 }
4884 else
4885 {
4886 return Ogre::Vector3::ZERO;
4887 }
4888}
4889
4890Vector3 Actor::getNodeForces(int nodeNumber)
4891{
4892 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4893 {
4894 return ar_nodes[nodeNumber].Forces;
4895 }
4896 else
4897 {
4898 return Ogre::Vector3::ZERO;
4899 }
4900}
4901
4902void Actor::getNodeMassOptions(int nodeNumber, bool& loaded, bool& overrideMass)
4903{
4904 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4905 {
4906 loaded = ar_nodes[nodeNumber].nd_loaded_mass;
4907 overrideMass = ar_nodes[nodeNumber].nd_override_mass;
4908 }
4909 else
4910 {
4911 loaded = false;
4912 overrideMass = false;
4913 }
4914}
4915
4916bool Actor::isNodeWheelRim(int nodeNumber)
4917{
4918 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4919 {
4920 return ar_nodes[nodeNumber].nd_rim_node;
4921 }
4922 else
4923 {
4924 return false;
4925 }
4926}
4927
4928bool Actor::isNodeWheelTire(int nodeNumber)
4929{
4930 if (nodeNumber >= 0 && nodeNumber < ar_num_nodes)
4931 {
4932 return ar_nodes[nodeNumber].nd_tyre_node;
4933 }
4934 else
4935 {
4936 return false;
4937 }
4938}
4939
4940void Actor::WriteDiagnosticDump(std::string const& fileName)
4941{
4942 // Purpose: to diff against output from https://github.com/only-a-ptr/rigs-of-rods/tree/retro-0407
4943 std::stringstream buf;
4944
4945 buf << "[nodes]" << std::endl;
4946 for (int i = 0; i < ar_num_nodes; i++)
4947 {
4948 buf
4949 << " pos:" << std::setw(3) << ar_nodes[i].pos // indicated pos in node buffer
4950 << ((ar_nodes[i].pos != i) ? " !!sync " : "") // warn if the indicated pos doesn't match
4951 << " (nodes)"
4952 << " id:" << std::setw(3) << ar_nodes_id[i]
4953 << " name:" << std::setw(ar_nodes_name_top_length) << ar_nodes_name[i]
4954 << ", buoyancy:" << std::setw(8) << ar_nodes[i].buoyancy
4955 << ", loaded:" << (int)(ar_nodes[i].nd_loaded_mass)
4956 << " (wheels)"
4957 << " wheel_rim:" << (int)ar_nodes[i].nd_rim_node
4958 << ", wheel_tyre:" << (int)ar_nodes[i].nd_tyre_node
4959 << " (set_node_defaults)"
4960 << " mass:" << std::setw(8) << ar_nodes[i].mass // param 1 load weight
4961 << ", friction_coef:" << std::setw(5) << ar_nodes[i].friction_coef // param 2 friction coef
4962 << ", volume_coef:" << ar_nodes[i].volume_coef // param 3 volume coef
4963 << ", surface_coef:" << ar_nodes[i].surface_coef // param 4 surface coef
4964 << ", overrideMass:" << ar_nodes[i].nd_override_mass // depends on param 1 load weight
4965
4966 // only set by `ActorSpawner::UpdateCollcabContacterNodes()` based on collcabs
4967 // The 'retro-0407' equivalent is `node::contacter` set by `Beam::updateContacterNodes()` based on collcabs!
4968 << " (collcabs)"
4969 << " " << ar_nodes[i].nd_cab_node
4970 << std::endl;
4971 }
4972
4973 buf << "[beams]" << std::endl;
4974 for (int i = 0; i < ar_num_beams; i++)
4975 {
4976 buf
4977 << " " << std::setw(4) << i // actual pos in beam buffer
4978 << ", node1:" << std::setw(3) << ((ar_beams[i].p1) ? ar_nodes_id[ar_beams[i].p1->pos] : -1)
4979 << ", node2:" << std::setw(3) << ((ar_beams[i].p2) ? ar_nodes_id[ar_beams[i].p2->pos] : -1)
4980 << ", refLen:" << std::setw(9) << ar_beams[i].refL
4981 << " (set_beam_defaults/scale)"
4982 << " spring:" << std::setw(8) << ar_beams[i].k //param1 default_spring
4983 << ", damp:" << std::setw(8) << ar_beams[i].d //param2 default_damp
4984 << ", default_deform:" << std::setw(8) << ar_beams[i].default_beam_deform //param3 default_deform
4985 << ", strength:" << std::setw(8) << ar_beams[i].strength //param4 default_break
4986 //param5 default_beam_diameter ~ only visual
4987 //param6 default_beam_material2 ~ only visual
4988 << ", plastic_coef:" << std::setw(8) << ar_beams[i].plastic_coef //param7 default_plastic_coef
4989 << std::endl;
4990 }
4991
4992 // Write out to 'logs' using OGRE resource system - works with Unicode paths on Windows
4993 Ogre::DataStreamPtr outStream = Ogre::ResourceGroupManager::getSingleton().createResource(fileName, RGN_LOGS, /*overwrite=*/true);
4994 std::string text = buf.str();
4995 outStream->write(text.c_str(), text.length());
4996}
4997
4999{
5001 {
5002 bool ev_active = getEventValue(state.event_id);
5003 if (state.eventlock_present)
5004 {
5005 // Toggle-mode
5006 if (ev_active && (ev_active != state.event_active_prev))
5007 {
5008 state.anim_active = !state.anim_active;
5009 }
5010 }
5011 else
5012 {
5013 // Direct-mode
5014 state.anim_active = ev_active;
5015 }
5016 state.event_active_prev = ev_active;
5017 }
5018}
5019
5024
5029
5034
5039
5044
5049
5051{
5053 {
5055 m_working_tuneup_def->name = fmt::format("Tuned {}", ar_filename);
5056 }
5057}
5058
5060{
5061 m_working_tuneup_def = nullptr;
5062}
5063
5064float Actor::getShockSpringRate(int shock_number)
5065{
5066 if (shock_number >= 0 && shock_number < ar_num_shocks)
5067 {
5068 return ar_beams[ar_shocks[shock_number].beamid].debug_k;
5069 }
5070 return -1.f;
5071}
5072
5073float Actor::getShockDamping(int shock_number)
5074{
5075 if (shock_number >= 0 && shock_number < ar_num_shocks)
5076 {
5077 return ar_beams[ar_shocks[shock_number].beamid].debug_d;
5078 }
5079 return -1.f;
5080}
5081
5082float Actor::getShockVelocity(int shock_number)
5083{
5084 if (shock_number >= 0 && shock_number < ar_num_shocks)
5085 {
5086 return ar_beams[ar_shocks[shock_number].beamid].debug_v;
5087 }
5088 return -1.f;
5089}
5090
5091int Actor::getShockNode1(int shock_number)
5092{
5093 if (shock_number >= 0 && shock_number < ar_num_shocks)
5094 {
5095 return ar_beams[ar_shocks[shock_number].beamid].p1->pos;
5096 }
5097 return -1.f;
5098}
5099
5100int Actor::getShockNode2(int shock_number)
5101{
5102 if (shock_number >= 0 && shock_number < ar_num_shocks)
5103 {
5104 return ar_beams[ar_shocks[shock_number].beamid].p2->pos;
5105 }
5106 return -1.f;
5107}
5108
5109bool Actor::shouldSetSimAttribute(ActorSimAttr attr, float val, int index)
5110{
5111 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
5112 {
5114 "Cannot change simulation attributes in multiplayer mode.");
5115 return false;
5116 }
5117
5118 if (index >= 0)
5119 {
5120 LOG(fmt::format("[RoR|Actor] setIndexedSimAttribute: '{}' @ {} = {}", ActorSimAttrToString(attr), index, val));
5122 }
5123 else
5124 {
5125 LOG(fmt::format("[RoR|Actor] setSimAttribute: '{}' = {}", ActorSimAttrToString(attr), val));
5127 }
5128
5129 return true;
5130}
5131
5133{
5134 if (shouldSetSimAttribute(attr, val))
5135 {
5136 // PLEASE maintain the same order as in `enum ActorSimAttr`
5137 switch (attr)
5138 {
5139 // TractionControl
5140 case ACTORSIMATTR_TC_RATIO: tc_ratio = val; return;
5141 case ACTORSIMATTR_TC_PULSE_TIME: tc_pulse_time = val; return;
5143
5144 // Engine
5147 case ACTORSIMATTR_ENGINE_TORQUE: if (ar_engine) { ar_engine->m_engine_torque = val; } return;
5148 case ACTORSIMATTR_ENGINE_DIFF_RATIO: if (ar_engine) { ar_engine->m_diff_ratio = val; } return;
5150
5151 // Engoption
5153 case ACTORSIMATTR_ENGOPTION_ENGINE_TYPE: if (ar_engine) { ar_engine->m_engine_type = (char)val; } return;
5163 case ACTORSIMATTR_ENGOPTION_AIR_INTAKE_NODE: if (ar_engine) { ar_engine->m_air_intake_node = (int)val; } return; // -1 = no air intake node (casted to `NodeNum_t` produces 65535 which is NODENUM_INVALID)
5164
5165 // Engturbo2 (actually 'engturbo' with type=2)
5180
5181 default: return;
5182 }
5183 }
5184}
5185
5186void Actor::setIndexedSimAttribute(ActorSimAttr attr, float val, int index)
5187{
5188 if (index >= 0 && shouldSetSimAttribute(attr, val, index))
5189 {
5190 // PLEASE maintain the same order as in `enum ActorSimAttr`
5191 switch (attr)
5192 {
5193 // Turbojets
5194 case ACTORSIMATTR_TURBOJET_MAX_DRY_THRUST: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_TURBOJET) { dynamic_cast<Turbojet*>(ar_aeroengines[index].GetRef())->setMaxDryThrust(val); } return;
5195 case ACTORSIMATTR_TURBOJET_MAX_WET_THRUST: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_TURBOJET) { dynamic_cast<Turbojet*>(ar_aeroengines[index].GetRef())->setAfterburnThrust(val); } return;
5196
5197 // Propeller engines
5198 case ACTORSIMATTR_PROPENGINE_MAX_POWER: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_XPROP) { dynamic_cast<Turboprop*>(ar_aeroengines[index].GetRef())->setMaxPower(val); } return;
5199
5200 // Screwprops
5201 case ACTORSIMATTR_SCREWPROP_MAX_POWER: if (index < getScrewpropCount()) { ar_screwprops[index]->setMaxPower(val); } return;
5202
5203 default: return;
5204 }
5205 }
5206}
5207
5209{
5210 // PLEASE maintain the same order as in `enum ActorSimAttr`
5211 switch (attr)
5212 {
5213 // TractionControl
5214 case ACTORSIMATTR_TC_RATIO: return tc_ratio;
5217
5218 // Engine
5219 case ACTORSIMATTR_ENGINE_SHIFTDOWN_RPM: if (ar_engine) { return ar_engine->m_engine_min_rpm; } return 0.f;
5220 case ACTORSIMATTR_ENGINE_SHIFTUP_RPM: if (ar_engine) { return ar_engine->m_engine_max_rpm; } return 0.f;
5221 case ACTORSIMATTR_ENGINE_TORQUE: if (ar_engine) { return ar_engine->m_engine_torque; } return 0.f;
5222 case ACTORSIMATTR_ENGINE_DIFF_RATIO: if (ar_engine) { return ar_engine->m_diff_ratio; } return 0.f;
5224
5225 // Engoption
5227 case ACTORSIMATTR_ENGOPTION_ENGINE_TYPE: if (ar_engine) { return (float)ar_engine->m_engine_type; } return 0.f;
5228 case ACTORSIMATTR_ENGOPTION_CLUTCH_FORCE: if (ar_engine) { return ar_engine->m_clutch_force; } return 0.f;
5229 case ACTORSIMATTR_ENGOPTION_SHIFT_TIME: if (ar_engine) { return ar_engine->m_shift_time; } return 0.f;
5230 case ACTORSIMATTR_ENGOPTION_CLUTCH_TIME: if (ar_engine) { return ar_engine->m_clutch_time; } return 0.f;
5233 case ACTORSIMATTR_ENGOPTION_IDLE_RPM: if (ar_engine) { return ar_engine->m_engine_idle_rpm; } return 0.f;
5237 case ACTORSIMATTR_ENGOPTION_AIR_INTAKE_NODE: if (ar_engine) { return (ar_engine->m_air_intake_node == NODENUM_INVALID) ? -1.f : (float)ar_engine->m_air_intake_node; } return 0.f; // -1 = no air intake node (casted to `NodeNum_t` produces 65535 which is NODENUM_INVALID)
5238
5239 // Engturbo2 (actually 'engturbo' with type=2)
5241 case ACTORSIMATTR_ENGTURBO2_NUM_TURBOS: if (ar_engine && ar_engine->m_turbo_ver == 2) { return ar_engine->m_num_turbos; } return 0.f;
5242 case ACTORSIMATTR_ENGTURBO2_MAX_RPM: if (ar_engine && ar_engine->m_turbo_ver == 2) { return ar_engine->m_max_turbo_rpm; } return 0.f;
5244 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;
5254
5255 default: return 0.f;
5256 }
5257}
5258
5260{
5261 if (index >= 0)
5262 {
5263 // PLEASE maintain the same order as in `enum ActorSimAttr`
5264 switch (attr)
5265 {
5266 // Turbojets
5267 case ACTORSIMATTR_TURBOJET_MAX_DRY_THRUST: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_TURBOJET) { return dynamic_cast<Turbojet*>(ar_aeroengines[index].GetRef())->getMaxDryThrust(); }
5268 case ACTORSIMATTR_TURBOJET_MAX_WET_THRUST: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_TURBOJET) { return dynamic_cast<Turbojet*>(ar_aeroengines[index].GetRef())->getAfterburnThrust(); }
5269
5270 // Propeller engines
5271 case ACTORSIMATTR_PROPENGINE_MAX_POWER: if (index < getAircraftEngineCount() && ar_aeroengines[index]->getType() == AeroEngineType::AE_XPROP) { return dynamic_cast<Turboprop*>(ar_aeroengines[index].GetRef())->getMaxPower(); }
5272
5273 // Screwprops
5274 case ACTORSIMATTR_SCREWPROP_MAX_POWER: if (index < getScrewpropCount()) { return ar_screwprops[index]->getMaxPower(); }
5275
5276 }
5277 }
5278
5279 return 0.f;
5280}
5281
5283{
5284 ar_forced_cinecam = cinecam_id;
5286}
5287
5292
5294{
5295 cinecam_id = ar_forced_cinecam;
5298}
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:58
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define RGN_LOGS
Definition Application.h:55
#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:509
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:445
BlinkType getBlinkType()
Definition Actor.cpp:4761
std::pair< float, float > ar_nb_wheels_k_interval
Search interval for springiness & damping of wheel / rim beams.
Definition Actor.h:594
float m_odometer_user
GUI state.
Definition Actor.h:703
std::vector< std::vector< int > > ar_node_to_beam_connections
Definition Actor.h:411
wing_t * ar_wings
Definition Actor.h:395
int ar_num_screwprops
Definition Actor.h:426
int ar_num_wings
Definition Actor.h:396
BitMask_t ar_forced_cinecam_flags
Sim state; flags for forced CineCam supplied by script.
Definition Actor.h:541
std::pair< float, float > ar_nb_shocks_k_interval
Search interval for springiness & damping of shock beams.
Definition Actor.h:592
NodeNum_t ar_custom_camera_node
Sim state; custom tracking node for 3rd-person camera.
Definition Actor.h:538
Ogre::AxisAlignedBox ar_bounding_box
standard bounding box (surrounds all nodes of an actor)
Definition Actor.h:405
int ar_nb_measure_steps
Amount of physics steps to be measured.
Definition Actor.h:584
int m_wheel_node_count
Static attr; filled at spawn.
Definition Actor.h:695
bool shouldSetSimAttribute(ActorSimAttr attr, float val, int index=-1)
Definition Actor.cpp:5109
bool isEventDefined(int eventID)
Definition Actor.cpp:3119
bool getEventBoolValueBounce(int eventID, float time=0.2f)
Definition Actor.cpp:3034
size_t m_net_total_buffer_size
For incoming/outgoing traffic; calculated on spawn.
Definition Actor.h:730
EnginePtr ar_engine
Definition Actor.h:467
float ar_collision_range
Physics attr.
Definition Actor.h:526
bool m_antilockbrake
GUI state.
Definition Actor.h:707
std::vector< Ogre::AxisAlignedBox > ar_predicted_coll_bounding_boxes
Definition Actor.h:413
std::vector< float > ar_minimass
minimum node mass in Kg - can be scaled in-game via NBUtil
Definition Actor.h:372
TransferCase * m_transfer_case
Physics.
Definition Actor.h:694
float ar_guisettings_speedo_max_kph
Definition Actor.h:555
float ar_dry_mass
User-defined (editable via NBUtil); from 'globals' arg#1 - default for all nodes.
Definition Actor.h:355
int ar_airbrake_intensity
Physics state; values 0-5.
Definition Actor.h:520
void toggleTransferCaseGearRatio()
Definition Actor.cpp:1522
bool m_blinker_left_lit
Blinking state of left turn signal.
Definition Actor.h:744
float ar_hydro_elevator_state
Definition Actor.h:506
std::pair< float, float > ar_nb_beams_scale
Scales for springiness & damping of regular beams.
Definition Actor.h:586
bool ar_forward_commands
Sim state.
Definition Actor.h:605
void toggleSlideNodeLock()
void engineTriggerHelper(int engineNumber, EngineTriggerType type, float triggerValue)
Definition Actor.cpp:4551
void resetSlideNodes()
Reset all the SlideNodes.
CacheEntryPtr & getUsedSkinEntry()
Definition Actor.cpp:5030
void antilockbrakeToggle()
Definition Actor.cpp:3967
bool m_ongoing_reset
Hack to prevent position/rotation creep during interactive truck reset (aka LiveRepair).
Definition Actor.h:569
void calcNodeConnectivityGraph()
Definition Actor.cpp:907
float m_mouse_grab_move_force
Definition Actor.h:681
void sendStreamData()
Send outgoing data.
Definition Actor.cpp:2026
float ar_wheel_speed
Physics state; wheel speed in m/s.
Definition Actor.h:495
bool hasEventSimulatedValue(int eventID)
Definition Actor.cpp:3053
void updateSkidmarks()
Creates or updates skidmarks.
Definition Actor.cpp:3135
collcab_rate_t ar_intra_collcabrate[MAX_CABS]
Definition Actor.h:433
bool m_net_initialized
Definition Actor.h:750
std::vector< float > ar_nodes_override_loadweights
'nodes': 'l' flag and number.
Definition Actor.h:369
CacheEntryPtrVec & getUsedAddonpartEntries()
Definition Actor.cpp:5035
Ogre::AxisAlignedBox ar_evboxes_bounding_box
bounding box around nodes eligible for eventbox triggering
Definition Actor.h:406
int GetNumActiveConnectedBeams(int nodeid)
Returns the number of active (non bounded) beams connected to a node.
Definition Actor.cpp:4049
std::vector< ropable_t > ar_ropables
Definition Actor.h:399
CmdKeyArray ar_command_key
BEWARE: commandkeys are indexed 1-MAX_COMMANDS!
Definition Actor.h:404
wheel_t ar_wheels[MAX_WHEELS]
Definition Actor.h:419
std::vector< float > ar_nb_reference
Temporary storage of the reference search result.
Definition Actor.h:582
std::vector< rope_t > ar_ropes
Definition Actor.h:398
void hookToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::HOOK_TOGGLE, NodeNum_t mousenode=NODENUM_INVALID, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID)
Definition Actor.cpp:3813
NodeNum_t m_mouse_grab_node
Sim state; node currently being dragged by user.
Definition Actor.h:679
DashBoardManagerPtr ar_dashboard
Gfx state; Dashboard manager (HUD element manager)
Definition Actor.h:563
float m_odometer_total
GUI state.
Definition Actor.h:702
float getSimAttribute(ActorSimAttr attr)
Definition Actor.cpp:5208
void clearEventSimulatedValues()
Definition Actor.cpp:3048
int getScrewpropCount()
Definition Actor.h:288
void prepareInside(bool inside)
Prepares vehicle for in-cabin camera use.
Definition Actor.cpp:3159
void displayTransferCaseMode()
Gets the current transfer case mode name (4WD Hi, ...)
Definition Actor.cpp:1477
bool getEventBoolValue(int eventID)
Definition Actor.cpp:3029
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:546
bool getCustomParticleMode()
Definition Actor.cpp:4769
Ogre::Vector3 getNodeVelocity(int nodeNumber)
Definition Actor.cpp:4878
void ropeToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::ROPE_TOGGLE, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID)
Definition Actor.cpp:3736
node_t * ar_nodes
Definition Actor.h:365
Ogre::Vector3 m_camera_gforces
Physics state (global)
Definition Actor.h:685
void calcNetwork()
Definition Actor.cpp:471
int ar_aerial_flap
Sim state; state of aircraft flaps (values: 0-5)
Definition Actor.h:516
void beaconsToggle()
Definition Actor.cpp:3985
void dispose()
Effectively destroys the object but keeps it in memory to satisfy shared pointers.
Definition Actor.cpp:89
void resetEventSimulatedValue(int eventID)
Definition Actor.cpp:3089
void updateVisual(float dt=0)
Definition Actor.cpp:3421
GfxActor * GetGfxActor()
Definition Actor.h:344
bool isNodeWheelRim(int nodeNumber)
Is node marked as wheel rim? Note some wheel models use only tire nodes. See https://docs....
Definition Actor.cpp:4916
Ogre::AxisAlignedBox ar_predicted_bounding_box
Definition Actor.h:407
void setNodeMass(int nodeNumber, float m)
Definition Actor.cpp:4674
void calculateLocalGForces()
Derive the truck local g-forces from the global ones.
Definition Actor.cpp:4529
int m_num_wheel_diffs
Physics attr.
Definition Actor.h:693
void updateFlareStates(float dt)
Definition Actor.cpp:3228
float getRotation()
Definition Actor.cpp:355
int m_num_proped_wheels
Physics attr, filled at spawn - Number of propelled wheels.
Definition Actor.h:667
Actor(ActorInstanceID_t actor_id, unsigned int vector_index, RigDef::DocumentPtr def, ActorSpawnRequest rq)
Definition Actor.cpp:4584
bool isTied()
Definition Actor.cpp:4060
std::vector< Ogre::SceneNode * > m_deletion_scene_nodes
For unloading vehicle; filled at spawn.
Definition Actor.h:665
Ogre::Vector3 m_camera_local_gforces_max
Physics state (camera local)
Definition Actor.h:687
std::vector< tie_t > ar_ties
Definition Actor.h:400
void NotifyActorCameraChanged()
Logic: sound, display; Notify this vehicle that camera changed;.
Definition Actor.cpp:4029
float ar_hydro_rudder_state
Definition Actor.h:504
float ar_sleep_counter
Sim state; idle time counter.
Definition Actor.h:507
std::pair< float, float > ar_nb_wheels_scale
Scales for springiness & damping of wheel / rim beams.
Definition Actor.h:588
bool m_water_contact_old
Scripting state.
Definition Actor.h:752
void toggleHeadlights()
Definition Actor.cpp:3198
void sendToSleep()
Definition Actor.cpp:3007
void mouseMove(NodeNum_t node, Ogre::Vector3 pos, float force)
Definition Actor.cpp:1400
float getEventBounceTime(int eventID)
Definition Actor.cpp:3124
int m_anglesnap_request
Accumulator.
Definition Actor.h:576
int m_num_axle_diffs
Physics attr.
Definition Actor.h:691
float m_stabilizer_shock_ratio
Physics state.
Definition Actor.h:688
float ar_posnode_spawn_height
Definition Actor.h:484
CineCameraID_t ar_current_cinecam
Sim state; index of current CineCam (CINECAMERAID_INVALID if using 3rd-person camera)
Definition Actor.h:537
ground_model_t * ar_submesh_ground_model
Definition Actor.h:508
void updateDashBoards(float dt)
Definition Actor.cpp:4076
Replay * getReplay()
Definition Actor.cpp:4815
float tc_wheelslip_constant
use ACTORSIMATTR_TC_WHEELSLIP_CONSTANT
Definition Actor.h:551
void DisjoinInterActorBeams()
Helper for MSG_ handlers, do not invoke by hand.
Definition Actor.cpp:3594
void importLightStateMask(BitMask_t lightmask)
Only for linked (locked/tied) actors forwarding flare states; see 'flaregroups_no_import' in ....
Definition Actor.cpp:3383
bool m_slidenodes_locked
Physics state; Are SlideNodes locked?
Definition Actor.h:749
void clearForcedCinecam()
Definition Actor.cpp:5288
int * ar_nodes_id
Number in truck file, -1 for nodes generated by wheels/cinecam.
Definition Actor.h:366
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:385
Differential * m_axle_diffs[1+MAX_WHEELS/2]
Physics.
Definition Actor.h:690
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:425
void removeWorkingTuneupDef()
Deletes the working tuneup def object if it exists.
Definition Actor.cpp:5059
float m_spawn_rotation
Definition Actor.h:682
void tieToggle(int group=-1, ActorLinkingRequestType mode=ActorLinkingRequestType::TIE_TOGGLE, ActorInstanceID_t forceunlock_filter=ACTORINSTANCEID_INVALID)
Definition Actor.cpp:3619
Replay * m_replay_handler
Definition Actor.h:678
void setIndexedSimAttribute(ActorSimAttr attr, float val, int index)
Definition Actor.cpp:5186
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:476
bool ar_nb_initialized
Definition Actor.h:580
Ogre::Vector3 getRotationCenter()
Definition Actor.cpp:1550
std::vector< Airbrake * > ar_airbrakes
Definition Actor.h:403
void ResetAngle(float rot)
Definition Actor.cpp:1263
bool ar_physics_paused
Actor physics individually paused by user.
Definition Actor.h:570
AeroEnginePtr getTurbojet(int index)
Definition Actor.cpp:4784
int getShockNode2(int shock_number)
Definition Actor.cpp:5100
void CalcCabCollisions()
Definition Actor.cpp:2553
Ogre::Timer ar_net_timer
Definition Actor.h:524
void toggleBlinkType(BlinkType blink)
Definition Actor.cpp:3289
float getShockSpringRate(int shock_number)
Definition Actor.cpp:5064
Ogre::Vector3 getPosition()
Definition Actor.cpp:370
bool m_tractioncontrol
GUI state.
Definition Actor.h:708
void toggleTransferCaseMode()
Definition Actor.cpp:1491
void ForceFeedbackStep(int steps)
Definition Actor.cpp:1837
float ar_hydro_dir_command
Definition Actor.h:498
Ogre::Vector3 getNodeForces(int nodeNumber)
Definition Actor.cpp:4890
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:581
Airfoil * m_fusealge_airfoil
Physics attr; defined in truckfile.
Definition Actor.h:698
float ar_elevator
Sim state; aircraft elevator.
Definition Actor.h:513
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:412
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:4654
soundsource_t ar_soundsources[MAX_SOUNDSCRIPTS_PER_TRUCK]
Definition Actor.h:421
std::vector< Ogre::Vector3 > ar_initial_node_positions
Absolute world positions, for resetting to pristine state.
Definition Actor.h:375
void ensureWorkingTuneupDef()
Creates a working tuneup def if it doesn't exist yet.
Definition Actor.cpp:5050
std::pair< float, float > ar_nb_wheels_d_interval
Search interval for springiness & damping of wheel / rim beams.
Definition Actor.h:593
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:521
float ar_wheel_spin
Physics state; wheel speed in radians/s.
Definition Actor.h:496
void recalculateNodeMasses()
Definition Actor.cpp:701
std::vector< flare_t > ar_flares
Definition Actor.h:402
std::pair< float, float > ar_nb_beams_d_interval
Search interval for springiness & damping of regular beams.
Definition Actor.h:589
bool tc_mode
Enabled?
Definition Actor.h:545
std::vector< Ogre::Entity * > m_deletion_entities
For unloading vehicle; filled at spawn.
Definition Actor.h:664
std::unique_ptr< Buoyance > m_buoyance
Definition Actor.h:531
bool alb_nodash
Anti-lock brake attribute: Hide the dashboard indicator?
Definition Actor.h:448
AeroEnginePtr getAircraftEngine(int index)
Definition Actor.cpp:4779
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:4750
std::vector< std::string > getManagedMaterialNames()
Definition Actor.cpp:4832
unsigned long ar_net_last_update_time
Definition Actor.h:525
ExtCameraMode ar_extern_camera_mode
Definition Actor.h:460
float ar_load_mass
User-defined (editable via NBUtil); from 'globals' arg#2 - only applies to nodes with 'l' flag.
Definition Actor.h:357
int ar_masscount
Calculated; Number of nodes loaded with l option.
Definition Actor.h:359
float getHeightAboveGround(bool skip_virtual_nodes=true)
Definition Actor.cpp:1594
void updateSoundSources()
Definition Actor.cpp:3404
bool m_blinker_right_lit
Blinking state of right turn signal.
Definition Actor.h:745
PointColDetector * m_inter_point_col_detector
Physics.
Definition Actor.h:670
float ar_hydro_elevator_command
Definition Actor.h:505
BitMask_t m_flaregroups_no_import
RoRnet::Lightmask.
Definition Actor.h:742
int m_stabilizer_shock_request
Physics state; values: { -1, 0, 1 }.
Definition Actor.h:689
void setSimAttribute(ActorSimAttr attr, float val)
HAZARDOUS - values may not be checked; Pay attention to 'safe values' at each attribute description.
Definition Actor.cpp:5132
std::vector< RailGroup * > m_railgroups
all the available RailGroups for this actor
Definition Actor.h:663
float ar_hydro_dir_state
Definition Actor.h:499
NodeNum_t ar_extern_camera_node
Definition Actor.h:461
bool ar_engine_hydraulics_ready
Sim state; does engine have enough RPM to power hydraulics?
Definition Actor.h:599
Ogre::Vector3 ar_origin
Physics state; base position for softbody nodes.
Definition Actor.h:473
int getInstanceId()
Definition Actor.h:306
ActorState ar_state
Definition Actor.h:567
bool getCustomLightVisible(int number)
Definition Actor.cpp:4691
std::unique_ptr< GfxActor > m_gfx_actor
Definition Actor.h:661
void RemoveInterActorBeam(beam_t *beam, ActorLinkingRequestType type)
Do not call directly - use MSG_SIM_ACTOR_LINKING_REQUESTED
Definition Actor.cpp:3541
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
float getEventValue(int eventID, bool pure=false, InputSourceType valueSource=InputSourceType::IST_ANY)
Definition Actor.cpp:3015
Ogre::Vector3 m_translation_request
Accumulator.
Definition Actor.h:577
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:423
float ar_hydro_aileron_command
Definition Actor.h:501
int ar_num_shocks
Number of shock absorbers.
Definition Actor.h:391
void setNodeMassOptions(int nodeNumber, bool loaded, bool overrideMass)
Definition Actor.cpp:4682
float m_avionic_chatter_timer
Sound fx state (some pseudo random number, doesn't matter)
Definition Actor.h:669
void forceAllFlaresOff()
Definition Actor.cpp:3220
float ar_total_mass
Calculated; total mass in Kg.
Definition Actor.h:360
void setLightStateMask(BitMask_t lightmask)
Does all the necessary toggling.
Definition Actor.cpp:3360
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:4842
float ar_hydro_aileron_state
Definition Actor.h:502
std::map< int, float > ar_actor_event_simulated_values
Definition Actor.h:491
int ar_num_aeroengines
Definition Actor.h:424
float ar_anim_shift_timer
For 'animator' with flag 'shifter'.
Definition Actor.h:451
void setForcedCinecam(CineCameraID_t cinecam_id, BitMask_t flags)
Definition Actor.cpp:5282
void WriteDiagnosticDump(std::string const &filename)
Definition Actor.cpp:4940
void SoftReset()
Definition Actor.cpp:1633
void wakeUp()
Definition Actor.cpp:2998
Ogre::MaterialPtr getManagedMaterialInstance(const std::string &orig_name)
Definition Actor.cpp:4823
std::map< std::string, Ogre::MaterialPtr > ar_managed_materials
Definition Actor.h:414
void searchBeamDefaults()
Searches for more stable beam defaults.
Definition Actor.cpp:1858
int ar_num_beams
Definition Actor.h:384
float getShockVelocity(int shock_number)
Definition Actor.cpp:5082
void getNodeMassOptions(int nodeNumber, bool &loaded, bool &overrideMass)
Definition Actor.cpp:4902
std::vector< authorinfo_t > authors
Definition Actor.h:397
float getSteeringAngle()
Definition Actor.cpp:4649
int countCustomLights(int control_number)
Definition Actor.cpp:4733
std::vector< std::string > getDescription()
Definition Actor.cpp:4659
void UpdatePropAnimInputEvents()
Definition Actor.cpp:4998
void toggleAxleDiffMode()
Definition Actor.cpp:1415
bool isEventAnalog(int eventID)
Definition Actor.cpp:3109
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:464
void autoBlinkReset()
Resets the turn signal when the steering wheel is turned back.
Definition Actor.cpp:3331
void muteAllSounds()
Definition Actor.cpp:4001
void setCustomLightVisible(int number, bool visible)
Definition Actor.cpp:4713
float ar_rudder
Sim state; aircraft rudder (boat rudders are controlled in RoR::Screwprop)
Definition Actor.h:514
ActorPtrVec ar_linked_actors
BEWARE: Includes indirect links, see DetermineLinkedActors(); Other actors linked using 'hooks/ties/r...
Definition Actor.h:568
bool alb_notoggle
Anti-lock brake attribute: Disable in-game toggle?
Definition Actor.h:449
void SyncReset(bool reset_position)
this one should be called only synchronously (without physics running in background)
Definition Actor.cpp:1657
int getAircraftEngineCount()
Definition Actor.h:283
float ar_avg_wheel_speed
Physics state; avg wheel speed in m/s.
Definition Actor.h:497
std::vector< PropAnimKeyState > m_prop_anim_key_states
Definition Actor.h:712
rotator_t * ar_rotators
Definition Actor.h:393
Ogre::Real ar_brake
Physics state; braking intensity.
Definition Actor.h:494
bool ar_minimass_skip_loaded_nodes
Definition Actor.h:378
std::string ar_filename
Attribute; filled at spawn.
Definition Actor.h:518
std::string * ar_nodes_name
Name in truck file, only if defined with 'nodes2'.
Definition Actor.h:367
bool ar_cparticles_active
Gfx state.
Definition Actor.h:609
bool ShouldAllowNonSimulatedInputs()
Definition Actor.cpp:3098
void CalcShocks2(int i, Ogre::Real difftoBeamL, Ogre::Real &k, Ogre::Real &d, Ogre::Real v)
Definition Actor.cpp:2574
void setEventSimulatedValue(int eventID, float value)
Definition Actor.cpp:3073
Ogre::Real m_min_camera_radius
Definition Actor.h:674
virtual ~Actor() override
Definition Actor.cpp:82
int ar_num_cinecams
Sim attr;.
Definition Actor.h:469
bool getBeaconMode() const
Definition Actor.h:240
CacheEntryPtr m_used_actor_entry
Required.
Definition Actor.h:719
GfxFlaresMode m_flares_mode
Snapshot of cvar 'gfx_flares_mode' on spawn.
Definition Actor.h:740
std::string m_section_config
Classic 'sectionconfig' in truck file.
Definition Actor.h:716
bool cc_mode
Cruise Control.
Definition Actor.h:452
bool isNodeWheelTire(int nodeNumber)
Is node marked as wheel tire? Note some wheel models use only tire nodes. See https://docs....
Definition Actor.cpp:4928
void CalcAnimators(hydrobeam_t const &hydrobeam, float &cstate, int &div)
Definition Actor.cpp:2164
float getEventSimulatedValue(int eventID)
Definition Actor.cpp:3061
Differential * m_wheel_diffs[MAX_WHEELS/2]
Physics.
Definition Actor.h:692
void unmuteAllSounds()
Definition Actor.cpp:4015
std::deque< NetUpdate > m_net_updates
Incoming stream of NetUpdates.
Definition Actor.h:784
int ar_cabs[MAX_CABS *3]
Definition Actor.h:427
void parkingbrakeToggle()
Definition Actor.cpp:3952
Ogre::Real getMinimalCameraRadius()
Definition Actor.cpp:4774
bool m_water_contact
Scripting state.
Definition Actor.h:751
int ar_num_collcabs
Definition Actor.h:434
float ar_hydro_rudder_command
Definition Actor.h:503
int ar_num_nodes
Definition Actor.h:380
Skidmark * m_skid_trails[MAX_WHEELS *2]
Definition Actor.h:706
std::vector< beam_t * > ar_inter_beams
Beams connecting 2 actors.
Definition Actor.h:389
void CalcForcesEulerCompute(bool doUpdate, int num_steps)
Ogre::Quaternion ar_main_camera_dir_corr
Sim attr;.
Definition Actor.h:475
Ogre::Vector3 m_mouse_grab_pos
Definition Actor.h:680
std::pair< float, float > ar_nb_shocks_scale
Scales for springiness & damping of shock beams.
Definition Actor.h:587
void setBlinkType(BlinkType blink)
Definition Actor.cpp:3297
std::vector< std::string > m_description
Definition Actor.h:711
CineCameraID_t ar_forced_cinecam
Sim state; index of CineCam forced by script (CINECAMERAID_INVALID if not forced)
Definition Actor.h:540
bool m_blinker_autoreset
When true, we're steering and blinker will turn off automatically.
Definition Actor.h:743
VehicleAIPtr ar_vehicle_ai
Definition Actor.h:485
BitMask_t m_lightmask
RoRnet::Lightmask.
Definition Actor.h:741
float getTotalMass(bool withLocked=true)
Definition Actor.cpp:843
int getShockNode1(int shock_number)
Definition Actor.cpp:5091
float getShockDamping(int shock_number)
Definition Actor.cpp:5073
void scaleTruck(float value)
Definition Actor.cpp:315
float ar_anim_previous_crank
For 'animator' with flag 'torque'.
Definition Actor.h:442
void CalcTriggers(int i, Ogre::Real difftoBeamL, bool update_hooks)
Definition Actor.cpp:2717
std::string getTruckFileResourceGroup()
Definition Actor.cpp:5020
Ogre::Vector3 ar_fusedrag
Physics state.
Definition Actor.h:517
bool tc_notoggle
Disable in-game toggle?
Definition Actor.h:549
float ar_aileron
Sim state; aircraft aileron.
Definition Actor.h:515
void HandleAngelScriptEvents(float dt)
Definition Actor.cpp:1846
AutopilotPtr ar_autopilot
Definition Actor.h:470
float ar_scale
Physics state; scale of the actor (nominal = 1.0)
Definition Actor.h:493
float tc_ratio
Regulating force.
Definition Actor.h:544
int ar_num_soundsources
Definition Actor.h:422
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:430
int ar_nodes_name_top_length
For nicely formatted diagnostic output.
Definition Actor.h:379
Ogre::Vector3 GetCameraDir()
Definition Actor.h:341
std::vector< std::vector< int > > ar_node_to_node_connections
Definition Actor.h:410
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:591
Ogre::Quaternion getOrientation()
Definition Actor.cpp:375
std::vector< hook_t > ar_hooks
Definition Actor.h:401
void UpdateBoundingBoxes()
Definition Actor.cpp:1201
void tractioncontrolToggle()
Definition Actor.cpp:3976
PointColDetector * m_intra_point_col_detector
Physics.
Definition Actor.h:671
int ar_num_wheels
Definition Actor.h:420
void setMass(float m)
Definition Actor.cpp:4664
float getNodeMass(int nodeNumber)
Definition Actor.cpp:4866
void AddInterActorBeam(beam_t *beam, ActorPtr other, ActorLinkingRequestType type)
Do not call directly - use MSG_SIM_ACTOR_LINKING_REQUESTED
Definition Actor.cpp:3500
CacheEntryPtr m_used_skin_entry
Optional, only graphics.
Definition Actor.h:720
int m_previous_gear
Sim state; land vehicle shifting.
Definition Actor.h:696
std::pair< float, float > ar_nb_beams_k_interval
Search interval for springiness & damping of regular beams.
Definition Actor.h:590
ScrewpropPtr getScrewprop(int index)
Definition Actor.cpp:4810
float m_rotation_request
Accumulator.
Definition Actor.h:575
shock_t * ar_shocks
Shock absorbers.
Definition Actor.h:390
int m_net_first_wheel_node
Network attr; Determines data buffer layout; calculated on spawn.
Definition Actor.h:732
Ogre::Vector3 m_camera_local_gforces_cur
Physics state (camera local)
Definition Actor.h:686
CacheEntryPtr & getUsedActorEntry()
The actor entry itself.
Definition Actor.cpp:5025
Ogre::Vector3 GetCameraRoll()
Definition Actor.h:342
bool CalcForcesEulerPrepare(bool doUpdate)
bool tc_nodash
Hide the dashboard indicator?
Definition Actor.h:548
NodeNum_t ar_cinecam_node[MAX_CAMERAS]
Sim attr; Cine-camera node indexes.
Definition Actor.h:468
Ogre::Vector3 * ar_nodes_spawn_offsets
Relative positions (incl. Tuning system tweaks) from the definition file, for spawn-like resetting (i...
Definition Actor.h:370
Ogre::Vector3 m_avg_node_position
average node position
Definition Actor.h:673
bool Intersects(ActorPtr actor, Ogre::Vector3 offset=Ogre::Vector3::ZERO)
Slow intersection test.
Definition Actor.cpp:926
void toggleCustomParticles()
Definition Actor.cpp:3393
CacheEntryPtrVec m_used_addonpart_entries
Optional, assigned by player via Tuning menu (.tuneup files).
Definition Actor.h:721
bool m_hud_features_ok
Gfx state; Are HUD features matching actor's capabilities?
Definition Actor.h:562
Ogre::Real ar_hydro_dir_wheel_display
Definition Actor.h:500
size_t m_net_node_buf_size
For incoming/outgoing traffic; calculated on spawn.
Definition Actor.h:727
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:757
beam_t * ar_beams
Definition Actor.h:383
void DetermineLinkedActors()
Definition Actor.cpp:861
bool ar_trailer_parking_brake
Definition Actor.h:510
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:722
Ogre::Timer m_reset_timer
Definition Actor.h:683
int ar_collcabs[MAX_CABS]
Definition Actor.h:431
void setAircraftFlaps(int flapsLevel)
Definition Actor.cpp:2988
float getNodeInitialMass(int nodeNumber)
Definition Actor.cpp:4854
int ar_nb_skip_steps
Amount of physics steps to be skipped before measuring.
Definition Actor.h:583
bool ar_has_active_shocks
Are there active stabilizer shocks?
Definition Actor.h:392
CacheEntryPtrVec & getUsedAssetpackEntries()
Definition Actor.cpp:5040
TuneupDefPtr m_working_tuneup_def
Each actor gets unique instance, even if loaded from .tuneup file in modcache.
Definition Actor.h:717
std::vector< float > ar_initial_node_masses
Definition Actor.h:374
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:5045
bool getForcedCinecam(CineCameraID_t &cinecam_id, BitMask_t &flags)
Definition Actor.cpp:5293
bool isLocked()
Are hooks locked?
Definition Actor.cpp:4068
int ar_num_rotators
Definition Actor.h:394
ActorType ar_driveable
Sim attr; marks vehicle type and features.
Definition Actor.h:466
int ar_net_stream_id
Definition Actor.h:522
void setLoadedMass(float m)
Definition Actor.cpp:4669
AeroEnginePtr getTurboprop(int index)
Definition Actor.cpp:4797
float getIndexedSimAttribute(ActorSimAttr attr, int index)
Definition Actor.cpp:5259
bool getHeadlightsVisible() const
Definition Actor.h:253
Ogre::Vector3 m_rotation_request_center
Definition Actor.h:574
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
virtual AeroEngineType getType()=0
float getRudder()
float getAilerons()
Definition AutoPilot.cpp:97
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
CameraBehaviors GetCurrentBehavior() const
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)
void setFloat(int key, float val)
void setInt(int key, int val)
void setEnabled(int key, bool val)
void setChar(int key, const char *val)
void setBool(int key, bool 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:979
int m_turbo_ver
Definition Engine.h:259
void setWheelSpin(float rpm)
Definition Engine.cpp:943
float getRPM()
Definition Engine.h:95
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:895
float m_antilag_power_factor
Definition Engine.h:277
char m_engine_type
't' = truck (default), 'c' = car ('engoption' attr #2)
Definition Engine.h:209
bool m_turbo_has_antilag
Definition Engine.h:274
bool m_turbo_has_bov
Definition Engine.h:266
SimGearboxMode getAutoMode()
Definition Engine.cpp:852
void startEngine()
Quick engine start. Plays sounds.
Definition Engine.cpp:1005
float getClutchForce() const
('engoption' attr #3)
Definition Engine.h:74
float m_diff_ratio
Global gear ratio ('engine' attr #4)
Definition Engine.h:214
NodeNum_t m_air_intake_node
Node number of the air intake node (if any) - defaults to cinecam ref node (classic behavior) but can...
Definition Engine.h:228
int m_max_turbo_rpm
Definition Engine.h:263
float m_engine_inertia
('engoption' attr #1)
Definition Engine.h:220
float m_antilag_rand_chance
Definition Engine.h:276
float m_min_wastegate_psi
Definition Engine.h:270
float m_engine_idle_rpm
('engoption' attr #8)
Definition Engine.h:223
bool m_turbo_has_wastegate
Definition Engine.h:269
float m_max_idle_mixture
Maximum throttle to maintain the idle RPM ('engoption' attr #9)
Definition Engine.h:219
float getClutch() const
Definition Engine.h:93
float getTorque()
Definition Engine.cpp:923
void setTCaseRatio(float ratio)
Set current transfer case gear (reduction) ratio.
Definition Engine.cpp:948
int getNumGears() const
Definition Engine.h:63
float m_turbo_wg_threshold_n
Definition Engine.h:273
float m_braking_torque
Engine attribute.
Definition Engine.h:210
float m_turbo_wg_threshold_p
Definition Engine.h:272
float m_turbo_engine_rpm_operation
Definition Engine.h:265
int m_min_bov_psi
Definition Engine.h:268
float m_shift_time
Time (in seconds) that it takes to shift ('engoption' attr #4)
Definition Engine.h:237
float getShiftUpRPM() const
Shift up RPM ('engine' attr #2)
Definition Engine.h:59
float m_antilag_min_rpm
Definition Engine.h:275
float m_clutch_time
Time (in seconds) the clutch takes to apply ('engoption' attr #5)
Definition Engine.h:199
float m_min_idle_mixture
Minimum throttle to maintain the idle RPM ('engoption' attr #10)
Definition Engine.h:218
int getAutoShift()
Definition Engine.cpp:1191
float m_clutch_force
('engoption' attr #3)
Definition Engine.h:198
int getGear()
Definition Engine.cpp:1048
void shift(int val)
Changes gear by a relative offset. Plays sounds.
Definition Engine.cpp:1094
void setClutch(float clutch)
Definition Engine.h:134
float m_engine_min_rpm
Shift down RPM ('engine' attr #1)
Definition Engine.h:222
float m_post_shift_time
Time (in seconds) until full torque is transferred ('engoption' attr #6)
Definition Engine.h:235
float m_engine_max_rpm
Shift up RPM ('engine' attr #2)
Definition Engine.h:221
float m_engine_stall_rpm
('engoption' attr #7)
Definition Engine.h:224
void setAcc(float val)
Definition Engine.cpp:862
float getAcc()
Definition Engine.cpp:890
bool hasTurbo() const
Definition Engine.h:73
float m_turbo_inertia_factor
Definition Engine.h:261
float m_engine_torque
Torque in N/m ('engine' attr #3)
Definition Engine.h:216
int m_num_turbos
Definition Engine.h:262
bool isRunning()
Definition Engine.h:102
float getTurboPSI()
Definition Engine.cpp:867
bool hasContact()
Ignition.
Definition Engine.h:103
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:142
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
float getEventBounceTime(int eventID)
bool isEventDefined(int eventID)
bool getEventBoolValueBounce(int eventID, float time=0.2f)
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
bool isEventAnalog(int eventID)
void AddLocalStream(RoRnet::StreamRegister *reg, int size)
Definition Network.cpp:671
void AddPacket(int streamid, int type, int len, const char *content)
Definition Network.cpp:617
bool GetUserInfo(int uid, RoRnet::UserInfo &result)
Definition Network.cpp:726
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 getMaxPower()
Definition ScrewProp.h:48
float getRudder()
float getThrottle()
void setMaxPower(float power)
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:511
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
InputSourceType
Definition InputEngine.h:67
@ 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() or setIndexedSimAttribute() is called; additional args: #2 RoR::Acto...
@ 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_GUISETTING_SPEEDO_KPH
(float) Like DD_ENGINE_SPEEDO_KPH, but if 'speedoTexture' and 'speedoMax' in 'guisettings' are set,...
@ DD_ENGINE_SPEEDO_KPH
@ DD_ENGINE_AUTOGEAR_STRING
string like "<current gear>/<max gear>"
@ DD_GUISETTING_TACHO_RPM
(float) Like DD_ENGINE_RPM, but if 'tachoTexture' and 'useMaxRPM' in 'guisettings' is set,...
@ DD_ENGINE_OFF
@ DD_PARKINGBRAKE
chassis pitch
@ DD_ENGINE_RUNNING
@ 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_CRUISECONTROL_ACTIVE
ties locked
@ 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
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::setIndexedSimAttribute() (and their get counterparts...
Definition SimData.h:925
@ ACTORSIMATTR_ENGOPTION_MIN_IDLE_MIXTURE
Min throttle to maintain idle RPM - Param #10 of 'engoption'.
Definition SimData.h:952
@ ACTORSIMATTR_ENGTURBO2_WASTEGATE_THRESHOLD_N
1 - WgThreshold ~ calculated from Param #10 of 'engturbo2'
Definition SimData.h:965
@ ACTORSIMATTR_ENGOPTION_AIR_INTAKE_NODE
Node number representing air intake - Prototype only; Not yet recognized by 'engoption'.
Definition SimData.h:954
@ ACTORSIMATTR_ENGOPTION_BRAKING_TORQUE
How much engine brakes on zero throttle - Param #11 of 'engoption'.
Definition SimData.h:953
@ ACTORSIMATTR_ENGINE_TORQUE
Engine torque in newton-meters (N/m) - Param #3 of 'engine'.
Definition SimData.h:938
@ ACTORSIMATTR_TURBOJET_MAX_WET_THRUST
Param #6 of 'turbojets': Maximum thrust with afterburner, in kilonewtons (indexed)
Definition SimData.h:974
@ ACTORSIMATTR_ENGOPTION_CLUTCH_FORCE
Param #3 of 'engoption'.
Definition SimData.h:945
@ ACTORSIMATTR_ENGTURBO2_ANTILAG_CHANCE
Param #12 of 'engturbo2'.
Definition SimData.h:968
@ ACTORSIMATTR_ENGTURBO2_WASTEGATE_THRESHOLD_P
1 + WgThreshold ~ calculated from Param #10 of 'engturbo2'
Definition SimData.h:966
@ ACTORSIMATTR_ENGOPTION_CLUTCH_TIME
Param #5 of 'engoption'.
Definition SimData.h:947
@ ACTORSIMATTR_ENGTURBO2_WASTEGATE_MAX_PSI
Param #9 of 'engturbo2'.
Definition SimData.h:964
@ ACTORSIMATTR_ENGOPTION_ENGINE_TYPE
Param #2 of 'engoption'.
Definition SimData.h:944
@ ACTORSIMATTR_TURBOJET_MAX_DRY_THRUST
Param #5 of 'turbojets': Maximum thrust without afterburner, in kilonewtons (indexed)
Definition SimData.h:973
@ ACTORSIMATTR_ENGTURBO2_WASTEGATE_ENABLED
Param #8 of 'engturbo2'.
Definition SimData.h:963
@ ACTORSIMATTR_ENGTURBO2_ANTILAG_POWER
Param #14 of 'engturbo2'.
Definition SimData.h:970
@ ACTORSIMATTR_ENGTURBO2_INERTIA_FACTOR
Time to spool up - Param #2 of 'engturbo2'.
Definition SimData.h:957
@ ACTORSIMATTR_ENGOPTION_POST_SHIFT_TIME
Time (in seconds) until full torque is transferred - Param #6 of 'engoption'.
Definition SimData.h:948
@ ACTORSIMATTR_ENGTURBO2_BOV_MIN_PSI
Blow-off valve PSI threshold - Param #7 of 'engturbo2'.
Definition SimData.h:962
@ ACTORSIMATTR_ENGINE_GEAR_RATIOS_ARRAY
Gearbox - Format: "<reverse_gear> <neutral_gear> <forward_gear 1> [<forward gear 2>]....
Definition SimData.h:940
@ ACTORSIMATTR_TC_PULSE_TIME
Pulse duration in seconds, safe values <0.00005 - 1>; Param #4 of 'tractioncontrol'.
Definition SimData.h:933
@ ACTORSIMATTR_ENGOPTION_STALL_RPM
RPM where engine stalls - Param #7 of 'engoption'.
Definition SimData.h:949
@ ACTORSIMATTR_ENGOPTION_MAX_IDLE_MIXTURE
Max throttle to maintain idle RPM - Param #9 of 'engoption'.
Definition SimData.h:951
@ ACTORSIMATTR_ENGTURBO2_NUM_TURBOS
Number of turbos - Param #3 of 'engturbo2'.
Definition SimData.h:958
@ ACTORSIMATTR_ENGOPTION_ENGINE_INERTIA
Param #1 of 'engoption'.
Definition SimData.h:943
@ ACTORSIMATTR_ENGTURBO2_ANTILAG_MIN_RPM
Param #13 of 'engturbo2'.
Definition SimData.h:969
@ ACTORSIMATTR_TC_WHEELSLIP_CONSTANT
Minimum wheel slip threshold, safe value = 0.25; Param #2 of 'tractioncontrol'.
Definition SimData.h:932
@ ACTORSIMATTR_ENGTURBO2_MAX_RPM
MaxPSI * 10000 ~ calculated from Param #4 of 'engturbo2'.
Definition SimData.h:959
@ ACTORSIMATTR_ENGOPTION_IDLE_RPM
Target idle RPM - Param #8 of 'engoption'.
Definition SimData.h:950
@ ACTORSIMATTR_ENGTURBO2_ANTILAG_ENABLED
Param #11 of 'engturbo2'.
Definition SimData.h:967
@ ACTORSIMATTR_ENGINE_SHIFTUP_RPM
Automatic transmission - Param #2 of 'engine'.
Definition SimData.h:937
@ ACTORSIMATTR_ENGTURBO2_BOV_ENABLED
Blow-off valve - Param #6 of 'engturbo2'.
Definition SimData.h:961
@ ACTORSIMATTR_ENGTURBO2_ENGINE_RPM_OP
Engine RPM threshold for turbo to operate - Param #5 of 'engturbo2'.
Definition SimData.h:960
@ ACTORSIMATTR_ENGOPTION_SHIFT_TIME
Param #4 of 'engoption'.
Definition SimData.h:946
@ ACTORSIMATTR_SCREWPROP_MAX_POWER
Param #4 of 'screwprops' (indexed)
Definition SimData.h:980
@ ACTORSIMATTR_TC_RATIO
Regulating force, safe values: <1 - 20>; Param #1 of 'tractioncontrol'.
Definition SimData.h:931
@ ACTORSIMATTR_ENGINE_SHIFTDOWN_RPM
Automatic transmission - Param #1 of 'engine'.
Definition SimData.h:936
@ ACTORSIMATTR_ENGINE_DIFF_RATIO
Differential ratio (aka global gear ratio) - Param #4 of 'engine'.
Definition SimData.h:939
@ ACTORSIMATTR_PROPENGINE_MAX_POWER
Param #7 of 'turboprops'/Param #8 of 'pistonprops', in kilowatts (indexed)
Definition SimData.h:977
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:779
std::vector< char > node_data
Compressed node positions.
Definition Actor.h:780
std::vector< float > wheel_data
Wheel rotations.
Definition Actor.h:781
Ogre::Vector3 out_body_forces
Definition Actor.h:773
Ogre::Vector3 accu_body_forces
Definition Actor.h:771
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