pilot.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  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/pilot.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 11/26/01 5:07p $*
  29. * *
  30. * $Revision:: 18 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "pilot.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 "heightdb.h"
  45. #include "debug.h"
  46. ////////////////////////////////////////////////////////////////
  47. // Save/Load constants
  48. ////////////////////////////////////////////////////////////////
  49. enum
  50. {
  51. CHUNKID_VARIABLES = 0x11040504,
  52. };
  53. enum
  54. {
  55. VARID_FINAL_DEST = 1,
  56. VARID_NEXT_POINT,
  57. VARID_CURRENT_TM,
  58. VARID_OBJ_SPACE_DEST,
  59. VARID_OBJ_SPACE_WPOINT,
  60. VARID_GAMEOBJ_PTR,
  61. VARID_MAX_SPEED,
  62. VARID_SPEED_FACTOR,
  63. VARID_AGGRESSIVENESS,
  64. VARID_ARRIVED_DIST,
  65. VARID_HOVERDIST,
  66. VARID_FACE_TARGET,
  67. VARID_TARGET_LOCATION,
  68. VARID_MODE,
  69. VARID_FORWARD_SPEED,
  70. VARID_STRAFE_SPEED,
  71. VARID_LIFT_SPEED,
  72. VARID_TURN_SHARPNESS,
  73. VARID_CIRCLE_ANGLE,
  74. VARID_CIRCLE_DIST,
  75. VARID_MIN_CIRCLE_ANGLE,
  76. VARID_MAX_CIRCLE_ANGLE,
  77. VARID_ISEXACT_Z_IMPORT,
  78. VARID_PATH_PTR
  79. };
  80. ////////////////////////////////////////////////////////////////
  81. // Constants
  82. ////////////////////////////////////////////////////////////////
  83. const float MAX_HEIGHT_DELTA = 2.5F;
  84. const float MAX_STRAFE_DELTA = 10.0F;
  85. const float MAX_FORWARD_DELTA = 10.0F;
  86. ////////////////////////////////////////////////////////////////
  87. // Clamp_Angle
  88. ////////////////////////////////////////////////////////////////
  89. inline float
  90. Clamp_Angle (float angle, float min_angle, float max_angle)
  91. {
  92. //
  93. // Make sure all the parameters are in the same range
  94. //
  95. angle = WWMath::Wrap (angle, 0, DEG_TO_RADF (360));
  96. min_angle = WWMath::Wrap (min_angle, 0, DEG_TO_RADF (360));
  97. max_angle = WWMath::Wrap (max_angle, 0, DEG_TO_RADF (360));
  98. float result = angle;
  99. if (min_angle <= max_angle) {
  100. //
  101. // Handle the 'typical' case where there is no 360-mark wrap-around
  102. //
  103. if (angle < min_angle) {
  104. result = min_angle;
  105. } else if (angle > max_angle) {
  106. result = max_angle;
  107. }
  108. } else {
  109. //
  110. // Handle the 360-mark wrap-around case
  111. //
  112. if (angle < min_angle && angle > max_angle) {
  113. float min_delta = min_angle - angle;
  114. float max_delta = angle - max_angle;
  115. //
  116. // Which edge is the angle closer to?
  117. //
  118. if (min_delta < max_delta) {
  119. result = min_angle;
  120. } else {
  121. result = max_angle;
  122. }
  123. }
  124. }
  125. return result;
  126. }
  127. ////////////////////////////////////////////////////////////////
  128. //
  129. // PilotClass
  130. //
  131. ////////////////////////////////////////////////////////////////
  132. PilotClass::PilotClass (void)
  133. : m_FinalDest (0, 0, 0),
  134. m_NextPoint (0, 0, 0),
  135. m_GameObj (NULL),
  136. m_MaxSpeed (20.0F),
  137. m_SpeedFactor (1.0F),
  138. m_Aggressiveness (0.25F),
  139. m_ArrivedDist (0),
  140. m_HoverDist (15.0F),
  141. m_FaceTarget (false),
  142. m_TargetLocation (0, 0, 0),
  143. m_Mode (MODE_HOVER),
  144. m_ForwardSpeed (0),
  145. m_StrafeSpeed (0),
  146. m_LiftSpeed (0),
  147. m_TurnSharpness (0),
  148. m_CurrentPath (NULL),
  149. m_CircleAngle (0),
  150. m_CircleDist (0),
  151. m_MinCircleAngle (-DEG_TO_RADF(180)),
  152. m_MaxCircleAngle (DEG_TO_RADF(180)),
  153. m_IsExactZImportant (false)
  154. {
  155. return ;
  156. }
  157. ////////////////////////////////////////////////////////////////
  158. //
  159. // Get_Object_Space_Velocity
  160. //
  161. // Returns the object-space velocity vector
  162. //
  163. ////////////////////////////////////////////////////////////////
  164. void
  165. PilotClass::Get_Object_Space_Velocity (Vector3 &vel_vector) const
  166. {
  167. //
  168. // Get a pointer to the physics object for this vehicle
  169. //
  170. MoveablePhysClass *phys_obj = m_GameObj->Peek_Physical_Object()->As_MoveablePhysClass ();
  171. WWASSERT (phys_obj != NULL);
  172. //
  173. // Get the world-space velocity vector for this vehicle and transform
  174. // it into object space.
  175. //
  176. Vector3 vel_vector_world;
  177. phys_obj->Get_Velocity (&vel_vector_world);
  178. Matrix3D::Inverse_Rotate_Vector (m_GameObj->Get_Transform (), vel_vector_world, &vel_vector);
  179. return ;
  180. }
  181. ////////////////////////////////////////////////////////////////
  182. //
  183. // Set_Target
  184. //
  185. ////////////////////////////////////////////////////////////////
  186. void
  187. PilotClass::Set_Target (const Vector3 *target)
  188. {
  189. if (target != NULL) {
  190. m_TargetLocation = *target;
  191. if (m_Mode != MODE_CIRCLE_POINT) {
  192. m_FaceTarget = true;
  193. }
  194. } else {
  195. m_FaceTarget = false;
  196. }
  197. return ;
  198. }
  199. ////////////////////////////////////////////////////////////////
  200. //
  201. // Initialize
  202. //
  203. ////////////////////////////////////////////////////////////////
  204. void
  205. PilotClass::Initialize (SmartGameObj *game_obj)
  206. {
  207. m_GameObj = game_obj;
  208. m_CurrentPath = NULL;
  209. //
  210. // If the game object is flying a vehicle, then get the vehicle instead
  211. //
  212. SoldierGameObj *soldier_game_obj = game_obj->As_SoldierGameObj ();
  213. if (soldier_game_obj != NULL) {
  214. VehicleGameObj *vehicle_game_obj = soldier_game_obj->Get_Profile_Vehicle ();
  215. if (vehicle_game_obj != NULL) {
  216. m_GameObj = vehicle_game_obj;
  217. }
  218. }
  219. //
  220. // By default assume our final destination is our current location
  221. //
  222. game_obj->Get_Position (&m_FinalDest);
  223. game_obj->Get_Position (&m_NextPoint);
  224. //
  225. // Determine if this game object is a vehicle or not (it better be...)
  226. //
  227. VehicleGameObj *vehicle = m_GameObj->As_VehicleGameObj ();
  228. if (vehicle != NULL) {
  229. //
  230. // Start this vehicle's engine's running
  231. //
  232. if (vehicle->Is_Engine_Enabled () == false) {
  233. vehicle->Enable_Engine (true);
  234. }
  235. }
  236. return ;
  237. }
  238. ////////////////////////////////////////////////////////////////
  239. //
  240. // Calculate_Desired_Relative_Facing
  241. //
  242. ////////////////////////////////////////////////////////////////
  243. float
  244. PilotClass::Calculate_Desired_Relative_Facing (void)
  245. {
  246. float base_angle = 0;
  247. //
  248. // Should we be facing the destination or the current target?
  249. //
  250. if (m_FaceTarget) {
  251. //
  252. // Convert the target location from world to object space
  253. //
  254. Vector3 target_pos;
  255. Matrix3D::Inverse_Transform_Vector (m_CurrentTM, m_TargetLocation, &target_pos);
  256. //
  257. // Calculate the absolute turn angle
  258. //
  259. base_angle = WWMath::Atan2 (target_pos.Y, target_pos.X);
  260. base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
  261. } else if (m_Mode != MODE_HOVER) {
  262. //
  263. // Calculate the absolute turn angle
  264. //
  265. base_angle = WWMath::Atan2 (m_ObjSpaceWaypoint.Y, m_ObjSpaceWaypoint.X);
  266. base_angle = WWMath::Wrap (base_angle, 0, WWMATH_PI * 2);
  267. }
  268. //
  269. // Convert the angle from [0, 360] to [-180, 180]
  270. //
  271. return WWMath::Wrap (base_angle, -WWMATH_PI, WWMATH_PI);
  272. }
  273. ////////////////////////////////////////////////////////////////
  274. //
  275. // Think
  276. //
  277. ////////////////////////////////////////////////////////////////
  278. bool
  279. PilotClass::Think (void)
  280. {
  281. //
  282. // Update the current
  283. //
  284. if (m_CurrentPath != NULL) {
  285. m_FinalDest = m_CurrentPath->Get_Dest_Pos ();
  286. m_NextPoint = m_CurrentPath->Get_Next_Pos ();
  287. }
  288. //
  289. // Let each mode think independently
  290. //
  291. switch (m_Mode)
  292. {
  293. case MODE_HOVER:
  294. Process_Hover ();
  295. break;
  296. case MODE_FLY_TO_POINT:
  297. Process_Fly_To_Point ();
  298. break;
  299. case MODE_CIRCLE_POINT:
  300. Process_Circle_Point ();
  301. break;
  302. }
  303. //
  304. // Pass the current controls onto the game object
  305. //
  306. Apply_Controls ();
  307. //
  308. // Return true when we've arrived at the destination
  309. //
  310. return Has_Arrived ();
  311. }
  312. ////////////////////////////////////////////////////////////////
  313. //
  314. // Calculate_Strafe_Speed
  315. //
  316. ////////////////////////////////////////////////////////////////
  317. void
  318. PilotClass::Calculate_Strafe_Speed (void)
  319. {
  320. m_StrafeSpeed = 0;
  321. bool should_strafe = m_FaceTarget;
  322. if (should_strafe) {
  323. m_StrafeSpeed = m_ObjSpaceWaypoint.Y / MAX_STRAFE_DELTA;
  324. m_StrafeSpeed = WWMath::Clamp (m_StrafeSpeed, -1.0F, 1.0F);
  325. }
  326. return ;
  327. }
  328. ////////////////////////////////////////////////////////////////
  329. //
  330. // Calculate_Forward_Speed
  331. //
  332. ////////////////////////////////////////////////////////////////
  333. void
  334. PilotClass::Calculate_Forward_Speed (float distance)
  335. {
  336. m_ForwardSpeed = 0;
  337. //
  338. // Pick a speed based on our distance from the destination
  339. //
  340. m_ForwardSpeed = distance / MAX_FORWARD_DELTA;
  341. m_ForwardSpeed = WWMath::Clamp (m_ForwardSpeed, -1.0F, 1.0F);
  342. return ;
  343. }
  344. ////////////////////////////////////////////////////////////////
  345. //
  346. // Calculate_Lift_Speed
  347. //
  348. ////////////////////////////////////////////////////////////////
  349. void
  350. PilotClass::Calculate_Lift_Speed (void)
  351. {
  352. //bool is_exact_height_important = (m_NextPoint != m_FinalDest);
  353. //
  354. // Determine what z position we should aim for
  355. //
  356. float preferred_height = m_NextPoint.Z;
  357. float flight_floor = -5000.0F;
  358. if (m_IsExactZImportant == false) {
  359. flight_floor = Determine_Preferred_Height ();
  360. preferred_height = flight_floor;
  361. if (preferred_height < m_NextPoint.Z) {
  362. preferred_height = m_NextPoint.Z;
  363. }
  364. } else {
  365. preferred_height = m_NextPoint.Z;
  366. }
  367. //
  368. // Get the vehicle's current position
  369. //
  370. Vector3 curr_pos;
  371. m_GameObj->Get_Position (&curr_pos);
  372. //
  373. // Calculate the lift speed based on the change in altitude
  374. // Once the height delta is within MAX_HIEGHT_DELTA, we set the desired velocity to zero
  375. // and try to let the hovering code maintain the desired altitude.
  376. //
  377. float delta_z = preferred_height - curr_pos.Z;
  378. float abs_delta_z = WWMath::Fabs(delta_z);
  379. if (m_Mode != MODE_HOVER || abs_delta_z > MAX_HEIGHT_DELTA) {
  380. m_LiftSpeed = delta_z / MAX_HEIGHT_DELTA;
  381. m_LiftSpeed = WWMath::Clamp (m_LiftSpeed, -1.0F, 1.0F);
  382. } else {
  383. m_LiftSpeed = 0.0f;
  384. }
  385. //
  386. // Keep the vehicle from 'sinking' sharply
  387. //
  388. if ( m_IsExactZImportant == false &&
  389. m_Mode == MODE_FLY_TO_POINT &&
  390. delta_z < 0)
  391. {
  392. m_LiftSpeed = m_LiftSpeed * 0.25f;
  393. }
  394. //
  395. // Slow down our forward speed to give us time to change altitude (if necessary)
  396. //
  397. if (curr_pos.Z < flight_floor) {
  398. //float new_speed = WWMath::Clamp (1.0F - m_LiftSpeed, 0.5F, 1.0F);
  399. //m_ForwardSpeed = min (m_ForwardSpeed, new_speed);
  400. }
  401. return ;
  402. }
  403. ////////////////////////////////////////////////////////////////
  404. //
  405. // Calculate_Turn_Sharpness
  406. //
  407. ////////////////////////////////////////////////////////////////
  408. void
  409. PilotClass::Calculate_Turn_Sharpness (void)
  410. {
  411. float facing_angle = Calculate_Desired_Relative_Facing ();
  412. //
  413. // Calculate how sharp we should turn to make this facing
  414. //
  415. m_TurnSharpness = facing_angle / DEG_TO_RADF (45);
  416. m_TurnSharpness = WWMath::Clamp (m_TurnSharpness, -1.0F, 1.0F);
  417. //
  418. // If the vehicle needs to face its target, then make it turn faster then normal
  419. //
  420. if (m_FaceTarget || m_Mode == MODE_CIRCLE_POINT) {
  421. m_TurnSharpness *= 2.0F;
  422. m_TurnSharpness = min (m_TurnSharpness, 1.0F);
  423. }
  424. return ;
  425. }
  426. ////////////////////////////////////////////////////////////////
  427. //
  428. // Process_Hover
  429. //
  430. ////////////////////////////////////////////////////////////////
  431. void
  432. PilotClass::Process_Hover (void)
  433. {
  434. //
  435. // Update the "simplified" transform
  436. //
  437. Update_Transform ();
  438. //
  439. // When hovering, we want to maintain a zero horizontal velocity
  440. // while moving towards our desired altitude
  441. //
  442. m_ForwardSpeed = 0.0f;
  443. m_StrafeSpeed = 0.0f;
  444. //
  445. // Update the "simplified" transform
  446. //
  447. Update_Transform ();
  448. //
  449. // When hovering, we want to maintain a zero horizontal velocity
  450. // while moving towards our desired altitude
  451. //
  452. m_ForwardSpeed = 0.0f;
  453. m_StrafeSpeed = 0.0f;
  454. //
  455. // When hovering its valid to face a target, so update
  456. // the vehicle's facing
  457. //
  458. if (m_FaceTarget) {
  459. Calculate_Turn_Sharpness ();
  460. }
  461. return ;
  462. }
  463. ////////////////////////////////////////////////////////////////
  464. //
  465. // Process_Circle_Point
  466. //
  467. ////////////////////////////////////////////////////////////////
  468. void
  469. PilotClass::Process_Circle_Point (void)
  470. {
  471. //
  472. // Reset all control inputs
  473. //
  474. m_ForwardSpeed = 0;
  475. m_StrafeSpeed = 0;
  476. m_LiftSpeed = 0;
  477. //
  478. // When hovering its valid to face a target, so update
  479. // the vehicle's facing
  480. //
  481. Update_Transform ();
  482. //
  483. // Make sure we are facing the destination
  484. //
  485. Calculate_Turn_Sharpness ();
  486. //
  487. // If we are facing the destination, then we can
  488. // start our 'circling' movement
  489. //
  490. if (::fabs (m_TurnSharpness) < 0.1F) {
  491. //
  492. // Make sure the vehicle is the appropriate distance from the circle point
  493. //
  494. Calculate_Forward_Speed (m_ObjSpaceDest.X - m_CircleDist);
  495. Calculate_Lift_Speed ();
  496. //
  497. // Calculate what our current circling angle is
  498. //
  499. Vector3 curr_pos;
  500. m_GameObj->Get_Position (&curr_pos);
  501. Vector3 delta = curr_pos - m_FinalDest;
  502. float curr_angle = WWMath::Atan2 (delta.Y, delta.X);
  503. float angle_delta = WWMath::Wrap (curr_angle - m_CircleAngle, -WWMATH_PI, WWMATH_PI);
  504. //
  505. // Calculate how fast we should strafe in order to circle the target
  506. //
  507. m_StrafeSpeed = angle_delta / DEG_TO_RADF (5);
  508. m_StrafeSpeed = WWMath::Clamp (m_StrafeSpeed, -1.0F, 1.0F);
  509. }
  510. return ;
  511. }
  512. ////////////////////////////////////////////////////////////////
  513. //
  514. // Process_Fly_To_Point
  515. //
  516. ////////////////////////////////////////////////////////////////
  517. void
  518. PilotClass::Process_Fly_To_Point (void)
  519. {
  520. //
  521. // Get the vehicle's current transform and check to see if
  522. // the vehicle has arrived at its destination yet
  523. //
  524. Update_Transform ();
  525. Check_Completion ();
  526. //
  527. // Calculate how sharp the vehicle needs to turn
  528. //
  529. Calculate_Turn_Sharpness ();
  530. //
  531. // Calculate how fast the vehicle needs to move in each direction
  532. //
  533. Calculate_Strafe_Speed ();
  534. //
  535. // If we are on a path then we need to calculate the forward speed
  536. // as if we are going full speed the whole way there
  537. //
  538. bool is_on_path = (m_NextPoint != m_FinalDest);
  539. if (is_on_path) {
  540. //
  541. // Clamp the speed to -1, 0, or 1.
  542. //
  543. m_ForwardSpeed = m_ObjSpaceWaypoint.X;
  544. if (m_ForwardSpeed > 0) {
  545. m_ForwardSpeed = 1.0F;
  546. } else if (m_ForwardSpeed < 0) {
  547. m_ForwardSpeed = -1.0F;
  548. } else {
  549. m_ForwardSpeed = 0.0F;
  550. }
  551. //
  552. // Reduce forward/back speed if we are mostly strafing.
  553. //
  554. if (WWMath::Fabs (m_StrafeSpeed) > 0.25f) {
  555. m_ForwardSpeed = m_ForwardSpeed * (1.25F - WWMath::Fabs (m_StrafeSpeed));
  556. }
  557. //
  558. // Let the pilot slow down to handle sharp corners
  559. //
  560. if (WWMath::Fabs (m_TurnSharpness) > 0.65F) {
  561. m_ForwardSpeed = m_ForwardSpeed * (1.25F - WWMath::Fabs (m_TurnSharpness));
  562. }
  563. } else {
  564. Calculate_Forward_Speed (m_ObjSpaceDest.X);
  565. }
  566. Calculate_Lift_Speed ();
  567. return ;
  568. }
  569. ////////////////////////////////////////////////////////////////
  570. //
  571. // Check_Completion
  572. //
  573. ////////////////////////////////////////////////////////////////
  574. void
  575. PilotClass::Check_Completion (void)
  576. {
  577. //
  578. // Get the vehicle's bounding box
  579. //
  580. MoveablePhysClass *phys_obj = m_GameObj->Peek_Physical_Object ()->As_MoveablePhysClass ();
  581. RenderObjClass *render_obj = phys_obj->Peek_Model ();
  582. AABoxClass bounding_box;
  583. render_obj->Get_Obj_Space_Bounding_Box (bounding_box);
  584. //
  585. // Calculate the distance remaining on the path
  586. //
  587. float dist_to_goal = 0;
  588. if (m_CurrentPath != NULL) {
  589. float dist_on_path = m_CurrentPath->Get_Remaining_Path_Length ();
  590. dist_to_goal = (m_ObjSpaceWaypoint.Length () + dist_on_path);
  591. } else {
  592. dist_to_goal = m_ObjSpaceDest.Length ();
  593. }
  594. //
  595. // Take into account the 'arrived distance' setting. We basically fly towards the
  596. // closest point on a sphere around the destination point. m_ObjSpaceDest will
  597. // be initialized to that point transformed into the vehicle's coordinate system.
  598. //
  599. if (dist_to_goal > m_ArrivedDist * m_ArrivedDist) {
  600. Vector3 arrived_len = m_ObjSpaceDest;
  601. arrived_len.Normalize ();
  602. arrived_len *= m_ArrivedDist;
  603. m_ObjSpaceDest -= arrived_len;
  604. m_ObjSpaceDest += bounding_box.Center;
  605. }
  606. //
  607. // Once we come within m_HoverDist of the desired position we switch into "hover" mode.
  608. //
  609. if (dist_to_goal < m_HoverDist && m_NextPoint == m_FinalDest) {
  610. Vector2 xy_delta(m_ObjSpaceWaypoint.X, m_ObjSpaceWaypoint.Y);
  611. if (xy_delta.Length2() < m_HoverDist * m_HoverDist) {
  612. Set_Mode(MODE_HOVER);
  613. //
  614. // If we don't have a target, we need to create a fake target so that
  615. // the orientation of the vehicle doesn't drift.
  616. //
  617. if (m_FaceTarget == false) {
  618. Vector3 fake_target;
  619. Matrix3D::Transform_Vector(m_CurrentTM,Vector3(1000.0f,0.0f,0.0f),&fake_target);
  620. Set_Target(&fake_target);
  621. }
  622. }
  623. }
  624. return ;
  625. }
  626. ////////////////////////////////////////////////////////////////
  627. //
  628. // Update_Transform
  629. //
  630. ////////////////////////////////////////////////////////////////
  631. void
  632. PilotClass::Update_Transform (void)
  633. {
  634. Matrix3D obj_tm = m_GameObj->Get_Transform ();
  635. //
  636. // Build a transform that only uses the facing and position
  637. // of the vehicle.
  638. //
  639. m_CurrentTM.Make_Identity ();
  640. m_CurrentTM.Rotate_Z (obj_tm.Get_Z_Rotation ());
  641. m_CurrentTM.Set_Translation (obj_tm.Get_Translation ());
  642. //
  643. // Now convert the destination from world space to object space
  644. //
  645. Matrix3D::Inverse_Transform_Vector (m_CurrentTM, m_FinalDest, &m_ObjSpaceDest);
  646. Matrix3D::Inverse_Transform_Vector (m_CurrentTM, m_NextPoint, &m_ObjSpaceWaypoint);
  647. //
  648. // If its not important for the vehicle to tightly follow the Z value
  649. // of its points, then simply zero out the Z component
  650. //
  651. if (m_IsExactZImportant == false) {
  652. m_ObjSpaceWaypoint.Z = 0;
  653. }
  654. return ;
  655. }
  656. ////////////////////////////////////////////////////////////////
  657. //
  658. // Determine_Preferred_Height
  659. //
  660. ////////////////////////////////////////////////////////////////
  661. float
  662. PilotClass::Determine_Preferred_Height (void)
  663. {
  664. float height = m_NextPoint.Z;
  665. if (m_Mode != MODE_FLY_TO_POINT) {
  666. return height;
  667. }
  668. //
  669. // Get a pointer to the physics object for this vehicle
  670. //
  671. MoveablePhysClass *phys_obj = m_GameObj->Peek_Physical_Object()->As_MoveablePhysClass ();
  672. WWASSERT (phys_obj != NULL);
  673. //
  674. // Get the world-space velocity vector for this vehicle and transform
  675. // it into object space.
  676. //
  677. Vector3 vel_vector_world;
  678. phys_obj->Get_Velocity (&vel_vector_world);
  679. //
  680. // Try to determine where the aircraft will be in 1 and 2 seconds
  681. // from now.
  682. //
  683. Vector3 curr_pos;
  684. m_GameObj->Get_Position (&curr_pos);
  685. RenderObjClass *render_obj = phys_obj->Peek_Model ();
  686. const AABoxClass &bounding_box = render_obj->Get_Bounding_Box ();
  687. Vector3 curr_pos1 = curr_pos;
  688. Vector3 curr_pos2 = curr_pos + Vector3 (bounding_box.Extent.X, bounding_box.Extent.Y, 0);
  689. Vector3 curr_pos3 = curr_pos + Vector3 (-bounding_box.Extent.X, -bounding_box.Extent.Y, 0);
  690. Vector3 curr_pos4 = curr_pos + Vector3 (bounding_box.Extent.X, -bounding_box.Extent.Y, 0);
  691. Vector3 curr_pos5 = curr_pos + Vector3 (-bounding_box.Extent.X, bounding_box.Extent.Y, 0);
  692. Vector3 future_pos1 = curr_pos1 + (vel_vector_world * 1.25F);
  693. Vector3 future_pos2 = curr_pos2 + (vel_vector_world * 1.25F);
  694. Vector3 future_pos3 = curr_pos3 + (vel_vector_world * 1.25F);
  695. Vector3 future_pos4 = curr_pos4 + (vel_vector_world * 1.25F);
  696. Vector3 future_pos5 = curr_pos5 + (vel_vector_world * 1.25F);
  697. Vector3 future_pos6 = curr_pos1 + (vel_vector_world * 2.25F);
  698. Vector3 future_pos7 = curr_pos2 + (vel_vector_world * 2.25F);
  699. Vector3 future_pos8 = curr_pos3 + (vel_vector_world * 2.25F);
  700. Vector3 future_pos9 = curr_pos4 + (vel_vector_world * 2.25F);
  701. Vector3 future_pos10 = curr_pos5 + (vel_vector_world * 2.25F);
  702. future_pos1.Z = curr_pos.Z;
  703. future_pos2.Z = curr_pos.Z;
  704. future_pos3.Z = curr_pos.Z;
  705. future_pos4.Z = curr_pos.Z;
  706. future_pos5.Z = curr_pos.Z;
  707. future_pos6.Z = curr_pos.Z;
  708. future_pos7.Z = curr_pos.Z;
  709. future_pos8.Z = curr_pos.Z;
  710. future_pos9.Z = curr_pos.Z;
  711. future_pos10.Z = curr_pos.Z;
  712. //
  713. // Now, lookup the preferred height for these future positions
  714. //
  715. float height1 = HeightDBClass::Get_Height (future_pos1);
  716. float height2 = HeightDBClass::Get_Height (future_pos2);
  717. float height3 = HeightDBClass::Get_Height (future_pos3);
  718. float height4 = HeightDBClass::Get_Height (future_pos4);
  719. float height5 = HeightDBClass::Get_Height (future_pos5);
  720. float height6 = HeightDBClass::Get_Height (future_pos6);
  721. float height7 = HeightDBClass::Get_Height (future_pos7);
  722. float height8 = HeightDBClass::Get_Height (future_pos8);
  723. float height9 = HeightDBClass::Get_Height (future_pos9);
  724. float height10 = HeightDBClass::Get_Height (future_pos10);
  725. //
  726. // Return the largest height to the caller
  727. //
  728. height = max (height1, height2);
  729. height = max (height, height3);
  730. height = max (height, height4);
  731. height = max (height, height5);
  732. height = max (height, height6);
  733. height = max (height, height7);
  734. height = max (height, height8);
  735. height = max (height, height9);
  736. height = max (height, height10);
  737. return height;
  738. }
  739. ////////////////////////////////////////////////////////////////
  740. //
  741. // Apply_Controls
  742. //
  743. ////////////////////////////////////////////////////////////////
  744. void
  745. PilotClass::Apply_Controls (void)
  746. {
  747. //
  748. // If the vehicle has a driver, then pass the controls onto the driver
  749. // instead...
  750. //
  751. SmartGameObj *game_obj = m_GameObj;
  752. VehicleGameObj *vehicle = m_GameObj->As_VehicleGameObj ();
  753. if (vehicle != NULL) {
  754. if (vehicle->Get_Driver () != NULL) {
  755. //game_obj = vehicle->Get_Driver ();
  756. }
  757. }
  758. //
  759. // Calculate our (current) normalized speeds
  760. //
  761. Vector3 vel_vector;
  762. Get_Object_Space_Velocity (vel_vector);
  763. float curr_forward_speed = WWMath::Clamp ((vel_vector.X / m_MaxSpeed), -1.0F, 1.0F);
  764. float curr_strafe_speed = WWMath::Clamp ((vel_vector.Y / m_MaxSpeed), -1.0F, 1.0F);
  765. float curr_lift_speed = WWMath::Clamp ((vel_vector.Z / m_MaxSpeed), -1.0F, 1.0F);
  766. //
  767. // Calculate how much we need to accelerate or decelerate in the three directions
  768. // in order to meet our target speeds
  769. //
  770. float forward_accel = 0.0f;
  771. float strafe_accel = 0.0f;
  772. float lift_accel = 0.0f;
  773. if (m_Mode == MODE_HOVER) {
  774. forward_accel = 5.5f * (m_ForwardSpeed - curr_forward_speed) + 0.25f * m_ObjSpaceDest.X;
  775. strafe_accel = 3.0f * (m_StrafeSpeed - curr_strafe_speed) + 0.5f * m_ObjSpaceDest.Y;
  776. lift_accel = 1.0f * (m_LiftSpeed - curr_lift_speed) + 0.5f * m_ObjSpaceDest.Z;
  777. forward_accel = WWMath::Clamp(forward_accel,-1.0f,1.0f);
  778. strafe_accel = WWMath::Clamp(strafe_accel,-1.0f,1.0f);
  779. lift_accel = WWMath::Clamp(lift_accel,-1.0f,1.0f);
  780. } else {
  781. forward_accel = WWMath::Clamp (5.0f * (m_ForwardSpeed - curr_forward_speed), -1.0F, 1.0F);
  782. strafe_accel = WWMath::Clamp (1.0f * (m_StrafeSpeed - curr_strafe_speed), -1.0F, 1.0F);
  783. lift_accel = WWMath::Clamp (3.0f * (m_LiftSpeed - curr_lift_speed), -1.0F, 1.0F);
  784. }
  785. //
  786. // Make the vehicle go forward/backward
  787. //
  788. game_obj->Set_Analog_Control (ControlClass::ANALOG_MOVE_FORWARD, forward_accel);
  789. //
  790. // Make the vehicle strafe left/right
  791. //
  792. game_obj->Set_Analog_Control (ControlClass::ANALOG_MOVE_LEFT, strafe_accel);
  793. //
  794. // Make the vehicle go up/down
  795. //
  796. game_obj->Set_Analog_Control (ControlClass::ANALOG_MOVE_UP, lift_accel);
  797. //
  798. // Make the vehicle turn
  799. //
  800. game_obj->Set_Analog_Control (ControlClass::ANALOG_TURN_LEFT, m_TurnSharpness);
  801. return ;
  802. }
  803. ////////////////////////////////////////////////////////////////
  804. //
  805. // Set_Mode
  806. //
  807. ////////////////////////////////////////////////////////////////
  808. void
  809. PilotClass::Set_Mode (PilotClass::MODE mode)
  810. {
  811. if (m_Mode != mode) {
  812. switch (mode)
  813. {
  814. case MODE_HOVER:
  815. break;
  816. case MODE_FLY_TO_POINT:
  817. break;
  818. case MODE_CIRCLE_POINT:
  819. {
  820. m_FaceTarget = false;
  821. //
  822. // Initialize the circling distance
  823. //
  824. Vector3 curr_pos;
  825. m_GameObj->Get_Position (&curr_pos);
  826. m_CircleDist = (curr_pos - m_FinalDest).Length ();
  827. }
  828. break;
  829. }
  830. //
  831. // When we change modes, we reset the target in case we had
  832. // a fake target from hover mode
  833. //
  834. Set_Target(NULL);
  835. m_Mode = mode;
  836. }
  837. return ;
  838. }
  839. ////////////////////////////////////////////////////////////////
  840. //
  841. // Set_Circle_Pos
  842. //
  843. ////////////////////////////////////////////////////////////////
  844. void
  845. PilotClass::Set_Circle_Pos (const Vector3 &pos)
  846. {
  847. Vector3 delta = pos - m_FinalDest;
  848. m_CircleAngle = WWMath::Atan2 (delta.Y, delta.X);
  849. m_CircleAngle = WWMath::Wrap (m_CircleAngle, 0, DEG_TO_RADF (360));
  850. m_CircleAngle = ::Clamp_Angle (m_CircleAngle, m_MinCircleAngle, m_MaxCircleAngle);
  851. return ;
  852. }
  853. ////////////////////////////////////////////////////////////////
  854. //
  855. // Set_Circle_Angle
  856. //
  857. ////////////////////////////////////////////////////////////////
  858. void
  859. PilotClass::Set_Circle_Angle (float angle)
  860. {
  861. m_CircleAngle = WWMath::Wrap (angle, 0, DEG_TO_RADF (360));
  862. m_CircleAngle = ::Clamp_Angle (m_CircleAngle, m_MinCircleAngle, m_MaxCircleAngle);
  863. return ;
  864. }
  865. ////////////////////////////////////////////////////////////////
  866. //
  867. // Set_Circle_Bounds
  868. //
  869. ////////////////////////////////////////////////////////////////
  870. void
  871. PilotClass::Set_Circle_Bounds (float min_angle, float max_angle)
  872. {
  873. m_MinCircleAngle = min_angle;
  874. m_MaxCircleAngle = max_angle;
  875. m_CircleAngle = ::Clamp_Angle (m_CircleAngle, m_MinCircleAngle, m_MaxCircleAngle);
  876. return ;
  877. }
  878. ////////////////////////////////////////////////////////////////
  879. //
  880. // Has_Arrived
  881. //
  882. ////////////////////////////////////////////////////////////////
  883. bool
  884. PilotClass::Has_Arrived (void) const
  885. {
  886. bool has_arrived = false;
  887. if (m_Mode == MODE_HOVER) {
  888. /*bool
  889. if (m_CurrentPath != NULL) {
  890. m_ObjSpaceDest.Length ()
  891. }*/
  892. //
  893. // In hovering mode, we have 'arrived' if we have mostly finished moving
  894. //
  895. Vector3 vel_vector;
  896. Get_Object_Space_Velocity (vel_vector);
  897. has_arrived = (WWMath::Fabs (vel_vector.X) < 0.5F &&
  898. WWMath::Fabs (vel_vector.Y) < 0.5F &&
  899. WWMath::Fabs (vel_vector.Z) < 0.5F);
  900. }
  901. return has_arrived;
  902. }
  903. ////////////////////////////////////////////////////////////////
  904. //
  905. // Get_Current_Circle_Angle
  906. //
  907. ////////////////////////////////////////////////////////////////
  908. float
  909. PilotClass::Get_Current_Circle_Angle (void) const
  910. {
  911. Vector3 curr_pos;
  912. m_GameObj->Get_Position (&curr_pos);
  913. Vector3 delta = curr_pos - m_FinalDest;
  914. return WWMath::Atan2 (delta.Y, delta.X);
  915. }
  916. ////////////////////////////////////////////////////////////////////////////////////////////
  917. //
  918. // Save
  919. //
  920. ////////////////////////////////////////////////////////////////////////////////////////////
  921. void
  922. PilotClass::Save (ChunkSaveClass &csave)
  923. {
  924. csave.Begin_Chunk (CHUNKID_VARIABLES);
  925. //
  926. // Save each variable to its own microchunk
  927. //
  928. WRITE_MICRO_CHUNK (csave, VARID_FINAL_DEST, m_FinalDest);
  929. WRITE_MICRO_CHUNK (csave, VARID_NEXT_POINT, m_NextPoint);
  930. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_TM, m_CurrentTM);
  931. WRITE_MICRO_CHUNK (csave, VARID_OBJ_SPACE_DEST, m_ObjSpaceDest);
  932. WRITE_MICRO_CHUNK (csave, VARID_OBJ_SPACE_WPOINT, m_ObjSpaceWaypoint);
  933. WRITE_MICRO_CHUNK (csave, VARID_PATH_PTR, m_CurrentPath);
  934. WRITE_MICRO_CHUNK (csave, VARID_GAMEOBJ_PTR, m_GameObj);
  935. WRITE_MICRO_CHUNK (csave, VARID_MAX_SPEED, m_MaxSpeed);
  936. WRITE_MICRO_CHUNK (csave, VARID_SPEED_FACTOR, m_SpeedFactor);
  937. WRITE_MICRO_CHUNK (csave, VARID_AGGRESSIVENESS, m_Aggressiveness);
  938. WRITE_MICRO_CHUNK (csave, VARID_ARRIVED_DIST, m_ArrivedDist);
  939. WRITE_MICRO_CHUNK (csave, VARID_HOVERDIST, m_HoverDist);
  940. WRITE_MICRO_CHUNK (csave, VARID_ISEXACT_Z_IMPORT, m_IsExactZImportant);
  941. WRITE_MICRO_CHUNK (csave, VARID_FACE_TARGET, m_FaceTarget);
  942. WRITE_MICRO_CHUNK (csave, VARID_TARGET_LOCATION, m_TargetLocation);
  943. WRITE_MICRO_CHUNK (csave, VARID_MODE, m_Mode);
  944. WRITE_MICRO_CHUNK (csave, VARID_FORWARD_SPEED, m_ForwardSpeed);
  945. WRITE_MICRO_CHUNK (csave, VARID_STRAFE_SPEED, m_StrafeSpeed);
  946. WRITE_MICRO_CHUNK (csave, VARID_LIFT_SPEED, m_LiftSpeed);
  947. WRITE_MICRO_CHUNK (csave, VARID_TURN_SHARPNESS, m_TurnSharpness);
  948. WRITE_MICRO_CHUNK (csave, VARID_CIRCLE_ANGLE, m_CircleAngle);
  949. WRITE_MICRO_CHUNK (csave, VARID_CIRCLE_DIST, m_CircleDist);
  950. WRITE_MICRO_CHUNK (csave, VARID_MIN_CIRCLE_ANGLE, m_MinCircleAngle);
  951. WRITE_MICRO_CHUNK (csave, VARID_MAX_CIRCLE_ANGLE, m_MaxCircleAngle);
  952. csave.End_Chunk ();
  953. return ;
  954. }
  955. ////////////////////////////////////////////////////////////////////////////////////////////
  956. //
  957. // Load
  958. //
  959. ////////////////////////////////////////////////////////////////////////////////////////////
  960. void
  961. PilotClass::Load (ChunkLoadClass &cload)
  962. {
  963. while (cload.Open_Chunk ()) {
  964. switch (cload.Cur_Chunk_ID ()) {
  965. case CHUNKID_VARIABLES:
  966. Load_Variables (cload);
  967. break;
  968. }
  969. cload.Close_Chunk ();
  970. }
  971. return ;
  972. }
  973. ///////////////////////////////////////////////////////////////////////
  974. //
  975. // Load_Variables
  976. //
  977. ///////////////////////////////////////////////////////////////////////
  978. void
  979. PilotClass::Load_Variables (ChunkLoadClass &cload)
  980. {
  981. //
  982. // Loop through all the microchunks that define the variables
  983. //
  984. while (cload.Open_Micro_Chunk ()) {
  985. switch (cload.Cur_Micro_Chunk_ID ()) {
  986. READ_MICRO_CHUNK (cload, VARID_FINAL_DEST, m_FinalDest);
  987. READ_MICRO_CHUNK (cload, VARID_NEXT_POINT, m_NextPoint);
  988. READ_MICRO_CHUNK (cload, VARID_CURRENT_TM, m_CurrentTM);
  989. READ_MICRO_CHUNK (cload, VARID_OBJ_SPACE_DEST, m_ObjSpaceDest);
  990. READ_MICRO_CHUNK (cload, VARID_OBJ_SPACE_WPOINT, m_ObjSpaceWaypoint);
  991. READ_MICRO_CHUNK (cload, VARID_PATH_PTR, m_CurrentPath);
  992. READ_MICRO_CHUNK (cload, VARID_GAMEOBJ_PTR, m_GameObj);
  993. READ_MICRO_CHUNK (cload, VARID_MAX_SPEED, m_MaxSpeed);
  994. READ_MICRO_CHUNK (cload, VARID_SPEED_FACTOR, m_SpeedFactor);
  995. READ_MICRO_CHUNK (cload, VARID_AGGRESSIVENESS, m_Aggressiveness);
  996. READ_MICRO_CHUNK (cload, VARID_ARRIVED_DIST, m_ArrivedDist);
  997. READ_MICRO_CHUNK (cload, VARID_HOVERDIST, m_HoverDist);
  998. READ_MICRO_CHUNK (cload, VARID_ISEXACT_Z_IMPORT, m_IsExactZImportant);
  999. READ_MICRO_CHUNK (cload, VARID_FACE_TARGET, m_FaceTarget);
  1000. READ_MICRO_CHUNK (cload, VARID_TARGET_LOCATION, m_TargetLocation);
  1001. READ_MICRO_CHUNK (cload, VARID_MODE, m_Mode);
  1002. READ_MICRO_CHUNK (cload, VARID_FORWARD_SPEED, m_ForwardSpeed);
  1003. READ_MICRO_CHUNK (cload, VARID_STRAFE_SPEED, m_StrafeSpeed);
  1004. READ_MICRO_CHUNK (cload, VARID_LIFT_SPEED, m_LiftSpeed);
  1005. READ_MICRO_CHUNK (cload, VARID_TURN_SHARPNESS, m_TurnSharpness);
  1006. READ_MICRO_CHUNK (cload, VARID_CIRCLE_ANGLE, m_CircleAngle);
  1007. READ_MICRO_CHUNK (cload, VARID_CIRCLE_DIST, m_CircleDist);
  1008. READ_MICRO_CHUNK (cload, VARID_MIN_CIRCLE_ANGLE, m_MinCircleAngle);
  1009. READ_MICRO_CHUNK (cload, VARID_MAX_CIRCLE_ANGLE, m_MaxCircleAngle);
  1010. }
  1011. cload.Close_Micro_Chunk ();
  1012. }
  1013. //
  1014. // Request that the game object ptr gets remapped
  1015. //
  1016. if (m_GameObj != NULL) {
  1017. REQUEST_POINTER_REMAP ((void **)&m_GameObj);
  1018. }
  1019. //
  1020. // Request that the path object ptr gets remapped
  1021. //
  1022. if (m_CurrentPath != NULL) {
  1023. REQUEST_POINTER_REMAP ((void **)&m_CurrentPath);
  1024. }
  1025. return ;
  1026. }
  1027. ///////////////////////////////////////////////////////////////////////
  1028. //
  1029. // Reset
  1030. //
  1031. ///////////////////////////////////////////////////////////////////////
  1032. void
  1033. PilotClass::Reset (void)
  1034. {
  1035. m_CurrentPath = NULL;
  1036. m_GameObj = NULL;
  1037. m_Mode = MODE_HOVER,
  1038. m_IsExactZImportant = false;
  1039. return ;
  1040. }