Path.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593
  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/wwphys/Path.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 3/20/02 6:52p $*
  29. * *
  30. * $Revision:: 52 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "path.h"
  36. #include <windows.h>
  37. #include "colmathaabox.h"
  38. #include "pathfind.h"
  39. #include "pathfindportal.h"
  40. #include "pathsolve.h"
  41. #include "pathdebugplotter.h"
  42. #include "cardinalspline.h"
  43. #include "pathobject.h"
  44. #include "vehiclecurve.h"
  45. #include "waypath.h"
  46. #include "waypoint.h"
  47. #include "chunkio.h"
  48. #include "persistfactory.h"
  49. #include "assetmgr.h"
  50. #include "wwmemlog.h"
  51. ////////////////////////////////////////////////////////////////
  52. // Save/Load constants
  53. ////////////////////////////////////////////////////////////////
  54. enum
  55. {
  56. CHUNKID_VARIABLES = 0x11040535,
  57. CHUNKID_SPLINE
  58. };
  59. enum
  60. {
  61. VARID_STATE = 1,
  62. VARID_TRAVERSAL_TYPE,
  63. VARID_START_POS,
  64. VARID_DEST_POS,
  65. VARID_EXPECTED_POS,
  66. VARID_LOOK_AHEAD_DIST,
  67. VARID_LOOK_AHEAD_TIME,
  68. VARID_MOVEMENT_RADIUS,
  69. VARID_SPLINE_TIME,
  70. VARID_VELOCITY,
  71. VARID_CURRENT_ACTION,
  72. VARID_MOVEMENT_DIRECTIONS,
  73. VARID_PATH_OBJECT,
  74. VARID_START_TIME,
  75. VARID_END_TIME,
  76. VARID_ISLOOPING,
  77. VARID_PATH_ACTION,
  78. VARID_OLD_PTR,
  79. VARID_TOTAL_DIST
  80. };
  81. ///////////////////////////////////////////////////////////////////////////
  82. // Local macros
  83. ///////////////////////////////////////////////////////////////////////////
  84. #define SAFE_DELETE(pobject) \
  85. if (pobject) { \
  86. delete pobject; \
  87. pobject = NULL; \
  88. } \
  89. ///////////////////////////////////////////////////////////////////////////
  90. // Constants
  91. ///////////////////////////////////////////////////////////////////////////
  92. static const float DEF_VEHICLE_VELOCITY = 40.0F;
  93. static const float DEF_HUMAN_VELOCITY = 2.5F; //5.0F;
  94. static const float ASSUMED_FPS = 30.0F; //15.0F;
  95. ///////////////////////////////////////////////////////////////////////////
  96. // Static member initialization
  97. ///////////////////////////////////////////////////////////////////////////
  98. bool PathClass::m_DisplayMoveVectors = false;
  99. static int _InstanceCount = 0;
  100. ///////////////////////////////////////////////////////////////////////////
  101. //
  102. // PathClass
  103. //
  104. ///////////////////////////////////////////////////////////////////////////
  105. PathClass::PathClass (void)
  106. : m_ExpectedPos (0, 0, 0),
  107. m_StartPos (0, 0, 0),
  108. m_DestPos (0, 0, 0),
  109. m_StartTime (0),
  110. m_EndTime (1.0F),
  111. m_LookAheadTime (0),
  112. m_LookAheadDist (0),
  113. m_SplineTime (0),
  114. m_State (ERROR_NOT_INITIALIZED),
  115. m_TraversalType (SPLINE),
  116. m_Velocity (DEF_VEHICLE_VELOCITY),
  117. m_Spline (NULL),
  118. m_MovementRadius (0.1F),
  119. m_MovementDirections (MOVE_X | MOVE_Y),
  120. m_CurrentAction (-1),
  121. m_IsLooping (false),
  122. m_TotalDist (0)
  123. {
  124. _InstanceCount ++;
  125. return ;
  126. }
  127. ///////////////////////////////////////////////////////////////////////////
  128. //
  129. // ~PathClass
  130. //
  131. ///////////////////////////////////////////////////////////////////////////
  132. PathClass::~PathClass (void)
  133. {
  134. _InstanceCount --;
  135. SAFE_DELETE (m_Spline);
  136. return ;
  137. }
  138. ///////////////////////////////////////////////////////////////////////////
  139. //
  140. // Initialize
  141. //
  142. ///////////////////////////////////////////////////////////////////////////
  143. void
  144. PathClass::Initialize (const Vector3 &start, const Vector3 &end)
  145. {
  146. m_State = STATE_TRAVERSING_PATH;
  147. m_SplineTime = 0;
  148. m_LookAheadTime = 0;
  149. m_TotalDist = 0;
  150. m_LookAheadDist = 0;
  151. m_CurrentAction = -1;
  152. m_StartTime = 0;
  153. m_EndTime = 1.0F;
  154. m_IsLooping = false;
  155. PATH_NODE node1;
  156. node1.time = 0;
  157. node1.next_time = 1.0F;
  158. node1.action_id = ACTION_NONE;
  159. node1.mechanism_id = 0;
  160. node1.tighten_spline = false;
  161. node1.pos = start;
  162. node1.dest_pos.Set (0, 0, 0);
  163. PATH_NODE node2;
  164. node2.time = 0;
  165. node2.next_time = 1.0F;
  166. node2.action_id = ACTION_NONE;
  167. node2.mechanism_id = 0;
  168. node2.tighten_spline = false;
  169. node2.pos = end;
  170. node2.dest_pos.Set (0, 0, 0);
  171. DynamicVectorClass<PATH_NODE> node_list;
  172. node_list.Add (node1);
  173. node_list.Add (node2);
  174. //
  175. // Convert the nodes into a spline and a set of action requests
  176. //
  177. Initialize_Spline (node_list);
  178. return ;
  179. }
  180. ///////////////////////////////////////////////////////////////////////////
  181. //
  182. // Initialize
  183. //
  184. ///////////////////////////////////////////////////////////////////////////
  185. void
  186. PathClass::Initialize (PathSolveClass &path_solve)
  187. {
  188. WWMEMLOG(MEM_PATHFIND);
  189. m_State = STATE_TRAVERSING_PATH;
  190. m_SplineTime = 0;
  191. m_LookAheadTime = 0;
  192. m_TotalDist = 0;
  193. m_LookAheadDist = 0;
  194. m_CurrentAction = -1;
  195. m_StartTime = 0;
  196. m_EndTime = 1.0F;
  197. m_IsLooping = false;
  198. DynamicVectorClass<PATH_NODE> node_list;
  199. //
  200. // Get the raw path data from the solver, and convert it into
  201. // a format we can digest
  202. //
  203. PathSolveClass::PATHPOINT_LIST &raw_path = path_solve.Get_Raw_Path ();
  204. for (int index = 0; index < raw_path.Count (); index ++) {
  205. //
  206. // Setup a path node that contains the information we
  207. // need from the path solver
  208. //
  209. PATH_NODE node;
  210. node.time = 0;
  211. node.next_time = 1.0F;
  212. node.action_id = ACTION_NONE;
  213. node.mechanism_id = 0;
  214. node.tighten_spline = false;
  215. node.pos = raw_path[index].m_Point;
  216. node.dest_pos.Set (0, 0, 0);
  217. //
  218. // Check for any action requirements
  219. //
  220. bool is_valid = true;
  221. PathfindPortalClass *portal = raw_path[index].m_Portal;
  222. if (portal != NULL) {
  223. //
  224. // See what kind of portal this is
  225. //
  226. PathfindActionPortalClass *action_portal = portal->As_PathfindActionPortalClass ();
  227. PathfindWaypathPortalClass *waypath_portal = portal->As_PathfindWaypathPortalClass ();
  228. if (action_portal != NULL) {
  229. //
  230. // Check to see if we are inadvertantly starting on an "exit" portal
  231. //
  232. if (index == 1 && action_portal->Get_Action_Type () == PathClass::ACTION_NONE) {
  233. //
  234. // Find the entrance portal
  235. //
  236. PathfindActionPortalClass *enter_portal = action_portal->Get_Enter_Portal ();
  237. if (enter_portal != NULL) {
  238. AABoxClass box;
  239. enter_portal->Get_Bounding_Box (box);
  240. //
  241. // Insert a node that will force the unit to do the entrance portal
  242. //
  243. PATH_NODE temp_node;
  244. temp_node.time = 0;
  245. temp_node.next_time = 1.0F;
  246. temp_node.tighten_spline = true;
  247. temp_node.pos = box.Center;
  248. temp_node.action_id = enter_portal->Get_Action_Type ();
  249. temp_node.dest_pos = enter_portal->Get_Destination ();
  250. temp_node.mechanism_id = enter_portal->Get_Mechanism_ID ();
  251. node_list.Add (temp_node);
  252. }
  253. }
  254. //
  255. // This is an action portal so set the action parameters
  256. //
  257. node.action_id = action_portal->Get_Action_Type ();
  258. node.dest_pos = action_portal->Get_Destination ();
  259. node.mechanism_id = action_portal->Get_Mechanism_ID ();
  260. node.tighten_spline = true;
  261. //
  262. // Override the positions so that its in the center
  263. // of the entrance portal. (This ensures guys won't
  264. // get stuck when the door opens).
  265. //
  266. AABoxClass box;
  267. action_portal->Get_Bounding_Box (box);
  268. node.pos = box.Center;
  269. } else if (waypath_portal != NULL) {
  270. is_valid = false;
  271. //
  272. // Lookup the next portal in the list (it should be the waypath exit portal)
  273. //
  274. if (index + 1 < raw_path.Count ()) {
  275. PathfindWaypathPortalClass *next_portal = raw_path[index + 1].m_Portal->As_PathfindWaypathPortalClass ();
  276. if (next_portal != NULL) {
  277. //
  278. // Create a temporary waypath segment from the portal information
  279. //
  280. const WaypathPositionClass &start_pos = waypath_portal->Get_Waypath_Pos ();
  281. const WaypathPositionClass &end_pos = next_portal->Get_Waypath_Pos ();
  282. WaypathClass *path_segment = new WaypathClass (start_pos, end_pos);
  283. //
  284. // Insert all the data from this waypath segment into our path
  285. //
  286. Add_Waypath_Data (node_list, path_segment, 0, path_segment->Get_Point_Count () - 1);
  287. REF_PTR_RELEASE (path_segment);
  288. //
  289. // Skip past the next portal
  290. //
  291. index ++;
  292. }
  293. }
  294. } else if (portal->Is_Two_Way_Portal () == false) {
  295. /*if (index < (raw_path.Count () - 1) && raw_path[index+1].m_Portal != NULL) {
  296. AABoxClass curr_box;
  297. AABoxClass next_box;
  298. portal->Get_Bounding_Box (curr_box);
  299. raw_path[index+1].m_Portal->Get_Bounding_Box (next_box);
  300. if (curr_box.Center.Z > (next_box.Center.Z + 1.5F)) {
  301. curr_box.Center.Z = 0;
  302. next_box.Center.Z = 0;
  303. float portal_dist = (curr_box.Center - next_box.Center).Length ();
  304. if (portal_dist < 665.0F) {
  305. //
  306. // Calculate an intermediate point that is one meter towards
  307. // the center of the next sector
  308. //
  309. Vector3 temp_delta = (raw_path[index+1].m_SectorCenter - next_box.Center);
  310. temp_delta.Normalize ();
  311. Vector3 intermediate_point = next_box.Center + temp_delta;
  312. //
  313. // Insert a node that will force the unit to jump down
  314. //
  315. PATH_NODE temp_node;
  316. temp_node.time = 0;
  317. temp_node.next_time = 1.0F;
  318. temp_node.tighten_spline = true;
  319. temp_node.pos = intermediate_point;
  320. temp_node.action_id = ACTION_JUMP;
  321. temp_node.mechanism_id = 0;
  322. temp_node.dest_pos.Set (intermediate_point);
  323. node_list.Add (temp_node);
  324. }
  325. }
  326. }*/
  327. }
  328. }
  329. if (is_valid) {
  330. node_list.Add (node);
  331. }
  332. }
  333. //
  334. // Convert the nodes into a spline and a set of action requests
  335. //
  336. Initialize_Spline (node_list);
  337. //
  338. // Clip the spline to the sectors and portals that the solver knows is safe
  339. //
  340. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE) == false) {
  341. Clip_Spline_To_Pathfind_Data (node_list, path_solve);
  342. }
  343. return ;
  344. }
  345. ///////////////////////////////////////////////////////////////////////////
  346. //
  347. // Add_Waypoint_Info_To_Node_List
  348. //
  349. ///////////////////////////////////////////////////////////////////////////
  350. void
  351. PathClass::Add_Waypoint_Info_To_Node_List
  352. (
  353. DynamicVectorClass<PathClass::PATH_NODE> & node_list,
  354. WaypointClass * waypoint,
  355. WaypointClass * next_point
  356. )
  357. {
  358. //
  359. // Fill in a path node structure for this waypoint
  360. //
  361. PathClass::PATH_NODE node;
  362. node.time = 0;
  363. node.next_time = 1.0F;
  364. node.action_id = ACTION_NONE;
  365. node.mechanism_id = 0;
  366. node.dest_pos.Set (0, 0, 0);
  367. node.pos = waypoint->Get_Position ();
  368. //
  369. // Check to see if there are any special actions to perform at
  370. // this waypoint
  371. //
  372. if (waypoint->Get_Flag (WaypointClass::FLAG_REQUIRES_JUMP)) {
  373. node.action_id = ACTION_JUMP;
  374. node.tighten_spline = true;
  375. //
  376. // Record the jump-to point (if there is one)
  377. //
  378. if (next_point != NULL) {
  379. node.dest_pos = next_point->Get_Position ();
  380. }
  381. } else if (waypoint->Get_Flag (WaypointClass::FLAG_REQUIRES_ACTION)) {
  382. //
  383. // Lookup the action portal from the waypoint
  384. //
  385. PathfindActionPortalClass *action_portal = waypoint->Get_Action_Portal ();
  386. if (action_portal != NULL) {
  387. //
  388. // Configure the node's action parameters
  389. //
  390. node.action_id = action_portal->Get_Action_Type ();
  391. node.dest_pos = action_portal->Get_Destination ();
  392. node.mechanism_id = action_portal->Get_Mechanism_ID ();
  393. node.tighten_spline = true;
  394. //
  395. // Override the positions so that its in the center
  396. // of the entrance portal. (This ensures guys won't
  397. // get stuck when the door opens).
  398. //
  399. AABoxClass box;
  400. action_portal->Get_Bounding_Box (box);
  401. node.pos = box.Center;
  402. }
  403. }
  404. //
  405. // Add the node to the list
  406. //
  407. node_list.Add (node);
  408. return ;
  409. }
  410. ///////////////////////////////////////////////////////////////////////////
  411. //
  412. // Add_Waypath_Data
  413. //
  414. ///////////////////////////////////////////////////////////////////////////
  415. void
  416. PathClass::Add_Waypath_Data
  417. (
  418. DynamicVectorClass<PATH_NODE> & node_list,
  419. WaypathClass * waypath,
  420. int start_index,
  421. int end_index
  422. )
  423. {
  424. //
  425. // If this is a looping path, then add the last waypoint first, so we can
  426. // wrap the spline from "last_point-1" all the way through "first_point+1".
  427. // If we don't do this, the spline will not look continuous.
  428. //
  429. if (m_IsLooping) {
  430. Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (end_index),
  431. waypath->Get_Point (start_index));
  432. }
  433. //
  434. // Loop along the waypath either forwards or backwards, depending
  435. // on what the caller has requested
  436. //
  437. int count = WWMath::Fabs (end_index - start_index);
  438. int inc = (end_index > start_index) ? 1 : -1;
  439. for (int index = start_index; count >= 0; index += inc, count --) {
  440. WaypointClass *waypoint = waypath->Get_Point (index);
  441. WaypointClass *next_point = waypath->Get_Point (index + inc);
  442. //
  443. // Add data from each waypoint into our path node list
  444. //
  445. if ((waypoint == NULL || next_point == NULL) || waypoint->Get_Position () != next_point->Get_Position ()) {
  446. Add_Waypoint_Info_To_Node_List (node_list, waypoint, next_point);
  447. }
  448. }
  449. //
  450. // If this is a looping path, then add the first and second waypoints again.
  451. // This is done so we can wrap the spline from "last_point-1" all the way
  452. // through "first_point+1". If we don't do this, the spline will not look
  453. // continuous.
  454. //
  455. if (m_IsLooping) {
  456. Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (start_index),
  457. waypath->Get_Point (start_index + inc));
  458. Add_Waypoint_Info_To_Node_List (node_list, waypath->Get_Point (start_index + inc),
  459. waypath->Get_Point (start_index + inc + inc));
  460. }
  461. return ;
  462. }
  463. ///////////////////////////////////////////////////////////////////////////
  464. //
  465. // Initialize
  466. //
  467. ///////////////////////////////////////////////////////////////////////////
  468. void
  469. PathClass::Initialize (WaypathClass *waypath, int start_pt_id, int end_pt_id)
  470. {
  471. WWMEMLOG(MEM_PATHFIND);
  472. m_State = ERROR_NOT_INITIALIZED;
  473. m_SplineTime = 0;
  474. m_LookAheadTime = 0;
  475. m_LookAheadDist = 0;
  476. m_CurrentAction = -1;
  477. m_StartTime = 0;
  478. m_EndTime = 1.0F;
  479. m_IsLooping = false;
  480. //
  481. // Initialize the path from this waypath
  482. //
  483. if (waypath != NULL) {
  484. //
  485. // Determine if this is a looping path or not
  486. //
  487. m_IsLooping = waypath->Get_Flag (WaypathClass::FLAG_LOOPING);
  488. //
  489. // Get the number of waypoints on this path
  490. //
  491. int count = waypath->Get_Point_Count ();
  492. if (count > 0) {
  493. //
  494. // Determine the starting and ending indicies
  495. //
  496. int start_index = 0;
  497. int end_index = count - 1;
  498. for (int index = 0; index < count; index ++) {
  499. if (waypath->Get_Point (index)->Get_ID () == start_pt_id) {
  500. start_index = index;
  501. }
  502. if (waypath->Get_Point (index)->Get_ID () == end_pt_id) {
  503. end_index = index;
  504. }
  505. }
  506. //
  507. // Initialize our first position
  508. //
  509. m_ExpectedPos = waypath->Get_Point (start_index)->Get_Position ();
  510. m_State = STATE_TRAVERSING_PATH;
  511. //
  512. // Add the waypath data to our node list
  513. //
  514. DynamicVectorClass<PATH_NODE> node_list;
  515. Add_Waypath_Data (node_list, waypath, start_index, end_index);
  516. //
  517. // Now create a spline from the waypath data
  518. //
  519. Initialize_Spline (node_list);
  520. }
  521. }
  522. return ;
  523. }
  524. ///////////////////////////////////////////////////////////////////////////
  525. //
  526. // Evaluate_Next_Point
  527. //
  528. ///////////////////////////////////////////////////////////////////////////
  529. bool
  530. PathClass::Evaluate_Next_Point (const Vector3 &curr_pos, Vector3 &new_pos)
  531. {
  532. //
  533. // Be careful not to evaluate when we are in a invalid state
  534. //
  535. if (m_State >= FIRST_ERROR) {
  536. new_pos = curr_pos;
  537. return false;
  538. } else if (m_State == STATE_PATH_COMPLETE) {
  539. new_pos = m_ExpectedPos;
  540. return true;
  541. }
  542. //
  543. // Get the delta from our current position to the point where we
  544. // should be heading
  545. //
  546. Vector3 delta = m_ExpectedPos - curr_pos;
  547. //
  548. // Zero out any directions we aren't moving in
  549. //
  550. if ((m_MovementDirections & MOVE_X) == 0) {
  551. delta.X = 0;
  552. }
  553. if ((m_MovementDirections & MOVE_Y) == 0) {
  554. delta.Y = 0;
  555. }
  556. if ((m_MovementDirections & MOVE_Z) == 0) {
  557. delta.Z = 0;
  558. }
  559. //
  560. // Calculate the distance we have yet to travel to
  561. // reach the next point
  562. //
  563. float delta_len = delta.Length ();
  564. delta_len -= m_MovementRadius;
  565. //
  566. // Reset our state
  567. //
  568. m_State = STATE_TRAVERSING_PATH;
  569. //
  570. // Recalculate our look-ahead distance
  571. //
  572. //m_LookAheadDist = (m_Velocity * 4.0F) / ASSUMED_FPS;
  573. //
  574. // Did we reach the expected position?
  575. //
  576. bool advance_dest = delta_len < m_LookAheadDist;
  577. if (advance_dest) {
  578. float new_time = m_SplineTime + m_LookAheadTime;
  579. if (new_time > (m_EndTime - (m_LookAheadTime * 0.5F))) {
  580. new_time = m_EndTime;
  581. }
  582. //
  583. // Have we successfully traversed the spline?
  584. //
  585. if (new_time >= m_EndTime) {
  586. //
  587. // Is this a looping path?
  588. //
  589. if (m_IsLooping) {
  590. //
  591. // If this path is looping, then wrap from the end of the
  592. // path to the beginning of the path
  593. //
  594. m_SplineTime = (new_time - m_EndTime) + m_StartTime;
  595. m_CurrentAction = -1;
  596. } else {
  597. //
  598. // If we are close to the destination, then consider us 'complete'.
  599. //
  600. //if (delta_len < 0.25F) {
  601. m_State = STATE_PATH_COMPLETE;
  602. m_SplineTime = m_EndTime;
  603. m_CurrentAction = -1;
  604. //}
  605. }
  606. } else {
  607. //
  608. // Check to see if the object needs to perform an action at this point
  609. //
  610. if (m_CurrentAction + 1 < m_PathActions.Count ()) {
  611. PATH_NODE &node = m_PathActions[m_CurrentAction + 1];
  612. //
  613. // Should we activate this action request?
  614. //
  615. float action_time = node.time;
  616. if (WWMath::Fabs (action_time - m_SplineTime) < (m_LookAheadTime * 0.5F)) {
  617. //
  618. // Set the new state and remember which action we are to perform
  619. //
  620. m_State = STATE_ACTION_REQUIRED;
  621. m_CurrentAction ++;
  622. //
  623. // Snap to the action point
  624. //
  625. new_time = node.next_time;
  626. }
  627. }
  628. //
  629. // Increase our position along the spline
  630. //
  631. m_SplineTime = new_time;
  632. }
  633. //
  634. // Evaluate the spline at the given 'time' to determine our new position
  635. //
  636. m_Spline->Evaluate (m_SplineTime, &m_ExpectedPos);
  637. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  638. m_SplineTime = ((VehicleCurveClass *)m_Spline)->Get_Last_Eval_Time ();
  639. }
  640. }
  641. new_pos = m_ExpectedPos;
  642. /*static PhysClass *_DebugObj = NULL;
  643. if (_DebugObj == NULL) {
  644. _DebugObj = new DecorationPhysClass;
  645. _DebugObj->Set_Model (WW3DAssetManager::Get_Instance ()->Create_Render_Obj ("C_HAVOC"));
  646. _DebugObj->Inc_Ignore_Counter ();
  647. PhysicsSceneClass::Get_Instance ()->Add_Dynamic_Object (_DebugObj);
  648. }
  649. _DebugObj->Set_Transform (Matrix3D (m_ExpectedPos));*/
  650. if (m_DisplayMoveVectors) {
  651. PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (curr_pos, Vector3 (0.25F,0.25F,0.25F)), Vector3 (1, 0, 0));
  652. PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (m_ExpectedPos, Vector3 (0.25F,0.25F,0.25F)), Vector3 (0, 1, 0));
  653. PhysicsSceneClass::Get_Instance ()->Add_Debug_Vector (curr_pos, m_ExpectedPos - curr_pos, Vector3 (0, 0.6F, 1));
  654. }
  655. return true;
  656. }
  657. ///////////////////////////////////////////////////////////////////////////
  658. //
  659. // Get_Curve_Sharpness
  660. //
  661. ///////////////////////////////////////////////////////////////////////////
  662. float
  663. PathClass::Get_Curve_Sharpness (Vector3 *position) const
  664. {
  665. float sharpness = 0;
  666. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  667. Vector3 foo;
  668. m_Spline->Evaluate (m_SplineTime, &foo);
  669. sharpness = ((VehicleCurveClass *)m_Spline)->Get_Current_Sharpness (position);
  670. }
  671. return sharpness;
  672. }
  673. ///////////////////////////////////////////////////////////////////////////
  674. //
  675. // Get_Last_Eval_Time
  676. //
  677. ///////////////////////////////////////////////////////////////////////////
  678. float
  679. PathClass::Get_Last_Eval_Time (void) const
  680. {
  681. return m_SplineTime;
  682. }
  683. ///////////////////////////////////////////////////////////////////////////
  684. //
  685. // Get_Remaining_Path_Length
  686. //
  687. ///////////////////////////////////////////////////////////////////////////
  688. float
  689. PathClass::Get_Remaining_Path_Length (void)
  690. {
  691. float length = m_TotalDist;
  692. //
  693. // The length remaining should correspond to the amount of time
  694. // remaining.
  695. //
  696. if (m_IsLooping == false && m_EndTime > 0) {
  697. length = m_TotalDist * ((m_EndTime - m_SplineTime) / m_EndTime);
  698. }
  699. return length;
  700. }
  701. ///////////////////////////////////////////////////////////////////////////
  702. //
  703. // Display_Path
  704. //
  705. ///////////////////////////////////////////////////////////////////////////
  706. void
  707. PathClass::Display_Path (bool onoff)
  708. {
  709. if (onoff == false) {
  710. PathDebugPlotterClass::Get_Instance ()->Display (false);
  711. } else if (m_State < FIRST_ERROR) {
  712. //
  713. // Turn off painting
  714. //
  715. PathDebugPlotterClass::Get_Instance ()->Display (false);
  716. PathDebugPlotterClass::Get_Instance ()->Reset ();
  717. //
  718. // Get the first position on the spline
  719. //
  720. Vector3 last_pos;
  721. m_Spline->Evaluate (0, &last_pos);
  722. //
  723. // Plot the spline
  724. //
  725. for (float t = m_LookAheadTime; t <= 1.0F; t += m_LookAheadTime) {
  726. //for (float t = m_LookAheadTime; t <= 1.0F; t += m_LookAheadTime / 2.0F) {
  727. Vector3 curr_pos;
  728. m_Spline->Evaluate (t, &curr_pos);
  729. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  730. t = ((VehicleCurveClass *)m_Spline)->Get_Last_Eval_Time ();
  731. }
  732. PathDebugPlotterClass::Get_Instance ()->Add (last_pos, curr_pos, Vector3 (0, 0.5F, 1));
  733. last_pos = curr_pos;
  734. }
  735. //
  736. // Turn painting back on
  737. //
  738. PathDebugPlotterClass::Get_Instance ()->Display (true);
  739. }
  740. return ;
  741. }
  742. /////////////////////////////////////////////////////////////////////////////////
  743. //
  744. // Set_Traversal_Type
  745. //
  746. /////////////////////////////////////////////////////////////////////////////////
  747. void
  748. PathClass::Set_Traversal_Type (TRAVERSAL_TYPE type)
  749. {
  750. //
  751. // Do we need to re-initialize any data?
  752. //
  753. if (m_TraversalType != type) {
  754. m_TraversalType = type;
  755. }
  756. return ;
  757. }
  758. /////////////////////////////////////////////////////////////////////////////////
  759. //
  760. // Find_Intersection_X
  761. //
  762. /////////////////////////////////////////////////////////////////////////////////
  763. bool
  764. Find_Intersection_X
  765. (
  766. const Vector3 & start,
  767. const Vector3 & end,
  768. const Vector3 & rgn_center,
  769. const Vector3 & rgn_extent,
  770. Vector3 * intersect_point
  771. )
  772. {
  773. float percent = (rgn_center.X - start.X) / (end.X - start.X);
  774. (*intersect_point) = start + ((end - start) * percent);
  775. bool retval = false;
  776. if (percent >= 0 && percent < 1.0F) {
  777. retval = (intersect_point->Y >= (rgn_center.Y - rgn_extent.Y));
  778. retval &= (intersect_point->Y <= (rgn_center.Y + rgn_extent.Y));
  779. retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
  780. retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
  781. }
  782. return retval;
  783. }
  784. /////////////////////////////////////////////////////////////////////////////////
  785. //
  786. // Find_Intersection_Y
  787. //
  788. /////////////////////////////////////////////////////////////////////////////////
  789. bool
  790. Find_Intersection_Y
  791. (
  792. const Vector3 & start,
  793. const Vector3 & end,
  794. const Vector3 & rgn_center,
  795. const Vector3 & rgn_extent,
  796. Vector3 * intersect_point
  797. )
  798. {
  799. float percent = (rgn_center.Y - start.Y) / (end.Y - start.Y);
  800. (*intersect_point) = start + ((end - start) * percent);
  801. bool retval = false;
  802. if (percent >= 0 && percent < 1.0F) {
  803. retval = (intersect_point->X >= (rgn_center.X - rgn_extent.X));
  804. retval &= (intersect_point->X <= (rgn_center.X + rgn_extent.X));
  805. retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
  806. retval &= (intersect_point->Z <= (rgn_center.Z + rgn_extent.Z));
  807. }
  808. return retval;
  809. }
  810. /////////////////////////////////////////////////////////////////////////////////
  811. //
  812. // Find_Side_Intersection
  813. //
  814. /////////////////////////////////////////////////////////////////////////////////
  815. bool
  816. Find_Side_Intersection
  817. (
  818. int index,
  819. const AABoxClass &box,
  820. const Vector3 & start,
  821. const Vector3 & end,
  822. Vector3 * intersection_point
  823. )
  824. {
  825. bool retval = false;
  826. if (index == 0) {
  827. Vector3 center = box.Center;
  828. center.X += box.Extent.X;
  829. Vector3 extent = Vector3 (0, box.Extent.Y, box.Extent.Z);
  830. retval = Find_Intersection_X (start, end, center, extent, intersection_point);
  831. } else if (index == 1) {
  832. Vector3 center = box.Center;
  833. center.X -= box.Extent.X;
  834. Vector3 extent = Vector3 (0, box.Extent.Y, box.Extent.Z);
  835. retval = Find_Intersection_X (start, end, center, extent, intersection_point);
  836. } else if (index == 2) {
  837. Vector3 center = box.Center;
  838. center.Y += box.Extent.Y;
  839. Vector3 extent = Vector3 (box.Extent.X, 0, box.Extent.Z);
  840. retval = Find_Intersection_Y (start, end, center, extent, intersection_point);
  841. } else if (index == 3) {
  842. Vector3 center = box.Center;
  843. center.Y -= box.Extent.Y;
  844. Vector3 extent = Vector3 (box.Extent.X, 0, box.Extent.Z);
  845. retval = Find_Intersection_Y (start, end, center, extent, intersection_point);
  846. }
  847. return retval;
  848. }
  849. /////////////////////////////////////////////////////////////////////////////////
  850. //
  851. // Is_Point_In_Boxes
  852. //
  853. /////////////////////////////////////////////////////////////////////////////////
  854. bool
  855. PathClass::Is_Point_In_Boxes
  856. (
  857. const Vector3 & point,
  858. BOX_LIST & box_list
  859. )
  860. {
  861. bool retval = false;
  862. //
  863. // Test each box in the list
  864. //
  865. for (int index = 0; (index < box_list.Count ()) && !retval; index ++) {
  866. retval |= box_list[index]->Contains (point);
  867. }
  868. return retval;
  869. }
  870. /////////////////////////////////////////////////////////////////////////////////
  871. //
  872. // Clip_Control_Point
  873. //
  874. /////////////////////////////////////////////////////////////////////////////////
  875. void
  876. PathClass::Clip_Control_Point
  877. (
  878. const Vector3 &start_point,
  879. Vector3 * point,
  880. BOX_LIST & sector_list,
  881. BOX_LIST & portal_list
  882. )
  883. {
  884. //
  885. // Loop through all the boxes we need to clip against
  886. //
  887. for (int index = 0; index < sector_list.Count (); index ++) {
  888. AABoxClass &box = *(sector_list[index]);
  889. //
  890. // Clip the line to each of the four sides of the box (don't care about
  891. // top or bottom for the moment).
  892. //
  893. for (int side_index = 0; side_index < 4; side_index ++) {
  894. //
  895. // Does the line pass through this side?
  896. //
  897. Vector3 intersect_point;
  898. if (Find_Side_Intersection (side_index, box, start_point, *point, &intersect_point)) {
  899. //
  900. // If there isn't a portal where this line passes through the side, then
  901. // clip the line to the side.
  902. //
  903. if (Is_Point_In_Boxes (intersect_point, portal_list) == false) {
  904. (*point) = intersect_point;
  905. }
  906. }
  907. }
  908. }
  909. return ;
  910. }
  911. /////////////////////////////////////////////////////////////////////////////////
  912. //
  913. // Initialize_Vehicle_Spline
  914. //
  915. /////////////////////////////////////////////////////////////////////////////////
  916. void
  917. PathClass::Initialize_Vehicle_Spline (DynamicVectorClass<PATH_NODE> &node_list)
  918. {
  919. m_Spline = new VehicleCurveClass (m_PathObject.Get_Turn_Radius ());
  920. Vector3 point;
  921. Vector3 last_point = m_StartPos;
  922. //
  923. // Setup the spline using each 'point' on the path
  924. // as a key along the spline.
  925. //
  926. float current_dist = 0;
  927. for (int index = 0; index < node_list.Count (); index ++) {
  928. point = node_list[index].pos;
  929. //
  930. // Add this point as a key along the spline
  931. //
  932. current_dist += (point - last_point).Length ();
  933. float curr_time = current_dist / m_TotalDist;
  934. m_Spline->Add_Key (point, curr_time);
  935. //
  936. // Is this a looping path?
  937. //
  938. if (m_IsLooping) {
  939. //
  940. // Record where the loop starts and ends...
  941. //
  942. if (index == 1) {
  943. m_StartTime = curr_time;
  944. } else if (index == node_list.Count () - 2) {
  945. m_EndTime = curr_time;
  946. }
  947. }
  948. last_point = point;
  949. }
  950. return ;
  951. }
  952. /////////////////////////////////////////////////////////////////////////////////
  953. //
  954. // Initialize_Human_Spline
  955. //
  956. /////////////////////////////////////////////////////////////////////////////////
  957. void
  958. PathClass::Initialize_Human_Spline(DynamicVectorClass<PATH_NODE> &node_list)
  959. {
  960. CardinalSpline3DClass temp_spline;
  961. float tightness = (m_TraversalType == SPLINE) ? 0.3F : 1.0F;
  962. //
  963. // Assume we do the whole path (looping paths only do a subset of
  964. // the path)
  965. //
  966. Vector3 point;
  967. Vector3 last_point = m_StartPos;
  968. //
  969. // Setup the temporary spline using each 'point' on the path
  970. // as a key along the spline.
  971. //
  972. float current_dist = 0;
  973. bool fixup_last_action = false;
  974. for (int index = 0; index < node_list.Count (); index ++) {
  975. point = node_list[index].pos;
  976. //
  977. // Add this point as a key along the spline
  978. //
  979. current_dist += (point - last_point).Length ();
  980. float curr_time = current_dist / m_TotalDist;
  981. temp_spline.Add_Key (point, curr_time);
  982. //
  983. // Is this a looping path?
  984. //
  985. if (m_IsLooping) {
  986. //
  987. // Record where the loop starts and ends...
  988. //
  989. if (index == 1) {
  990. m_StartTime = curr_time;
  991. } else if (index == node_list.Count () - 2) {
  992. m_EndTime = curr_time;
  993. }
  994. }
  995. //
  996. // Do we need to fix-up the time values for the last action node?
  997. //
  998. if (fixup_last_action) {
  999. PATH_NODE &node = m_PathActions[m_PathActions.Count () - 1];
  1000. node.next_time = curr_time;
  1001. fixup_last_action = false;
  1002. }
  1003. //
  1004. // Do we have an action at this node that we need to store?
  1005. //
  1006. if (node_list[index].action_id != 0) {
  1007. //
  1008. // Add an action node to our list so when the unit gets
  1009. // to this point along the spline, we can have it perform
  1010. // the requested action.
  1011. //
  1012. PATH_NODE node;
  1013. node.time = curr_time;
  1014. node.next_time = 1.0F;
  1015. node.action_id = node_list[index].action_id;
  1016. node.mechanism_id = node_list[index].mechanism_id;
  1017. node.dest_pos = node_list[index].dest_pos;
  1018. node.pos = node_list[index].pos;
  1019. m_PathActions.Add (node);
  1020. //
  1021. // We need to fix up the 'next_time' member during the next iteration
  1022. //
  1023. fixup_last_action = true;
  1024. }
  1025. //
  1026. // For some type of nodes we need to tighten the spline so its
  1027. // as sharp as possible (actions generally require this)
  1028. //
  1029. if (node_list[index].tighten_spline) {
  1030. temp_spline.Set_Tightness (index, 1.0F);
  1031. } else {
  1032. temp_spline.Set_Tightness (index, tightness);
  1033. }
  1034. last_point = point;
  1035. }
  1036. //
  1037. // Convert the temp spline to a hermite spline so we
  1038. // can adjust the tangent points if necessary (for clipping).
  1039. //
  1040. temp_spline.Update_Tangents ();
  1041. m_Spline = new HermiteSpline3DClass;
  1042. (*((HermiteSpline3DClass*)m_Spline)) = temp_spline;
  1043. return ;
  1044. }
  1045. /////////////////////////////////////////////////////////////////////////////////
  1046. //
  1047. // Clip_Spline_To_Pathfind_Data
  1048. //
  1049. /////////////////////////////////////////////////////////////////////////////////
  1050. void
  1051. PathClass::Clip_Spline_To_Pathfind_Data
  1052. (
  1053. DynamicVectorClass<PATH_NODE> &node_list,
  1054. PathSolveClass & path_solve
  1055. )
  1056. {
  1057. //
  1058. // Get the portal and sector list from the pathsolver
  1059. //
  1060. BOX_LIST sector_list;
  1061. BOX_LIST portal_list;
  1062. path_solve.Get_Volumes (sector_list, portal_list);
  1063. //
  1064. // Loop through the points in the hermite spline, convert
  1065. // each tangent to bezier curve control points, clip these
  1066. // points to the sectors and portals the path contains,
  1067. // and convert the clipped points back into hermite spline
  1068. // tangents.
  1069. //
  1070. for (int index = 0; index < node_list.Count () - 1; index ++) {
  1071. //
  1072. // Get the current and next points on the path
  1073. //
  1074. Vector3 point = node_list[index].pos;
  1075. Vector3 next_point = node_list[index + 1].pos;
  1076. //
  1077. // Get the in and out tangents for each of these points
  1078. //
  1079. Vector3 tangent_in;
  1080. Vector3 tangent_out;
  1081. Vector3 next_tangent_in;
  1082. Vector3 next_tangent_out;
  1083. ((HermiteSpline3DClass*)m_Spline)->Get_Tangents (index, &tangent_in, &tangent_out);
  1084. ((HermiteSpline3DClass*)m_Spline)->Get_Tangents (index + 1, &next_tangent_in, &next_tangent_out);
  1085. //
  1086. // Convert the tangents to bezier curve control points
  1087. //
  1088. float one_third = 1.0F / 3.0F;
  1089. Vector3 ctrl_pt1 = point + (tangent_out * one_third);
  1090. Vector3 ctrl_pt2 = next_point - (next_tangent_in * one_third);
  1091. //
  1092. // Clip the control points to the pathfind sectors and portals
  1093. //
  1094. Clip_Control_Point (point, &ctrl_pt1, sector_list, portal_list);
  1095. Clip_Control_Point (next_point, &ctrl_pt2, sector_list, portal_list);
  1096. //
  1097. // Convert the control points back into tangents
  1098. //
  1099. tangent_out = (ctrl_pt1 - point) * 3.0F;
  1100. next_tangent_in = (next_point - ctrl_pt2) * 3.0F;
  1101. //
  1102. // Pass the tangents back to the hermite spline
  1103. //
  1104. ((HermiteSpline3DClass*)m_Spline)->Set_Tangents (index, tangent_in, tangent_out);
  1105. ((HermiteSpline3DClass*)m_Spline)->Set_Tangents (index + 1, next_tangent_in, next_tangent_out);
  1106. }
  1107. //
  1108. // Free the temporary portal-box list
  1109. //
  1110. for (index = 0; index < portal_list.Count (); index ++) {
  1111. AABoxClass *portal_box = portal_list[index];
  1112. delete portal_box;
  1113. }
  1114. //
  1115. // Free the temporary sector-box list
  1116. //
  1117. for (index = 0; index < sector_list.Count (); index ++) {
  1118. AABoxClass *box = sector_list[index];
  1119. delete box;
  1120. }
  1121. return ;
  1122. }
  1123. /////////////////////////////////////////////////////////////////////////////////
  1124. //
  1125. // Initialize_Spline
  1126. //
  1127. /////////////////////////////////////////////////////////////////////////////////
  1128. void
  1129. PathClass::Initialize_Spline (DynamicVectorClass<PATH_NODE> &node_list)
  1130. {
  1131. if (node_list.Count () > 0) {
  1132. //
  1133. // Start fresh
  1134. //
  1135. m_PathActions.Delete_All ();
  1136. SAFE_DELETE (m_Spline);
  1137. //
  1138. // Get the first and last positions from node list
  1139. //
  1140. m_StartPos = node_list[0].pos;
  1141. m_DestPos = node_list[node_list.Count () - 1].pos;
  1142. if (m_IsLooping && node_list.Count () > 1) {
  1143. m_StartPos = node_list[1].pos;
  1144. m_DestPos = m_StartPos;
  1145. }
  1146. Vector3 point;
  1147. Vector3 last_point = m_StartPos;
  1148. //
  1149. // Determine how long the spline is
  1150. //
  1151. m_TotalDist = 0;
  1152. for (int index = 0; index < node_list.Count (); index ++) {
  1153. point = node_list[index].pos;
  1154. //
  1155. // Add the distance of this point from the last point
  1156. // into the total
  1157. //
  1158. m_TotalDist += (point - last_point).Length ();
  1159. last_point = point;
  1160. }
  1161. //
  1162. // Initialize the spline depending on the path object type
  1163. //
  1164. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  1165. Initialize_Vehicle_Spline (node_list);
  1166. m_Velocity = DEF_VEHICLE_VELOCITY;
  1167. } else {
  1168. Initialize_Human_Spline (node_list);
  1169. m_Velocity = DEF_HUMAN_VELOCITY;
  1170. }
  1171. //
  1172. // Setup the variables to look-ahead 8 frames and switch
  1173. // the new look-ahead after the unit has traveled 4 frames
  1174. //
  1175. float approx_frames = (m_TotalDist * ASSUMED_FPS) / m_Velocity;
  1176. m_LookAheadTime = 8.0F / approx_frames;
  1177. m_LookAheadDist = (m_Velocity * 4.0F) / ASSUMED_FPS;
  1178. //
  1179. // Vehicles automatically look ahead to each turn, so make sure
  1180. // we don't skip one...
  1181. //
  1182. if (m_PathObject.Is_Flag_Set (PathObjectClass::IS_VEHICLE)) {
  1183. m_LookAheadTime = m_LookAheadTime / 5.0F;
  1184. }
  1185. /*if (m_TotalDist > 0) {
  1186. m_LookAheadTime = 2.0F / m_TotalDist;
  1187. m_LookAheadDist = 4.0F;
  1188. }*/
  1189. m_SplineTime = m_StartTime;
  1190. m_ExpectedPos = m_StartPos;
  1191. }
  1192. return ;
  1193. }
  1194. /////////////////////////////////////////////////////////////////////////////////
  1195. //
  1196. // Set_Path_Object
  1197. //
  1198. /////////////////////////////////////////////////////////////////////////////////
  1199. void
  1200. PathClass::Set_Path_Object (PathObjectClass &path_object)
  1201. {
  1202. m_PathObject = path_object;
  1203. return ;
  1204. }
  1205. /////////////////////////////////////////////////////////////////////////////////
  1206. //
  1207. // Get_Path_Object
  1208. //
  1209. /////////////////////////////////////////////////////////////////////////////////
  1210. void
  1211. PathClass::Get_Path_Object (PathObjectClass &path_object) const
  1212. {
  1213. path_object = m_PathObject;
  1214. return ;
  1215. }
  1216. ////////////////////////////////////////////////////////////////////////////////////////////
  1217. //
  1218. // Save
  1219. //
  1220. ////////////////////////////////////////////////////////////////////////////////////////////
  1221. void
  1222. PathClass::Save (ChunkSaveClass &csave)
  1223. {
  1224. if (m_Spline != NULL) {
  1225. //
  1226. // Use the spline's factory to save it to its own chunk
  1227. //
  1228. csave.Begin_Chunk (CHUNKID_SPLINE);
  1229. csave.Begin_Chunk (m_Spline->Get_Factory ().Chunk_ID ());
  1230. m_Spline->Get_Factory ().Save (csave, m_Spline);
  1231. csave.End_Chunk ();
  1232. csave.End_Chunk ();
  1233. }
  1234. csave.Begin_Chunk (CHUNKID_VARIABLES);
  1235. //
  1236. // Save each variable to its own microchunk
  1237. //
  1238. WRITE_MICRO_CHUNK (csave, VARID_STATE, m_State);
  1239. WRITE_MICRO_CHUNK (csave, VARID_TRAVERSAL_TYPE, m_TraversalType);
  1240. WRITE_MICRO_CHUNK (csave, VARID_START_POS, m_StartPos);
  1241. WRITE_MICRO_CHUNK (csave, VARID_DEST_POS, m_DestPos);
  1242. WRITE_MICRO_CHUNK (csave, VARID_EXPECTED_POS, m_ExpectedPos);
  1243. WRITE_MICRO_CHUNK (csave, VARID_LOOK_AHEAD_DIST, m_LookAheadDist);
  1244. WRITE_MICRO_CHUNK (csave, VARID_LOOK_AHEAD_TIME, m_LookAheadTime);
  1245. WRITE_MICRO_CHUNK (csave, VARID_MOVEMENT_RADIUS, m_MovementRadius);
  1246. WRITE_MICRO_CHUNK (csave, VARID_SPLINE_TIME, m_SplineTime);
  1247. WRITE_MICRO_CHUNK (csave, VARID_VELOCITY, m_Velocity);
  1248. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_ACTION, m_CurrentAction);
  1249. WRITE_MICRO_CHUNK (csave, VARID_MOVEMENT_DIRECTIONS, m_MovementDirections);
  1250. WRITE_MICRO_CHUNK (csave, VARID_PATH_OBJECT, m_PathObject);
  1251. WRITE_MICRO_CHUNK (csave, VARID_START_TIME, m_StartTime);
  1252. WRITE_MICRO_CHUNK (csave, VARID_END_TIME, m_EndTime);
  1253. WRITE_MICRO_CHUNK (csave, VARID_ISLOOPING, m_IsLooping);
  1254. WRITE_MICRO_CHUNK (csave, VARID_TOTAL_DIST, m_TotalDist);
  1255. PathClass *this_ptr = this;
  1256. WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, this_ptr);
  1257. //
  1258. // Save each of the action nodes for this path
  1259. //
  1260. for (int index = 0; index < m_PathActions.Count (); index ++) {
  1261. PATH_NODE &node = m_PathActions[index];
  1262. WRITE_MICRO_CHUNK (csave, VARID_PATH_ACTION, node);
  1263. }
  1264. csave.End_Chunk ();
  1265. return ;
  1266. }
  1267. ////////////////////////////////////////////////////////////////////////////////////////////
  1268. //
  1269. // Load
  1270. //
  1271. ////////////////////////////////////////////////////////////////////////////////////////////
  1272. void
  1273. PathClass::Load (ChunkLoadClass &cload)
  1274. {
  1275. while (cload.Open_Chunk ()) {
  1276. switch (cload.Cur_Chunk_ID ()) {
  1277. case CHUNKID_SPLINE:
  1278. {
  1279. //
  1280. // Use the spline's factory to load it from disk
  1281. //
  1282. if (cload.Open_Chunk ()) {
  1283. PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
  1284. if (factory != NULL) {
  1285. m_Spline = (Curve3DClass *)factory->Load (cload);
  1286. }
  1287. cload.Close_Chunk ();
  1288. }
  1289. }
  1290. break;
  1291. case CHUNKID_VARIABLES:
  1292. Load_Variables (cload);
  1293. break;
  1294. }
  1295. cload.Close_Chunk ();
  1296. }
  1297. return ;
  1298. }
  1299. ///////////////////////////////////////////////////////////////////////
  1300. //
  1301. // Load_Variables
  1302. //
  1303. ///////////////////////////////////////////////////////////////////////
  1304. void
  1305. PathClass::Load_Variables (ChunkLoadClass &cload)
  1306. {
  1307. PathClass *old_ptr = NULL;
  1308. //
  1309. // Loop through all the microchunks that define the variables
  1310. //
  1311. while (cload.Open_Micro_Chunk ()) {
  1312. switch (cload.Cur_Micro_Chunk_ID ()) {
  1313. READ_MICRO_CHUNK (cload, VARID_STATE, m_State);
  1314. READ_MICRO_CHUNK (cload, VARID_TRAVERSAL_TYPE, m_TraversalType);
  1315. READ_MICRO_CHUNK (cload, VARID_START_POS, m_StartPos);
  1316. READ_MICRO_CHUNK (cload, VARID_DEST_POS, m_DestPos);
  1317. READ_MICRO_CHUNK (cload, VARID_EXPECTED_POS, m_ExpectedPos);
  1318. READ_MICRO_CHUNK (cload, VARID_LOOK_AHEAD_DIST, m_LookAheadDist);
  1319. READ_MICRO_CHUNK (cload, VARID_LOOK_AHEAD_TIME, m_LookAheadTime);
  1320. READ_MICRO_CHUNK (cload, VARID_MOVEMENT_RADIUS, m_MovementRadius);
  1321. READ_MICRO_CHUNK (cload, VARID_SPLINE_TIME, m_SplineTime);
  1322. READ_MICRO_CHUNK (cload, VARID_VELOCITY, m_Velocity);
  1323. READ_MICRO_CHUNK (cload, VARID_CURRENT_ACTION, m_CurrentAction);
  1324. READ_MICRO_CHUNK (cload, VARID_MOVEMENT_DIRECTIONS, m_MovementDirections);
  1325. READ_MICRO_CHUNK (cload, VARID_PATH_OBJECT, m_PathObject);
  1326. READ_MICRO_CHUNK (cload, VARID_START_TIME, m_StartTime);
  1327. READ_MICRO_CHUNK (cload, VARID_END_TIME, m_EndTime);
  1328. READ_MICRO_CHUNK (cload, VARID_ISLOOPING, m_IsLooping);
  1329. READ_MICRO_CHUNK (cload, VARID_TOTAL_DIST, m_TotalDist);
  1330. READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
  1331. case VARID_PATH_ACTION:
  1332. {
  1333. //
  1334. // Read the action information from disk and add it to our list
  1335. //
  1336. PATH_NODE node;
  1337. LOAD_MICRO_CHUNK (cload, node);
  1338. m_PathActions.Add (node);
  1339. }
  1340. break;
  1341. }
  1342. cload.Close_Micro_Chunk ();
  1343. }
  1344. //
  1345. // Register our old ptr so other objects can remap to us
  1346. //
  1347. if (old_ptr != NULL) {
  1348. SaveLoadSystemClass::Register_Pointer( old_ptr, this );
  1349. }
  1350. return ;
  1351. }