Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
VehicleAI.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3
4 Copyright 2005-2012 Pierre-Michel Ricordel
5 Copyright 2007-2012 Thomas Fischer
6 Copyright 2013-2016 Petr Ohlidal
7
8 For more information, see http://www.rigsofrods.org/
9
10 Rigs of Rods is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License version 3, as
12 published by the Free Software Foundation.
13
14 Rigs of Rods is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#ifdef USE_ANGELSCRIPT
24
25#include "VehicleAI.h"
26
27#include "Actor.h"
28#include "Console.h"
29#include "Engine.h"
30#include "GameContext.h"
31#include "AeroEngine.h"
32#include "ScrewProp.h"
33#include "GUIManager.h"
34#include "GUI_TopMenubar.h"
35
36#include "scriptdictionary/scriptdictionary.h"
37
38using namespace Ogre;
39using namespace RoR;
40
42 is_waiting(false),
43 wait_time(0.0f)
44{
45 beam = b;
46}
47
51
52void VehicleAI::setActive(bool value)
53{
54 is_enabled = value;
55 init_y = beam->getPosition().y;
56}
57
59{
60 return is_enabled;
61}
62
63void VehicleAI::addWaypoint(std::string const& id, Ogre::Vector3 const& point)
64{
65 if (current_waypoint == Vector3::ZERO)
66 current_waypoint = point;
67
69 waypoints.emplace(free_waypoints, point);
70 waypoint_ids.emplace(id, free_waypoints);
71 waypoint_names.emplace(free_waypoints, id);
72}
73
74void VehicleAI::addWaypoints(AngelScript::CScriptDictionary& d)
75{
76 for (auto item : d)
77 {
78 Ogre::Vector3 point;
79 item.GetValue(&point, item.GetTypeId());
80 std::string key(item.GetKey());
81 this->addWaypoint(key, point);
82 }
83}
84
85Ogre::Vector3 VehicleAI::getTranslation(int offset, unsigned int wp)
86{
87 Ogre::Vector3 translation = Ogre::Vector3::ZERO;
88
89 if (int(wp) == 0) // First waypoint we have nothing to compare, return translation based on initial vehicle rotation
90 {
91 if (App::GetGuiManager()->TopMenubar.ai_position_scheme == 0) // Set vehicle behind vehicle
92 {
93 translation.x += offset * cos(beam->getRotation() - Ogre::Math::HALF_PI);
94 translation.z += offset * sin(beam->getRotation() - Ogre::Math::HALF_PI);
95 }
96 else if (App::GetGuiManager()->TopMenubar.ai_position_scheme == 1) // Set vehicle parallel to vehicle
97 {
98 translation.x += offset * cos(beam->getRotation());
99 translation.z += offset * sin(beam->getRotation());
100 }
101 }
102 else // Return translation based on two waypoints
103 {
104 if (App::GetGuiManager()->TopMenubar.ai_position_scheme == 0)
105 {
106 Ogre::Vector3 dir = App::GetGuiManager()->TopMenubar.ai_waypoints[int(wp)-1].position - App::GetGuiManager()->TopMenubar.ai_waypoints[int(wp)].position;
107 translation -= offset * dir.normalisedCopy();
108 }
109 else if (App::GetGuiManager()->TopMenubar.ai_position_scheme == 1)
110 {
111 Ogre::Vector3 dir = App::GetGuiManager()->TopMenubar.ai_waypoints[int(wp)].position - App::GetGuiManager()->TopMenubar.ai_waypoints[int(wp)-1].position;
112 float angle = Ogre::Vector3::UNIT_Z.angleBetween(dir.normalisedCopy()).valueRadians();
113
114 if (dir.x > 0) // Direction on the right fails to produce offset in some angles, invert to have the same offset on both sides
115 {
116 angle = -Ogre::Vector3::UNIT_Z.angleBetween(dir.normalisedCopy()).valueRadians();
117 }
118
119 translation.x -= offset * cos(angle);
120 translation.z -= offset * sin(angle);
121 }
122 }
123
124 return translation;
125}
126
127void VehicleAI::addEvent(std::string const& id, int ev)
128{
129 int waypointid = waypoint_ids[id];
130 if (waypointid)
131 waypoint_events.emplace(waypointid, ev);
132}
133
134void VehicleAI::setValueAtWaypoint(std::string const& id, int value_id, float value)
135{
136 int waypointid = waypoint_ids[id];
137 if (waypointid)
138 {
139 switch (value_id)
140 {
141 case AI_SPEED:
142 waypoint_speed.emplace(id, value);
143 break;
144 case AI_POWER:
145 waypoint_power.emplace(waypointid, value);
146 break;
147 default:
148 break;
149 }
150 }
151}
152
154{
156 {
158 }
159
161 if (event)
162 {
163 switch (event)
164 {
165 case AI_LIGHTSTOGGLE:
167 break;
168 case AI_BEACONSTOGGLE:
170 break;
171 default:
172 break;
173 }
174 }
175
177 if (speed)
178 maxspeed = speed;
179
180 float power = waypoint_power[current_waypoint_id];
181 if (power)
182 acc_power = power;
183
186 {
188 last_waypoint = true;
189
190 if (beam->ar_engine || beam->ar_num_screwprops > 0) // Keep airplanes going
191 {
192 is_enabled = false;
193 }
194
195 if (beam->ar_engine) // Stop truck
196 {
197 beam->ar_parking_brake = true;
198 }
199 else if (beam->ar_num_screwprops > 0) // Stop boat
200 {
201 for (int i = 0; i < beam->ar_num_screwprops; i++)
202 {
203 beam->ar_screwprops[i]->reset();
204 }
205 }
206 }
208
209 if (beam->ar_engine)
210 {
213 }
214}
215
216void VehicleAI::update(float dt, int doUpdate)
217{
218 if (is_waiting)
219 {
220 wait_time -= dt;
221 if (wait_time < 0)
222 {
223 is_waiting = false;
224 }
225 return;
226 }
227
228 Vector3 mAgentPosition = beam->getPosition();
229 // Vector3 > Vector2
230 mAgentPosition.y = 0;
231 current_waypoint.y = 0;
232 prev_waypoint.y = 0;
233 next_waypoint.y = 0;
234
235 int dist = 5;
236 if (beam->ar_num_screwprops > 0)
237 {
238 dist = 50; // More tolerance for boats
239 }
240 else if (beam->ar_num_aeroengines > 0)
241 {
242 dist = 100; // Even more tolerance for airplanes
243 }
244
245 // Find the angle (Radian) of the upcoming turn
246 Ogre::Vector3 dir1 = current_waypoint - prev_waypoint;
247 Ogre::Vector3 dir2 = next_waypoint - current_waypoint;
248 float angle_rad = 0;
249 float angle_deg = 0;
250 if (App::GetGuiManager()->TopMenubar.ai_mode == 0) // Normal driving mode
251 {
252 angle_rad = dir1.angleBetween(dir2.normalisedCopy()).valueRadians(); // PI
253 angle_deg = dir1.angleBetween(dir2.normalisedCopy()).valueDegrees(); // Degrees 0 - 180
254 }
255
256 if (App::GetGuiManager()->TopMenubar.ai_mode == 4) // Chase driving mode
257 {
258 if (App::GetGameContext()->GetPlayerActor()) // We are in vehicle
259 {
261 }
262 else // We are in feet
263 {
265 }
266 }
267 else
268 {
269 for (int i = 0; i < beam->ar_num_nodes; i++)
270 {
271 Ogre::Vector3 pos = beam->getNodePosition(i);
272 pos.y = 0;
273 if (current_waypoint.distance(pos) < dist)
274 {
276 return;
277 }
278 }
279 }
280
281 Vector3 TargetPosition = current_waypoint;
282 TargetPosition.y = 0; // Vector3 > Vector2
283 Quaternion TargetOrientation = Quaternion::ZERO;
284
285 Quaternion mAgentOrientation = Quaternion(Radian(beam->getRotation()), Vector3::NEGATIVE_UNIT_Y);
286 mAgentOrientation.normalise();
287
288 Vector3 mVectorToTarget = TargetPosition - mAgentPosition; // A-B = B->A
289 mAgentPosition.normalise();
290
291 Vector3 mAgentHeading = mAgentOrientation * mAgentPosition;
292 Vector3 mTargetHeading = TargetOrientation * TargetPosition;
293 mAgentHeading.normalise();
294 mTargetHeading.normalise();
295
296 // Compute new torque scalar (-1.0 to 1.0) based on heading vector to target.
297 Vector3 mSteeringForce = mAgentOrientation.Inverse() * mVectorToTarget;
298 mSteeringForce.normalise();
299
300 float mYaw = mSteeringForce.x;
301 float mPitch = mSteeringForce.z;
302 //float mRoll = mTargetVO.getRotationTo( mAgentVO ).getRoll().valueRadians();
303
304 if (mPitch > 0)
305 {
306 if (mYaw > 0)
307 mYaw = 1;
308 else
309 mYaw = -1;
310 }
311
312 // Steer truck
313 if (beam->ar_engine)
314 {
315 beam->ar_hydro_dir_command = mYaw; // mYaw
316 }
317
318 // Steer boat
319 if (beam->ar_num_screwprops > 0)
320 {
321 for (int i = 0; i < beam->ar_num_screwprops; i++)
322 {
323 beam->ar_screwprops[i]->setRudder(-mYaw);
324 }
325 }
326
327 // Steer airplane
328 if (beam->ar_num_aeroengines > 0)
329 {
330 if (!last_waypoint) // Not last waypoint yet, follow waypoints
331 {
332 beam->ar_hydro_dir_command = mYaw; // Wheels
333 beam->ar_aileron = mYaw / 2; // Wings
334
335 if (abs(beam->GetCameraRoll().y) > 0.5f) // Oversteer, avoid flip
336 {
337 beam->ar_aileron = 0;
338 }
339 }
340
341 if (abs(mYaw) < 0.1f || last_waypoint) // Too little steering or last waypoint, stabilize
342 {
344 }
345 }
346
347 if (beam->ar_engine) // Truck
348 {
349 // Start engine if not running
350 if (!beam->ar_engine->isRunning())
352
353 beam->ar_parking_brake = false;
354 float kmh_wheel_speed = beam->getWheelSpeed() * 3.6;
355
356 if (abs(mYaw) < 0.5f)
357 {
358 if (kmh_wheel_speed < maxspeed - 1)
359 {
360 beam->ar_brake = 0;
361 beam->ar_engine->autoSetAcc(acc_power - (angle_rad * 0.1f)); // Start easy after turn
362 }
363 else if (kmh_wheel_speed > maxspeed + 1)
364 {
365 beam->ar_brake = 1.0f / 3.0f;
367 }
368 else
369 {
370 beam->ar_brake = 0;
372 }
373 }
374 else
375 {
376 if (kmh_wheel_speed < maxspeed - 1)
377 {
378 beam->ar_brake = 0;
380 }
381 else if (kmh_wheel_speed > maxspeed + 1)
382 {
383 beam->ar_brake = 1.0f / 2.0f;
385 }
386 else
387 {
388 beam->ar_brake = 0;
390 }
391 }
392
393 if (App::GetGuiManager()->TopMenubar.ai_mode == 0) // Normal driving mode
394 {
395 Ogre::Vector3 pos = beam->getPosition();
396 pos.y = 0;
397
399 {
400 // Turn ahead, reduce speed relative to the angle and the current speed
401 if (angle_deg > 0 && current_waypoint.distance(pos) < kmh_wheel_speed)
402 {
403 // Speed limit: 10 - 180 degree angle -> 50 - 5 km/h
404 float t = ((angle_deg - 10) / (180 - 10))*1.4f; // Reduce a bit to achive ~20 km/h for a 90 degree angle
405 maxspeed = (1 - t)*50 + t*5;
406 if (maxspeed > 50) // Limit to 50 km/h
407 {
408 maxspeed = 50;
409 }
410 if (maxspeed > App::GetGuiManager()->TopMenubar.ai_speed) // Respect user defined lower speed
411 {
413 }
414 }
415 else // Reset
416 {
418 }
419 }
420
421 // Collision avoidance with other actors
423 {
424 if (actor->ar_driveable == NOT_DRIVEABLE) // Ignore objects that may be actors
425 continue;
426 if (actor == beam) // Ignore ourselves
427 continue;
428
429 Ogre::Vector3 a = actor->getPosition() - beam->getPosition();
430
431 if (beam->getDirection().angleBetween(a).valueDegrees() < 30) // Is in front
432 {
433 // Actor ahead, slow down - distance relative to current speed so the faster we go the earlier we slow down
434 if (beam->getPosition().distance(actor->getPosition()) < kmh_wheel_speed)
435 {
436 beam->ar_brake = 1;
438 }
439
440 for (int i = 0; i < beam->ar_num_nodes; i++)
441 {
442 for (int k = 0; k < actor->ar_num_nodes; k++)
443 {
444 // Too close, stop
445 if (beam->getNodePosition(i).distance(actor->getNodePosition(k)) < 5)
446 {
447 beam->ar_parking_brake = true;
449 break;
450 }
451 }
452 }
453 }
454 }
455
456 // Collision avoidance with character
458
459 if (beam->getDirection().angleBetween(b).valueDegrees() < 30 && // Is in front
461 {
462 // Character ahead, slow down - distance relative to current speed so the faster we go the earlier we slow down
463 if (beam->getPosition().distance(App::GetGameContext()->GetPlayerCharacter()->getPosition()) < kmh_wheel_speed)
464 {
465 beam->ar_brake = 1;
467 }
468
469 for (int i = 0; i < beam->ar_num_nodes; i++)
470 {
471 // Too close, steer
473 {
476 break;
477 }
478 }
479 }
480 }
481 else if (App::GetGuiManager()->TopMenubar.ai_mode == 1 || // Race driving mode
482 App::GetGuiManager()->TopMenubar.ai_mode == 2 || // Drag race mode
483 App::GetGuiManager()->TopMenubar.ai_mode == 3) // Crash driving mode
484 {
486 {
488 }
489 }
490 else if (App::GetGuiManager()->TopMenubar.ai_mode == 4) // Chase driving mode
491 {
492 if (App::GetGameContext()->GetPlayerActor())
493 {
494 maxspeed += App::GetGameContext()->GetPlayerActor()->getSpeed(); // Get him!!
495 }
496 else // Reset
497 {
499 }
500
501 // Collision avoidance with other actors
503 {
504 if (actor->ar_driveable == NOT_DRIVEABLE) // Ignore objects that may be actors
505 continue;
506 if (actor == beam) // Ignore ourselves
507 continue;
508
509 Ogre::Vector3 a = actor->getPosition() - beam->getPosition();
510
511 if (beam->getDirection().angleBetween(a).valueDegrees() < 30) // Is in front
512 {
513 // Too close, stop
514 if (beam->getPosition().distance(actor->getPosition()) < 10)
515 {
517 beam->ar_parking_brake = true;
519 }
520 }
521 }
522
523 // Collision avoidance with character
525
526 if (beam->getDirection().angleBetween(b).valueDegrees() < 30 && // Is in front
528 {
529 // Too close, stop
531 {
532 beam->ar_parking_brake = true;
534 }
535 }
536 }
537 }
538 else if (beam->ar_num_aeroengines > 0) // Airplane
539 {
540 if (beam->getParkingBrake())
541 {
542 beam->ar_parking_brake = false;
543 }
544
545 for (int i = 0; i < beam->ar_num_aeroengines; i++) // Start engines
546 {
547 if (!beam->ar_aeroengines[i]->getIgnition())
548 {
551 }
552 }
553
554 float target_alt = App::GetGuiManager()->TopMenubar.ai_altitude / 3.28083f; // Feet
555
556 if (beam->getPosition().y - init_y < target_alt * 0.8f)
557 {
558 hold = false;
559 }
560
561 if (beam->getPosition().y - init_y < target_alt && !hold) // Reach defined altitude
562 {
563 beam->ar_elevator = 0.5f - abs(beam->GetCameraDir().y);
565
566 if (beam->GetCameraDir().y > 0.5f) // Avoid over-elevate flip
567 {
568 beam->ar_elevator = -0.05f;
569 }
570 }
571 else if (beam->getPosition().y - init_y > target_alt) // We reached defined altitude, hold
572 {
573 for (int i = 0; i < beam->ar_num_aeroengines; i++)
574 {
576 }
577 hold = true;
578 }
579
580 if (hold)
581 {
583
584 if (beam->GetCameraDir().y < 0)
585 {
586 beam->ar_aerial_flap = 1;
587 }
588 else if (beam->GetCameraDir().y > 0)
589 {
590 beam->ar_aerial_flap = 0;
591 }
592 }
593 }
594 else if (beam->ar_num_screwprops > 0) // Boat
595 {
596 Vector3 hdir = beam->GetCameraDir();
597 float knots = hdir.dotProduct(beam->ar_nodes[beam->ar_main_camera_node_pos].Velocity) * 1.9438f; // 1.943 = m/s in knots/s
599
600 if (abs(mYaw) < 0.5f)
601 {
602 if (knots < maxspeed - 1)
603 {
604 for (int i = 0; i < beam->ar_num_screwprops; i++)
605 {
607 }
608 }
609 else if (knots > maxspeed + 1)
610 {
611 for (int i = 0; i < beam->ar_num_screwprops; i++)
612 {
614 }
615 }
616 else
617 {
618 for (int i = 0; i < beam->ar_num_screwprops; i++)
619 {
621 }
622 }
623 }
624 else
625 {
626 if (knots < maxspeed - 1)
627 {
628 for (int i = 0; i < beam->ar_num_screwprops; i++)
629 {
631 }
632 }
633 else if (knots > maxspeed + 1)
634 {
635 for (int i = 0; i < beam->ar_num_screwprops; i++)
636 {
638 }
639 }
640 else
641 {
642 for (int i = 0; i < beam->ar_num_screwprops; i++)
643 {
645 }
646 }
647 }
648 }
649}
650
651#endif // USE_ANGELSCRIPT
Game state manager and message-queue provider.
Simple waypoint AI.
bool ar_parking_brake
Definition Actor.h:467
int ar_num_screwprops
Definition Actor.h:391
EnginePtr ar_engine
Definition Actor.h:432
float getWheelSpeed() const
Definition Actor.h:107
node_t * ar_nodes
Definition Actor.h:330
int ar_aerial_flap
Sim state; state of aircraft flaps (values: 0-5)
Definition Actor.h:474
void beaconsToggle()
Definition Actor.cpp:3849
float getSpeed()
Definition Actor.h:90
float getRotation()
Definition Actor.cpp:355
void toggleHeadlights()
Definition Actor.cpp:3062
ScrewpropPtr ar_screwprops[MAX_SCREWPROPS]
Definition Actor.h:390
bool getParkingBrake()
Definition Actor.h:180
NodeNum_t ar_main_camera_node_pos
Sim attr; ar_camera_node_pos[0] >= 0 ? ar_camera_node_pos[0] : 0.
Definition Actor.h:441
Ogre::Vector3 getPosition()
Definition Actor.cpp:370
float ar_hydro_dir_command
Definition Actor.h:456
float ar_elevator
Sim state; aerial controller.
Definition Actor.h:471
Ogre::Vector3 getDirection()
average actor velocity, calculated using the actor positions of the last two frames
Definition Actor.cpp:365
AeroEnginePtr ar_aeroengines[MAX_AEROENGINES]
Definition Actor.h:388
Ogre::Vector3 getNodePosition(int nodeNumber)
Returns world position of node.
Definition Actor.cpp:4690
int ar_num_aeroengines
Definition Actor.h:389
Ogre::Real ar_brake
Physics state; braking intensity.
Definition Actor.h:452
int ar_num_nodes
Definition Actor.h:345
float ar_aileron
Sim state; aerial controller.
Definition Actor.h:473
Ogre::Vector3 GetCameraDir()
Definition Actor.h:306
Ogre::Vector3 GetCameraRoll()
Definition Actor.h:307
ActorPtrVec & GetActors()
virtual bool getIgnition()=0
virtual void setThrottle(float val)=0
virtual void flipStart()=0
Ogre::Vector3 getPosition()
Definition Character.cpp:92
ActorPtr GetActorCoupling()
@ CONSOLE_MSGTYPE_SCRIPT
Messages sent from scripts.
Definition Console.h:62
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_NOTICE
Definition Console.h:51
void startEngine()
Quick engine start. Plays sounds.
Definition Engine.cpp:995
void autoSetAcc(float val)
Definition Engine.cpp:1075
bool isRunning()
Definition Engine.h:101
std::vector< ai_events > ai_waypoints
GUI::TopMenubar TopMenubar
Definition GUIManager.h:133
Character * GetPlayerCharacter()
const ActorPtr & GetPlayerActor()
ActorManager * GetActorManager()
void setRudder(float val)
Definition ScrewProp.cpp:94
void setThrottle(float val)
Definition ScrewProp.cpp:81
void addEvent(std::string const &id, int ev)
Adds a event.
void setValueAtWaypoint(std::string const &id, int value_id, float value)
Sets a value at a waypoint.
Ogre::Vector3 getTranslation(int offset, unsigned int wp)
Gets offset translation based on vehicle rotation and waypoints.
Definition VehicleAI.cpp:85
int current_waypoint_id
The coordinates of the next waypoint.
Definition VehicleAI.h:146
void addWaypoints(AngelScript::CScriptDictionary &d)
Adds a dictionary with waypoints.
Definition VehicleAI.cpp:74
int free_waypoints
The amount of waypoints.
Definition VehicleAI.h:154
bool is_enabled
True if the AI is driving.
Definition VehicleAI.h:142
Ogre::Vector3 prev_waypoint
Definition VehicleAI.h:144
void update(float dt, int doUpdate)
Updates the AI.
float maxspeed
(KM/H) The max speed the AI is allowed to drive.
Definition VehicleAI.h:140
Ogre::Vector3 current_waypoint
The coordinates of the waypoint that the AI is driving to.
Definition VehicleAI.h:143
float acc_power
The engine power.
Definition VehicleAI.h:155
std::map< int, Ogre::Vector3 > waypoints
Map with all waypoints.
Definition VehicleAI.h:147
void setActive(bool value)
Activates/Deactivates the AI.
Definition VehicleAI.cpp:52
ActorPtr beam
The verhicle the AI is driving.
Definition VehicleAI.h:141
Ogre::Vector3 next_waypoint
The coordinates of the previous waypoint.
Definition VehicleAI.h:145
std::map< int, int > waypoint_events
Map with all waypoint events.
Definition VehicleAI.h:150
std::map< std::string, int > waypoint_ids
Map with all waypoint IDs.
Definition VehicleAI.h:148
void addWaypoint(std::string const &id, Ogre::Vector3 const &point)
Adds one waypoint.
Definition VehicleAI.cpp:63
void updateWaypoint()
Updates the AI waypoint.
std::map< int, std::string > waypoint_names
Map with all waypoint names.
Definition VehicleAI.h:149
std::map< int, float > waypoint_power
Map with all waypoint engine power.
Definition VehicleAI.h:152
bool isActive()
Returns the status of the AI.
Definition VehicleAI.cpp:58
std::map< Ogre::String, float > waypoint_speed
Map with all waypoint speeds.
Definition VehicleAI.h:151
float wait_time
(seconds) The amount of time the AI has to wait.
Definition VehicleAI.h:138
VehicleAI(ActorPtr b)
Definition VehicleAI.cpp:41
virtual ~VehicleAI() override
Definition VehicleAI.cpp:48
@ NOT_DRIVEABLE
not drivable at all
Definition SimData.h:84
GUIManager * GetGuiManager()
GameContext * GetGameContext()
Console * GetConsole()
@ AI_LIGHTSTOGGLE
Definition VehicleAI.h:47
@ AI_BEACONSTOGGLE
Definition VehicleAI.h:49
@ AI_POWER
Definition VehicleAI.h:58
@ AI_SPEED
Definition VehicleAI.h:57
Ogre::Vector3 Velocity
Definition SimData.h:268