vehicledriver.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : LevelEdit *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/vehicledriver.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 2/22/02 7:13p $*
  29. * *
  30. * $Revision:: 43 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "vehicledriver.h"
  36. #include "smartgameobj.h"
  37. #include "matrix3d.h"
  38. #include "movephys.h"
  39. #include "vehiclephys.h"
  40. #include "path.h"
  41. #include "pscene.h"
  42. #include "vehicle.h"
  43. #include "soldier.h"
  44. #include "combat.h"
  45. #include "chunkio.h"
  46. #include <windows.h>
  47. #include "vehiclecurve.h"
  48. ////////////////////////////////////////////////////////////////
  49. // Save/Load constants
  50. ////////////////////////////////////////////////////////////////
  51. enum
  52. {
  53. CHUNKID_VARIABLES = 0x11040454,
  54. };
  55. enum
  56. {
  57. VARID_CURRENT_DEST = 1,
  58. VARID_FINAL_DEST,
  59. VARID_MAX_SPEED,
  60. VARID_SPEED_FACTOR,
  61. VARID_LAST_POS,
  62. VARID_BAD_PROGRESS_COUNT,
  63. VARID_IS_BACKING_UP,
  64. VARID_IS_BACKUP_LOCKED,
  65. VARID_TURN_OFF_ENGINE,
  66. VARID_PATH_PTR,
  67. VARID_GAME_OBJ_PTR,
  68. VARID_ARRIVED_DIST
  69. };
  70. ////////////////////////////////////////////////////////////////
  71. //
  72. // VehicleDriverClass
  73. //
  74. ////////////////////////////////////////////////////////////////
  75. VehicleDriverClass::VehicleDriverClass (void)
  76. : m_CurrentDest (0, 0, 0),
  77. m_FinalDest (0, 0, 0),
  78. m_CurrentPath (NULL),
  79. m_GameObj (NULL),
  80. m_IsBackingUp (false),
  81. m_MaxSpeed (20.0F),
  82. m_SpeedFactor (1.0F),
  83. m_LastPos (0, 0, 0),
  84. m_BadProgressCount (0),
  85. m_IsBackupLocked (false),
  86. m_TurnOffEngineWhenDone (false),
  87. m_ArrivedDist (1.0F),
  88. m_BrakingDist (0),
  89. m_LastFrameExpectedVelocity (0)
  90. {
  91. return ;
  92. }
  93. ////////////////////////////////////////////////////////////////
  94. //
  95. // ~VehicleDriverClass
  96. //
  97. ////////////////////////////////////////////////////////////////
  98. VehicleDriverClass::~VehicleDriverClass (void)
  99. {
  100. Free();
  101. }
  102. ////////////////////////////////////////////////////////////////
  103. //
  104. // Get_Velocity
  105. //
  106. // Returns the object-space velocity vector
  107. //
  108. ////////////////////////////////////////////////////////////////
  109. void
  110. VehicleDriverClass::Get_Velocity (const Matrix3D &tm, Vector3 &vel_vector)
  111. {
  112. //
  113. // Get a pointer to the physics object for this vehicle
  114. //
  115. MoveablePhysClass *phys_obj = m_GameObj->Peek_Physical_Object()->As_MoveablePhysClass ();
  116. if (phys_obj != NULL) {
  117. //
  118. // Get the world-space velocity vector for this vehicle and transform
  119. // it into object space.
  120. //
  121. Vector3 vel_vector_world;
  122. phys_obj->Get_Velocity (&vel_vector_world);
  123. Matrix3D::Inverse_Rotate_Vector (tm, vel_vector_world, &vel_vector);
  124. } else {
  125. vel_vector.Set (0, 0, 0);
  126. }
  127. return ;
  128. }
  129. ////////////////////////////////////////////////////////////////
  130. //
  131. // Initialize
  132. //
  133. ////////////////////////////////////////////////////////////////
  134. void
  135. VehicleDriverClass::Initialize (SmartGameObj *game_obj, PathClass *path)
  136. {
  137. m_GameObj = game_obj;
  138. //
  139. // If the game object is driving a vehicle, then get the vehicle instead
  140. //
  141. SoldierGameObj *soldier_game_obj = game_obj->As_SoldierGameObj ();
  142. if (soldier_game_obj != NULL) {
  143. VehicleGameObj *vehicle_game_obj = soldier_game_obj->Get_Profile_Vehicle ();
  144. if (vehicle_game_obj != NULL) {
  145. m_GameObj = vehicle_game_obj;
  146. }
  147. }
  148. m_CurrentPath = path;
  149. m_FinalDest = path->Get_Dest_Pos ();
  150. m_CurrentDest = path->Get_Dest_Pos ();
  151. m_BrakingDist = 0;
  152. m_LastFrameExpectedVelocity = 0;
  153. //
  154. // Determine if this game object is a vehicle or not (it better be...)
  155. //
  156. VehicleGameObj *vehicle = m_GameObj->As_VehicleGameObj ();
  157. if (vehicle != NULL) {
  158. //
  159. // Start this vehicle's engine's running
  160. //
  161. if (vehicle->Is_Engine_Enabled () == false) {
  162. vehicle->Enable_Engine (true);
  163. m_TurnOffEngineWhenDone = true;
  164. } else {
  165. m_TurnOffEngineWhenDone = false;
  166. }
  167. }
  168. return ;
  169. }
  170. ///////////////////////////////////////////////////////////////////////////
  171. //
  172. // Find_Tangent_Angle
  173. //
  174. ///////////////////////////////////////////////////////////////////////////
  175. static float
  176. Find_Tangent_Angle
  177. (
  178. const Vector2 & center,
  179. float radius,
  180. const Vector2 & point,
  181. bool clockwise
  182. )
  183. {
  184. float angle = 0;
  185. //
  186. // Calculate the distance from the point to the center of the circle
  187. //
  188. float delta_x = point.X - center.X;
  189. float delta_y = point.Y - center.Y;
  190. float dist = ::sqrt (delta_x * delta_x + delta_y * delta_y);
  191. if (dist == 0) {
  192. dist = 0.0001F;
  193. }
  194. //
  195. // Determine the offset angle (from the line between the point and center)
  196. // where the 2 tangent points lie.
  197. //
  198. float angle_offset = WWMath::Fast_Acos (radius / dist);
  199. float base_angle = WWMath::Atan2 (delta_x, -delta_y);
  200. base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
  201. //
  202. // Return which ever tangent point we would come across first, depending
  203. // on our orientation
  204. //
  205. if (clockwise) {
  206. angle = (WWMATH_PI * 3) - (base_angle + angle_offset);
  207. } else {
  208. angle = base_angle - angle_offset;
  209. }
  210. angle = WWMath::Wrap (angle, 0, WWMATH_PI * 2);
  211. if (dist < radius) {
  212. angle = DEG_TO_RADF (360.0F);
  213. }
  214. return angle;
  215. }
  216. ////////////////////////////////////////////////////////////////
  217. //
  218. // Drive
  219. //
  220. ////////////////////////////////////////////////////////////////
  221. bool
  222. VehicleDriverClass::Drive (void)
  223. {
  224. bool retval = false;
  225. //
  226. // Get the turn radius of the current vehicle
  227. //
  228. PathObjectClass path_obj;
  229. m_CurrentPath->Get_Path_Object (path_obj);
  230. float turn_radius = path_obj.Get_Turn_Radius ();
  231. //
  232. // Drive either using wheeled vehicle logic or tracked vehicle logic
  233. //
  234. if (turn_radius != 0) {
  235. retval = Drive_Wheeled ();
  236. } else {
  237. retval = Drive_Tracked ();
  238. }
  239. return retval;
  240. }
  241. ////////////////////////////////////////////////////////////////
  242. //
  243. // Drive_Wheeled
  244. //
  245. ////////////////////////////////////////////////////////////////
  246. bool
  247. VehicleDriverClass::Drive_Wheeled (void)
  248. {
  249. //
  250. // Convert the destinations from world to object space
  251. //
  252. Matrix3D tm = m_GameObj->Get_Transform ();
  253. Vector3 pos = tm.Get_Translation ();
  254. float facing = tm.Get_Z_Rotation ();
  255. tm.Make_Identity ();
  256. tm.Set_Translation (pos);
  257. tm.Rotate_Z (facing);
  258. /*PhysClass *phys_obj = m_GameObj->Peek_Physical_Object ();
  259. RenderObjClass *model = phys_obj->Peek_Model ();
  260. AABoxClass bounding_box;
  261. bounding_box = model->Get_Bounding_Box ();
  262. Vector3 offset;
  263. Matrix3D::Rotate_Vector (tm, Vector3 (bounding_box.Extent.X, 0, 0), &offset);
  264. tm.Set_Translation (tm.Get_Translation () - offset);
  265. PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (tm.Get_Translation (), Vector3 (0.5F,0.5F,0.5F)), Vector3 (1, 0, 1));*/
  266. //
  267. // Make sure we have the final destination point
  268. //
  269. if (m_CurrentPath != NULL) {
  270. m_FinalDest = m_CurrentPath->Get_Dest_Pos ();
  271. }
  272. float max_speed = m_MaxSpeed * m_SpeedFactor;
  273. float curve_modifier = 1.0F;
  274. //
  275. // Modify some of our parameters if we are force moving backwards
  276. //
  277. if (m_IsBackupLocked) {
  278. tm.Rotate_Z (DEG_TO_RADF (180));
  279. //curve_modifier = 2.0F;
  280. }
  281. //
  282. // Convert the current and final destination points into object space
  283. //
  284. Vector3 final_dest;
  285. Vector3 current_dest;
  286. Matrix3D::Inverse_Transform_Vector (tm, m_FinalDest, &final_dest);
  287. Matrix3D::Inverse_Transform_Vector (tm, m_CurrentDest, &current_dest);
  288. final_dest.Z = 0;
  289. current_dest.Z = 0;
  290. //
  291. // Get the vehicle's current position
  292. //
  293. Vector3 curr_pos;
  294. m_GameObj->Get_Position (&curr_pos);
  295. //
  296. // Calculate the distance to the destination
  297. //
  298. float dist_on_path = m_CurrentPath->Get_Remaining_Path_Length ();
  299. float dist_to_goal = (current_dest.Length () + dist_on_path);
  300. //
  301. // Are we there yet?
  302. //
  303. bool is_complete = (dist_to_goal <= m_ArrivedDist);
  304. if (is_complete) {
  305. dist_to_goal = 0;
  306. }
  307. if (is_complete == false) {
  308. //
  309. // Get the sharpness of the curve from the path object
  310. //
  311. Vector3 curve_pos (0, 0, 0);
  312. float curve_sharpness = m_CurrentPath->Get_Curve_Sharpness (&curve_pos);
  313. //
  314. // Take into account the distance to the curve
  315. //
  316. float curve_dist = (curve_pos - tm.Get_Translation ()).Length ();
  317. curve_sharpness *= 1.0F - WWMath::Clamp (curve_dist / max_speed, 0, 1.0F);
  318. curve_sharpness *= curve_modifier;
  319. //
  320. // Calculate what the angle is between us and the next destination point
  321. //
  322. float turn_angle = WWMath::Atan2 (current_dest.Y, current_dest.X);
  323. turn_angle = WWMath::Wrap (turn_angle, DEG_TO_RADF(-180), DEG_TO_RADF(180));
  324. //
  325. // Calculate how sharp we need to turn based on the sharpness of the curve
  326. //
  327. float max_angle = DEG_TO_RADF (10.0F) + (1.0F - curve_sharpness) * DEG_TO_RADF (30.0F);
  328. float turn_accel = turn_angle / max_angle;
  329. /*PathObjectClass path_obj;
  330. m_CurrentPath->Get_Path_Object (path_obj);
  331. float turn_radius = path_obj.Get_Turn_Radius ();*/
  332. /*if ((turn_radius * turn_radius) > current_dest.Length2 () && WWMath::Fabs (turn_angle) > DEG_TO_RADF (60.0F)) {
  333. m_IsBackingUp = true;
  334. }*/
  335. //
  336. // Calculate our (current) normalized speed
  337. //
  338. Vector3 vel_vector;
  339. Get_Velocity (tm, vel_vector);
  340. float speed = vel_vector.X;
  341. float norm_speed = speed / max_speed;
  342. //
  343. // Calculate how fast we should accelerate
  344. //
  345. float expected_velocity = 1.0F - (0.3F * curve_sharpness);
  346. //
  347. // Take into account the braking speed as we approach the goal
  348. //
  349. float brake_velocity = Calculate_Brake (dist_to_goal, expected_velocity, speed, max_speed);
  350. expected_velocity = min (brake_velocity, expected_velocity);
  351. //
  352. // Calculate how far off our last velocity request was
  353. //
  354. float vel_factor = 1.0F;
  355. if (norm_speed != 0) {
  356. vel_factor = m_LastFrameExpectedVelocity / WWMath::Fabs (norm_speed);
  357. }
  358. //
  359. // Try to account for our last velocity request error
  360. //
  361. m_LastFrameExpectedVelocity = WWMath::Fabs (expected_velocity);
  362. float forward_accel = (expected_velocity - norm_speed) * vel_factor;
  363. /*StringClass message;
  364. message.Format ("forward accel = %f\n", forward_accel);
  365. WWDEBUG_SAY (((const char *)message));*/
  366. //
  367. // Invert everything if we are locked in driving backwards mode
  368. //
  369. if (m_IsBackupLocked) {
  370. forward_accel = -forward_accel;
  371. turn_accel = -turn_accel;
  372. }
  373. //
  374. // Handle the case where we are backing up
  375. //
  376. /*if (m_IsBackingUp) {
  377. //
  378. // Switch out of backup mode if we have a pretty good facing on the
  379. // destination or we got stuck while backing up.
  380. //
  381. if (WWMath::Fabs (turn_angle) < DEG_TO_RADF (25.0F) || Are_We_Stuck (vel_vector)) {
  382. m_IsBackingUp = false;
  383. m_BadProgressCount = 0;
  384. } else {
  385. //
  386. // Backup and turn in the opposite direction
  387. //
  388. forward_accel = -forward_accel;
  389. turn_accel = -turn_accel;
  390. }
  391. } else if (Are_We_Stuck (vel_vector)) {
  392. m_IsBackingUp = true;
  393. m_BadProgressCount = 0;
  394. }*/
  395. //
  396. // Clamp the analog controls to -1 and 1
  397. //
  398. turn_accel = WWMath::Clamp (turn_accel, -1.0F, 1.0F);
  399. forward_accel = WWMath::Clamp (forward_accel, -1.0F, 1.0F);
  400. //
  401. // Pass the controls onto the vehicle
  402. //
  403. Apply_Controls (forward_accel, turn_accel);
  404. } else {
  405. //
  406. // Calculate our (current) normalized speed
  407. //
  408. Vector3 vel_vector;
  409. Get_Velocity (tm, vel_vector);
  410. float speed = vel_vector.X;
  411. float norm_speed = speed / max_speed;
  412. //
  413. // Try to zero out our forward movement
  414. //
  415. float forward_accel = WWMath::Clamp (-norm_speed * 5.0F, -1.0F, 1.0F);
  416. if (m_GameObj->Peek_Physical_Object ()->As_VTOLVehicleClass ()) {
  417. forward_accel = WWMath::Clamp (-norm_speed * 5.0F, -1.0F, 1.0F);
  418. }
  419. //
  420. // Invert everything if we are locked in driving backwards mode
  421. //
  422. if (m_IsBackupLocked) {
  423. forward_accel = -forward_accel;
  424. }
  425. //
  426. // Try to hover about the point
  427. //
  428. Apply_Controls (forward_accel, 0);
  429. //
  430. // We aren't really done unless we've stopped most motion
  431. //
  432. if (m_GameObj->Peek_Physical_Object ()->As_VTOLVehicleClass ()) {
  433. is_complete = (WWMath::Fabs (norm_speed) < 0.0001F);
  434. }
  435. //
  436. // Do we need to turn off the engine?
  437. //
  438. if (is_complete && m_TurnOffEngineWhenDone) {
  439. //
  440. // Turn off the engine if we are finished moving
  441. //
  442. VehicleGameObj *vehicle = m_GameObj->As_VehicleGameObj ();
  443. if (vehicle != NULL) {
  444. Apply_Controls (0, 0);
  445. vehicle->Enable_Engine (false);
  446. }
  447. }
  448. }
  449. return is_complete;
  450. }
  451. ////////////////////////////////////////////////////////////////
  452. //
  453. // Calculate_Brake
  454. //
  455. ////////////////////////////////////////////////////////////////
  456. float
  457. VehicleDriverClass::Calculate_Brake
  458. (
  459. float dist_to_goal,
  460. float expected_velocity,
  461. float curr_speed,
  462. float max_speed
  463. )
  464. {
  465. float brake_velocity = expected_velocity;
  466. //
  467. // Handle VTOL's differently
  468. //
  469. if (m_GameObj->Peek_Physical_Object ()->As_VTOLVehicleClass () == NULL) {
  470. //
  471. // Start braking when the vehicle is 1 second away from its destination
  472. //
  473. if (m_BrakingDist == 0 && dist_to_goal < curr_speed) {
  474. m_BrakingDist = curr_speed;
  475. }
  476. //
  477. // Calculate what speed we should be given the distance to the destination
  478. //
  479. if (m_BrakingDist > 0) {
  480. float end_velocity = (0.5F / max_speed);
  481. float percent = WWMath::Clamp (dist_to_goal / (m_BrakingDist - m_ArrivedDist), -1.0F, 1.0F);
  482. brake_velocity = ((1.0F - percent) * end_velocity) + (percent * expected_velocity);
  483. }
  484. } else {
  485. //
  486. // Start braking when the vehicle is 3 seconds away from its destination
  487. //
  488. if (m_BrakingDist == 0 && dist_to_goal < max_speed * 3) {
  489. m_BrakingDist = max_speed * 3;
  490. }
  491. //
  492. // Calculate what speed we should be given the distance to the destination
  493. //
  494. if (m_BrakingDist > 0) {
  495. float percent = WWMath::Clamp (dist_to_goal / (m_BrakingDist - m_ArrivedDist), -1.0F, 1.0F);
  496. float end_velocity = WWMath::Clamp ((-(curr_speed / max_speed) * 5.0F) + (0.75F / max_speed), -1.0F, 1.0F);
  497. brake_velocity = ((1.0F - percent) * end_velocity) + (percent * expected_velocity);
  498. }
  499. }
  500. return brake_velocity;
  501. }
  502. ////////////////////////////////////////////////////////////////
  503. //
  504. // Drive_Tracked
  505. //
  506. ////////////////////////////////////////////////////////////////
  507. bool
  508. VehicleDriverClass::Drive_Tracked (void)
  509. {
  510. //
  511. // Convert the destinations from world to object space
  512. //
  513. Matrix3D tm = m_GameObj->Get_Transform ();
  514. Vector3 pos = tm.Get_Translation ();
  515. float facing = tm.Get_Z_Rotation ();
  516. tm.Make_Identity ();
  517. tm.Set_Translation (pos);
  518. tm.Rotate_Z (facing);
  519. float max_speed = m_MaxSpeed * m_SpeedFactor;
  520. //
  521. // Let the path know how fast we are moving
  522. //
  523. Vector3 vel_vector;
  524. Get_Velocity (tm, vel_vector);
  525. m_CurrentPath->Set_Velocity (WWMath::Fabs (vel_vector.X));
  526. //
  527. // Make sure we have the final destination point
  528. //
  529. if (m_CurrentPath != NULL) {
  530. m_FinalDest = m_CurrentPath->Get_Dest_Pos ();
  531. m_CurrentDest = m_CurrentPath->Get_Next_Pos ();
  532. }
  533. //
  534. // Modify some of our parameters if we are force moving backwards
  535. //
  536. if (m_IsBackupLocked) {
  537. tm.Rotate_Z (DEG_TO_RADF (180));
  538. }
  539. //
  540. // Convert the current and final destination points into object space
  541. //
  542. Vector3 final_dest;
  543. Vector3 current_dest;
  544. Matrix3D::Inverse_Transform_Vector (tm, m_FinalDest, &final_dest);
  545. Matrix3D::Inverse_Transform_Vector (tm, m_CurrentDest, &current_dest);
  546. final_dest.Z = 0;
  547. current_dest.Z = 0;
  548. //
  549. // Get the vehicle's current position
  550. //
  551. Vector3 curr_pos;
  552. m_GameObj->Get_Position (&curr_pos);
  553. //
  554. // Calculate the distance to the destination
  555. //
  556. float dist_on_path = m_CurrentPath->Get_Remaining_Path_Length ();
  557. float dist_to_goal = (current_dest.Length () + dist_on_path);
  558. //
  559. // Are we there yet?
  560. //
  561. bool is_complete = (dist_to_goal <= m_ArrivedDist);
  562. if (is_complete) {
  563. dist_to_goal = 0;
  564. }
  565. if (is_complete == false) {
  566. //
  567. // Calculate what the angle is between us and the next destination point
  568. //
  569. float turn_angle = WWMath::Atan2 (current_dest.Y, current_dest.X);
  570. turn_angle = WWMath::Wrap (turn_angle, DEG_TO_RADF(-180), DEG_TO_RADF(180));
  571. //StringClass message;
  572. // message.Format ("turn_angle = %f\n", turn_angle * 180 / 3.1415926F);
  573. //WWDEBUG_SAY (((const char *)message));
  574. //
  575. // Calculate how sharp we need to turn based on the sharpness of the curve
  576. //
  577. float turn_accel = turn_angle / DEG_TO_RADF (45.0F);
  578. //
  579. // Calculate our (current) normalized speed
  580. //
  581. float speed = vel_vector.X;
  582. float norm_speed = speed / max_speed;
  583. //
  584. // Calculate how fast we should accelerate
  585. //
  586. float expected_velocity = 1.0F;
  587. //
  588. // Start braking when the vehicle is 1 second away from its destination
  589. //
  590. if (m_BrakingDist == 0 && dist_to_goal < speed) {
  591. m_BrakingDist = speed;
  592. }
  593. if (m_BrakingDist > 0) {
  594. float end_velocity = (0.5F / max_speed);
  595. float percent = WWMath::Clamp (dist_to_goal / (m_BrakingDist - m_ArrivedDist), -1.0F, 1.0F);
  596. float braking_vel = ((1.0F - percent) * end_velocity) + (percent * expected_velocity);
  597. expected_velocity = min (expected_velocity, braking_vel);
  598. }
  599. float forward_accel = expected_velocity - norm_speed;
  600. //
  601. // Make tracked vehicles turn in place
  602. //
  603. if (WWMath::Fabs (turn_angle) > DEG_TO_RADF (10.0F)) {
  604. forward_accel /= 4.0F;
  605. }
  606. //
  607. // Invert everything if we are locked in driving backwards mode
  608. //
  609. if (m_IsBackupLocked) {
  610. forward_accel = -forward_accel;
  611. }
  612. //
  613. // Clamp the analog controls to -1 and 1
  614. //
  615. turn_accel = WWMath::Clamp (turn_accel, -1.0F, 1.0F);
  616. forward_accel = WWMath::Clamp (forward_accel, -1.0F, 1.0F);
  617. //
  618. // Pass the controls onto the vehicle
  619. //
  620. Apply_Controls (forward_accel, turn_accel);
  621. } else {
  622. //
  623. // Calculate our (current) normalized speed
  624. //
  625. Vector3 vel_vector;
  626. Get_Velocity (tm, vel_vector);
  627. float speed = vel_vector.X;
  628. float norm_speed = speed / max_speed;
  629. //
  630. // Try to zero out our forward movement
  631. //
  632. float forward_accel = WWMath::Clamp (-norm_speed * 2, -1.0F, 1.0F);
  633. //
  634. // Invert everything if we are locked in driving backwards mode
  635. //
  636. if (m_IsBackupLocked) {
  637. forward_accel = -forward_accel;
  638. }
  639. //
  640. // Try to hover about the point
  641. //
  642. Apply_Controls (forward_accel, 0);
  643. //
  644. // Do we need to turn off the engine?
  645. //
  646. if (m_TurnOffEngineWhenDone) {
  647. //
  648. // Turn off the engine if we are finished moving
  649. //
  650. VehicleGameObj *vehicle = m_GameObj->As_VehicleGameObj ();
  651. if (vehicle != NULL) {
  652. Apply_Controls (0, 0);
  653. vehicle->Enable_Engine (false);
  654. }
  655. }
  656. }
  657. return is_complete;
  658. }
  659. ////////////////////////////////////////////////////////////////
  660. //
  661. // Apply_Controls
  662. //
  663. ////////////////////////////////////////////////////////////////
  664. void
  665. VehicleDriverClass::Apply_Controls
  666. (
  667. float forward_accel,
  668. float left_accel
  669. )
  670. {
  671. if (!WWMath::Is_Valid_Float(forward_accel)) {
  672. #ifdef WWDEBUG
  673. const char* name = m_GameObj->Peek_Physical_Object()->Peek_Model()->Get_Name();
  674. WWDEBUG_SAY(("VehicleDriverClass::Apply_Controls - BAD FLOAT forward_access = %f - Model name = %s\n", forward_accel, name));
  675. #endif //WWDEBUG
  676. forward_accel = 0.0f;
  677. }
  678. if (!WWMath::Is_Valid_Float(left_accel)) {
  679. #ifdef WWDEBUG
  680. const char* name = m_GameObj->Peek_Physical_Object()->Peek_Model()->Get_Name();
  681. WWDEBUG_SAY(("VehicleDriverClass::Apply_Controls - BAD FLOAT left_access = %f - Model name = %s\n", left_accel, name));
  682. #endif //WWDEBUG
  683. left_accel = 0.0f;
  684. }
  685. //
  686. // Now pass the controls onto the game object
  687. //
  688. m_GameObj->Set_Analog_Control (ControlClass::ANALOG_MOVE_FORWARD, forward_accel);
  689. m_GameObj->Set_Analog_Control (ControlClass::ANALOG_TURN_LEFT, left_accel);
  690. m_GameObj->Set_Analog_Control (ControlClass::ANALOG_MOVE_LEFT, 0);
  691. return ;
  692. }
  693. ////////////////////////////////////////////////////////////////
  694. //
  695. // Force_Backup
  696. //
  697. ////////////////////////////////////////////////////////////////
  698. void
  699. VehicleDriverClass::Force_Backup (bool onoff)
  700. {
  701. //
  702. // Simply set these flags, the Drive method will
  703. // make use of them.
  704. //
  705. m_IsBackingUp = onoff;
  706. m_IsBackupLocked = onoff;
  707. return ;
  708. }
  709. ////////////////////////////////////////////////////////////////
  710. //
  711. // Are_We_Stuck
  712. //
  713. ////////////////////////////////////////////////////////////////
  714. bool
  715. VehicleDriverClass::Are_We_Stuck (const Vector3 &vel_vector)
  716. {
  717. //
  718. // Determine if we are not making significant progress
  719. //
  720. if (WWMath::Fabs (vel_vector.X) < (m_MaxSpeed * m_SpeedFactor) / 10.0F) {
  721. m_BadProgressCount ++;
  722. } else {
  723. m_BadProgressCount = 0;
  724. }
  725. //
  726. // If we aren't making progress for 30 frames in a row,
  727. // then assume we are stuck
  728. //
  729. return bool(m_BadProgressCount > 30);
  730. }
  731. ////////////////////////////////////////////////////////////////////////////////////////////
  732. //
  733. // Save
  734. //
  735. ////////////////////////////////////////////////////////////////////////////////////////////
  736. void
  737. VehicleDriverClass::Save (ChunkSaveClass &csave)
  738. {
  739. csave.Begin_Chunk (CHUNKID_VARIABLES);
  740. //
  741. // Save each variable to its own microchunk
  742. //
  743. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_DEST, m_CurrentDest);
  744. WRITE_MICRO_CHUNK (csave, VARID_FINAL_DEST, m_FinalDest);
  745. WRITE_MICRO_CHUNK (csave, VARID_MAX_SPEED, m_MaxSpeed);
  746. WRITE_MICRO_CHUNK (csave, VARID_SPEED_FACTOR, m_SpeedFactor);
  747. WRITE_MICRO_CHUNK (csave, VARID_LAST_POS, m_LastPos);
  748. WRITE_MICRO_CHUNK (csave, VARID_BAD_PROGRESS_COUNT, m_BadProgressCount);
  749. WRITE_MICRO_CHUNK (csave, VARID_IS_BACKING_UP, m_IsBackingUp);
  750. WRITE_MICRO_CHUNK (csave, VARID_IS_BACKUP_LOCKED, m_IsBackupLocked);
  751. WRITE_MICRO_CHUNK (csave, VARID_TURN_OFF_ENGINE, m_TurnOffEngineWhenDone);
  752. WRITE_MICRO_CHUNK (csave, VARID_PATH_PTR, m_CurrentPath);
  753. WRITE_MICRO_CHUNK (csave, VARID_GAME_OBJ_PTR, m_GameObj);
  754. WRITE_MICRO_CHUNK (csave, VARID_ARRIVED_DIST, m_ArrivedDist);
  755. csave.End_Chunk ();
  756. return ;
  757. }
  758. ////////////////////////////////////////////////////////////////////////////////////////////
  759. //
  760. // Load
  761. //
  762. ////////////////////////////////////////////////////////////////////////////////////////////
  763. void
  764. VehicleDriverClass::Load (ChunkLoadClass &cload)
  765. {
  766. while (cload.Open_Chunk ()) {
  767. switch (cload.Cur_Chunk_ID ()) {
  768. case CHUNKID_VARIABLES:
  769. Load_Variables (cload);
  770. break;
  771. }
  772. cload.Close_Chunk ();
  773. }
  774. return ;
  775. }
  776. ///////////////////////////////////////////////////////////////////////
  777. //
  778. // Load_Variables
  779. //
  780. ///////////////////////////////////////////////////////////////////////
  781. void
  782. VehicleDriverClass::Load_Variables (ChunkLoadClass &cload)
  783. {
  784. //
  785. // Loop through all the microchunks that define the variables
  786. //
  787. while (cload.Open_Micro_Chunk ()) {
  788. switch (cload.Cur_Micro_Chunk_ID ()) {
  789. READ_MICRO_CHUNK (cload, VARID_CURRENT_DEST, m_CurrentDest);
  790. READ_MICRO_CHUNK (cload, VARID_FINAL_DEST, m_FinalDest);
  791. READ_MICRO_CHUNK (cload, VARID_MAX_SPEED, m_MaxSpeed);
  792. READ_MICRO_CHUNK (cload, VARID_SPEED_FACTOR, m_SpeedFactor);
  793. READ_MICRO_CHUNK (cload, VARID_LAST_POS, m_LastPos);
  794. READ_MICRO_CHUNK (cload, VARID_BAD_PROGRESS_COUNT, m_BadProgressCount);
  795. READ_MICRO_CHUNK (cload, VARID_IS_BACKING_UP, m_IsBackingUp);
  796. READ_MICRO_CHUNK (cload, VARID_IS_BACKUP_LOCKED, m_IsBackupLocked);
  797. READ_MICRO_CHUNK (cload, VARID_TURN_OFF_ENGINE, m_TurnOffEngineWhenDone);
  798. READ_MICRO_CHUNK (cload, VARID_PATH_PTR, m_CurrentPath);
  799. READ_MICRO_CHUNK (cload, VARID_GAME_OBJ_PTR, m_GameObj);
  800. READ_MICRO_CHUNK (cload, VARID_ARRIVED_DIST, m_ArrivedDist);
  801. }
  802. cload.Close_Micro_Chunk ();
  803. }
  804. //
  805. // Request that the game object ptr gets remapped
  806. //
  807. if (m_GameObj != NULL) {
  808. REQUEST_POINTER_REMAP ((void **)&m_GameObj);
  809. }
  810. //
  811. // Request that the path ptr gets remapped
  812. //
  813. if (m_CurrentPath != NULL) {
  814. REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&m_CurrentPath);
  815. }
  816. return ;
  817. }
  818. ///////////////////////////////////////////////////////////////////////
  819. //
  820. // Reset
  821. //
  822. ///////////////////////////////////////////////////////////////////////
  823. void
  824. VehicleDriverClass::Reset (void)
  825. {
  826. m_CurrentPath = NULL;
  827. m_GameObj = NULL;
  828. m_BrakingDist = 0;
  829. m_LastFrameExpectedVelocity = 0;
  830. m_TurnOffEngineWhenDone = false;
  831. m_BadProgressCount = 0;
  832. return ;
  833. }