DRIVE.CPP 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: F:\projects\c&c\vcs\code\drive.cpv 2.17 16 Oct 1995 16:51:16 JOE_BOSTIC $ */
  15. /***********************************************************************************************
  16. *** 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 ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : DRIVE.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : April 22, 1994 *
  26. * *
  27. * Last Update : July 30, 1995 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * DriveClass::AI -- Processes unit movement and rotation. *
  32. * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
  33. * DriveClass::Assign_Destination -- Set the unit's NavCom. *
  34. * DriveClass::Class_Of -- Fetches a reference to the class type for this object. *
  35. * DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
  36. * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
  37. * DriveClass::DriveClass -- Constructor for drive class object. *
  38. * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. *
  39. * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
  40. * DriveClass::Force_Track -- Forces the unit to use the indicated track. *
  41. * DriveClass::Lay_Track -- Handles track laying logic for the unit. *
  42. * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
  43. * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
  44. * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. *
  45. * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
  46. * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
  47. * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
  48. * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
  49. * DriveClass::While_Moving -- Processes unit movement. *
  50. * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
  51. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52. #include "function.h"
  53. DriveClass::DriveClass(void) : Class(0), SimLeptonX(0), SimLeptonY(0) {}; // Added SimLeptonX and Y. ST - 4/30/2019 8:06AM
  54. /***********************************************************************************************
  55. * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
  56. * *
  57. * This routine will set the vehicle to rotate to the direction specified. For tracked *
  58. * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series *
  59. * of short drives (three point turn) to face the desired direction. *
  60. * *
  61. * INPUT: dir -- The direction that this vehicle should face. *
  62. * *
  63. * OUTPUT: none *
  64. * *
  65. * WARNINGS: none *
  66. * *
  67. * HISTORY: *
  68. * 05/29/1995 JLB : Created. *
  69. *=============================================================================================*/
  70. void DriveClass::Do_Turn(DirType dir)
  71. {
  72. if (dir != PrimaryFacing) {
  73. /*
  74. ** Special rotation track is needed for units that
  75. ** cannot rotate in place.
  76. */
  77. if (Special.IsThreePoint && TrackNumber == -1 && Class->Speed == SPEED_WHEEL) {
  78. int facediff; // Signed difference between current and desired facing.
  79. FacingType face; // Current facing (ordinal value).
  80. facediff = PrimaryFacing.Difference(dir) >> 5;
  81. facediff = Bound(facediff, -2, 2);
  82. if (facediff) {
  83. face = Dir_Facing(PrimaryFacing);
  84. IsOnShortTrack = true;
  85. Force_Track(face*FACING_COUNT + (face + facediff), Coord);
  86. Path[0] = FACING_NONE;
  87. Set_Speed(0xFF); // Full speed.
  88. }
  89. } else {
  90. PrimaryFacing.Set_Desired(dir);
  91. //if (Special.IsJurassic && AreThingiesEnabled && What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir);
  92. if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir);
  93. }
  94. }
  95. }
  96. /***********************************************************************************************
  97. * DriveClass::Force_Track -- Forces the unit to use the indicated track. *
  98. * *
  99. * This override (nuclear bomb) style routine is to be used when a unit needs to start *
  100. * on a movement track but is outside the normal movement system. This occurs when a *
  101. * harvester starts driving off of a refinery. *
  102. * *
  103. * INPUT: track -- The track number to start on. *
  104. * *
  105. * coord -- The coordinate that the unit will end up at when the movement track *
  106. * is completed. *
  107. * *
  108. * OUTPUT: none *
  109. * *
  110. * WARNINGS: none *
  111. * *
  112. * HISTORY: *
  113. * 03/17/1995 JLB : Created. *
  114. *=============================================================================================*/
  115. void DriveClass::Force_Track(int track, COORDINATE coord)
  116. {
  117. TrackNumber = track;
  118. TrackIndex = 0;
  119. Start_Driver(coord);
  120. }
  121. /***********************************************************************************************
  122. * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
  123. * *
  124. * Use this routine to determine what the Tiberium load is (as a fixed point percentage). *
  125. * *
  126. * INPUT: none *
  127. * *
  128. * OUTPUT: Returns with the current "fullness" rating for the object. This will be 0x0000 for *
  129. * empty and 0x0100 for full. *
  130. * *
  131. * WARNINGS: none *
  132. * *
  133. * HISTORY: *
  134. * 03/17/1995 JLB : Created. *
  135. *=============================================================================================*/
  136. int DriveClass::Tiberium_Load(void) const
  137. {
  138. if (*this == UNIT_HARVESTER) {
  139. return(Cardinal_To_Fixed(UnitTypeClass::STEP_COUNT, Tiberium));
  140. }
  141. return(0x0000);
  142. }
  143. /***********************************************************************************************
  144. * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
  145. * *
  146. * This routine will check to see if the target is infantry and it can be overrun. It will *
  147. * try to overrun the infantry rather than attack it. This only applies to computer *
  148. * controlled vehicles. If it isn't the infantry overrun case, then it falls into the *
  149. * base class for normal (complex) approach algorithm. *
  150. * *
  151. * INPUT: none *
  152. * *
  153. * OUTPUT: none *
  154. * *
  155. * WARNINGS: none *
  156. * *
  157. * HISTORY: *
  158. * 03/17/1995 JLB : Created. *
  159. * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. *
  160. *=============================================================================================*/
  161. void DriveClass::Approach_Target(void)
  162. {
  163. /*
  164. ** Only if there is a legal target should the approach check occur.
  165. */
  166. if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) {
  167. /*
  168. ** Special case:
  169. ** If this is for a unit that can crush infantry, and the target is
  170. ** infantry, AND the infantry is pretty darn close, then just try
  171. ** to drive over the infantry instead of firing on it.
  172. */
  173. TechnoClass * target = As_Techno(TarCom);
  174. if (Class->Primary != WEAPON_FLAME_TONGUE && Class->IsCrusher && Distance(TarCom) < 0x0180 && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) {
  175. Assign_Destination(TarCom);
  176. return;
  177. }
  178. }
  179. /*
  180. ** In the other cases, uses the more complex "get to just within weapon range"
  181. ** algorithm.
  182. */
  183. FootClass::Approach_Target();
  184. }
  185. /***********************************************************************************************
  186. * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. *
  187. * *
  188. * This routine is called when a vehicle enters a square or when it is about to enter a *
  189. * square (controlled by parameter). When a vehicle that can crush infantry enters a *
  190. * cell that contains infantry, then the infantry will be destroyed (regardless of *
  191. * affiliation). When a vehicle threatens to overrun a square, all occupying infantry *
  192. * will attempt to get out of the way. *
  193. * *
  194. * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. *
  195. * *
  196. * threaten -- Don't kill, but just threaten to enter the cell. *
  197. * *
  198. * OUTPUT: none *
  199. * *
  200. * WARNINGS: none *
  201. * *
  202. * HISTORY: *
  203. * 01/19/1995 JLB : Created. *
  204. *=============================================================================================*/
  205. void DriveClass::Overrun_Square(CELL cell, bool threaten)
  206. {
  207. CellClass * cellptr = &Map[cell];
  208. if (Class->IsCrusher) {
  209. if (threaten) {
  210. /*
  211. ** If the cell contains infantry, then they will panic when a vehicle tries
  212. ** drive over them. Have the infantry run away instead.
  213. */
  214. if (cellptr->Flag.Composite & 0x1F) {
  215. /*
  216. ** Scattering is controlled by the game difficulty level.
  217. */
  218. if (((GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_HARD) || Special.IsScatter || Scenario > 8) &&
  219. !(GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY)) {
  220. cellptr->Incoming(0, true);
  221. }
  222. }
  223. } else {
  224. ObjectClass * object = cellptr->Cell_Occupier();
  225. int crushed = false;
  226. while (object) {
  227. if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < 0x80) {
  228. ObjectClass * next = object->Next;
  229. crushed = true;
  230. /*
  231. ** Record credit for the kill(s)
  232. */
  233. Sound_Effect(VOC_SQUISH2, Coord);
  234. object->Record_The_Kill(this);
  235. object->Mark(MARK_UP);
  236. object->Limbo();
  237. delete object;
  238. new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord));
  239. object = next;
  240. } else {
  241. object = object->Next;
  242. }
  243. }
  244. if (crushed) Do_Uncloak();
  245. }
  246. }
  247. }
  248. /***********************************************************************************************
  249. * DriveClass::DriveClass -- Constructor for drive class object. *
  250. * *
  251. * This will initialize the drive class to its default state. It is called as a result *
  252. * of creating a unit. *
  253. * *
  254. * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. *
  255. * *
  256. * OUTPUT: none *
  257. * *
  258. * WARNINGS: none *
  259. * *
  260. * HISTORY: *
  261. * 07/13/1994 JLB : Created. *
  262. *=============================================================================================*/
  263. DriveClass::DriveClass(UnitType classid, HousesType house) :
  264. Class(&UnitTypeClass::As_Reference(classid)),
  265. FootClass(house)
  266. {
  267. /*
  268. ** For two shooters, clear out the second shot flag -- it will be set the first time
  269. ** the object fires. For non two shooters, set the flag since it will never be cleared
  270. ** and the second shot flag tells the system that normal rearm times apply -- this is
  271. ** what is desired for non two shooters.
  272. */
  273. if (Class->IsTwoShooter) {
  274. IsSecondShot = false;
  275. } else {
  276. IsSecondShot = true;
  277. }
  278. IsHarvesting = false;
  279. IsTurretLockedDown = false;
  280. IsOnShortTrack = false;
  281. IsReturning = false;
  282. TrackNumber = -1;
  283. TrackIndex = 0;
  284. SpeedAccum = 0;
  285. Tiberium = 0;
  286. Strength = Class->MaxStrength;
  287. }
  288. #ifdef CHEAT_KEYS
  289. /***********************************************************************************************
  290. * DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
  291. * *
  292. * This debug utility function will display the status of the drive class to the mono *
  293. * screen. It is through this information that bugs can be tracked down. *
  294. * *
  295. * INPUT: none *
  296. * *
  297. * OUTPUT: none *
  298. * *
  299. * WARNINGS: none *
  300. * *
  301. * HISTORY: *
  302. * 05/31/1994 JLB : Created. *
  303. *=============================================================================================*/
  304. void DriveClass::Debug_Dump(MonoClass *mono) const
  305. {
  306. mono->Set_Cursor(33, 7);
  307. mono->Printf("%2d:%2d", TrackNumber, TrackIndex);
  308. mono->Text_Print("X", 16 + (IsTurretLockedDown?2:0), 10);
  309. // mono->Text_Print("X", 16 + (IsOnShortTrack?2:0), 11);
  310. mono->Set_Cursor(41, 7);mono->Printf("%d", Fixed_To_Cardinal(100, Tiberium_Load()));
  311. FootClass::Debug_Dump(mono);
  312. }
  313. #endif
  314. /***********************************************************************************************
  315. * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. *
  316. * *
  317. * This routine is used to assign an appropriate movement destination for the unit so that *
  318. * it will leave the map. The scripts are usually the one to call this routine when it *
  319. * is determined that the unit has fulfilled its mission and must "depart". *
  320. * *
  321. * INPUT: none *
  322. * *
  323. * OUTPUT: none *
  324. * *
  325. * WARNINGS: none *
  326. * *
  327. * HISTORY: *
  328. * 05/31/1994 JLB : Created. *
  329. *=============================================================================================*/
  330. void DriveClass::Exit_Map(void)
  331. {
  332. CELL cell; // Map exit cell number.
  333. if (*this == UNIT_HOVER && !Target_Legal(NavCom)) {
  334. /*
  335. ** Scan a swath of cells from current position to the edge of the map and if
  336. ** there is any blocking object, just wait so to try again later.
  337. */
  338. Mark(MARK_UP);
  339. for (int x = Cell_X(Coord_Cell(Center_Coord()))-1; x <= Cell_X(Coord_Cell(Center_Coord()))+1; x++) {
  340. for (int y = Cell_Y(Coord_Cell(Center_Coord()))+1; y < Map.MapCellY+Map.MapCellHeight; y++) {
  341. cell = XY_Cell(x, y);
  342. if (Map[cell].Cell_Techno()) {
  343. Mark(MARK_DOWN);
  344. return;
  345. }
  346. }
  347. }
  348. Mark(MARK_DOWN);
  349. /*
  350. ** A clear path to the map edge exists. Assign it as the navigation computer
  351. ** destination and let the transport move.
  352. */
  353. cell = XY_Cell(Cell_X(Coord_Cell(Coord)), Map.MapCellY+Map.MapCellHeight);
  354. IsReturning = true;
  355. Assign_Destination(::As_Target(cell));
  356. }
  357. }
  358. /***********************************************************************************************
  359. * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
  360. * *
  361. * This routine calculates the new coordinate value needed for the *
  362. * smooth turn logic. The adjustment and flag values must be *
  363. * determined prior to entering this routine. *
  364. * *
  365. * INPUT: adj -- The adjustment coordinate as lifted from the *
  366. * correct smooth turn table. *
  367. * *
  368. * dir -- Pointer to dir for possible modification *
  369. * according to the flag bits. *
  370. * *
  371. * OUTPUT: Returns with the coordinate the unit should positioned to. *
  372. * *
  373. * WARNINGS: none *
  374. * *
  375. * HISTORY: *
  376. * 03/14/1994 JLB : Created. *
  377. * 07/13/1994 JLB : Converted to member function. *
  378. *=============================================================================================*/
  379. COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType *dir)
  380. {
  381. DirType workdir = *dir;
  382. int x,y;
  383. int temp;
  384. TrackControlType flags = TrackControl[TrackNumber].Flag;
  385. x = Coord_X(adj);
  386. y = Coord_Y(adj);
  387. if (flags & F_T) {
  388. temp = x;
  389. x = y;
  390. y = temp;
  391. workdir = (DirType)(DIR_W - workdir);
  392. }
  393. if (flags & F_X) {
  394. x = -x;
  395. workdir = (DirType)-workdir;
  396. }
  397. if (flags & F_Y) {
  398. y = -y;
  399. workdir = (DirType)(DIR_S - workdir);
  400. }
  401. *dir = workdir;
  402. return(XY_Coord( Coord_X(Head_To_Coord()) + x, Coord_Y(Head_To_Coord()) + y));
  403. }
  404. /***********************************************************************************************
  405. * DriveClass::Assign_Destination -- Set the unit's NavCom. *
  406. * *
  407. * This routine is used to set the unit's navigation computer to the *
  408. * specified target. Once the navigation computer is set, the unit *
  409. * will start planning and moving toward the destination. *
  410. * *
  411. * INPUT: target -- The destination target for the unit to head to. *
  412. * *
  413. * OUTPUT: none *
  414. * *
  415. * WARNINGS: none *
  416. * *
  417. * HISTORY: *
  418. * 09/07/1992 JLB : Created. *
  419. * 04/15/1994 JLB : Converted to member function. *
  420. *=============================================================================================*/
  421. void DriveClass::Assign_Destination(TARGET target)
  422. {
  423. /*
  424. ** Abort early if there is anything wrong with the parameters
  425. ** or the unit already is assigned the specified destination.
  426. */
  427. if (target == NavCom) return;
  428. #ifdef NEVER
  429. UnitClass *tunit; // Destination unit pointer.
  430. /*
  431. ** When in move mode, a map position may really indicate
  432. ** a unit to guard.
  433. */
  434. if (Is_Target_Cell(target)) {
  435. cell = As_Cell(target);
  436. tunit = Map[cell].Cell_Unit();
  437. if (tunit) {
  438. /*
  439. ** Prevent targeting of itself.
  440. */
  441. if (tunit != this) {
  442. target = tunit->As_Target();
  443. }
  444. } else {
  445. tbuilding = Map[cell].Cell_Building();
  446. if (tbuilding) {
  447. target = tbuilding->As_Target();
  448. }
  449. }
  450. }
  451. #endif
  452. /*
  453. ** For harvesting type vehicles, it might go into a dock and unload procedure
  454. ** when the harvester is full and an empty refinery is selected as a target.
  455. */
  456. BuildingClass * b = As_Building(target);
  457. /*
  458. ** Transport vehicles must tell all passengers that are about to load, that they
  459. ** cannot proceed. This is accomplished with a radio message to this effect.
  460. */
  461. //if (tunit && In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) {
  462. if (In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) {
  463. Transmit_Message(RADIO_OVER_OUT);
  464. }
  465. /*
  466. ** If the player clicked on a friendly repair facility and the repair
  467. ** facility is currently not involved with some other unit (radio or unloading).
  468. */
  469. if (b && *b == STRUCT_REPAIR) {
  470. if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this)) {
  471. ArchiveTarget = target;
  472. } else {
  473. /*
  474. ** Establish radio contact protocol. If the facility responds correctly,
  475. ** then remain in radio contact and proceed toward the desired destination.
  476. */
  477. if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) {
  478. /*
  479. ** Last check to make sure that the loading square is free from permanent
  480. ** occupation (such as a building).
  481. */
  482. CELL cell = Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1);
  483. if (Ground[Map[cell].Land_Type()].Cost[Class->Speed] ) {
  484. if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) {
  485. FootClass::Assign_Destination(target);
  486. Path[0] = FACING_NONE;
  487. return;
  488. }
  489. /*
  490. ** Failure to establish a docking relationship with the refinery.
  491. ** Bail & await further instructions.
  492. */
  493. Transmit_Message(RADIO_OVER_OUT);
  494. }
  495. }
  496. }
  497. }
  498. /*
  499. ** Set the unit's navigation computer.
  500. */
  501. FootClass::Assign_Destination(target);
  502. Path[0] = FACING_NONE; // Force recalculation of path.
  503. if (!IsDriving) {
  504. Start_Of_Move();
  505. }
  506. }
  507. /***********************************************************************************************
  508. * DriveClass::While_Moving -- Processes unit movement. *
  509. * *
  510. * This routine is used to process movement for the units as they move. *
  511. * It is called many times for each cell's worth of movement. This *
  512. * routine only applies after the next cell HeadTo has been determined. *
  513. * *
  514. * INPUT: none *
  515. * *
  516. * OUTPUT: true/false; Should this routine be called again? *
  517. * *
  518. * WARNINGS: none *
  519. * *
  520. * HISTORY: *
  521. * 02/02/1992 JLB : Created. *
  522. * 04/15/1994 JLB : Converted to member function. *
  523. *=============================================================================================*/
  524. bool DriveClass::While_Moving(void)
  525. {
  526. int actual; // Working movement addition value.
  527. /*
  528. ** Perform quick legality checks.
  529. */
  530. if (!IsDriving || TrackNumber == -1 || (IsRotating && !Class->IsTurretEquipped)) {
  531. SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold.
  532. return(false);
  533. }
  534. /*
  535. ** If enough movement has accumulated so that the unit can
  536. ** visibly move on the map, then process accordingly.
  537. ** Slow the unit down if he's carrying a flag.
  538. */
  539. MPHType maxspeed = MPHType(min((int)(Class->MaxSpeed * House->GroundspeedBias), (int)MPH_LIGHT_SPEED));
  540. if (((UnitClass *)this)->Flagged != HOUSE_NONE) {
  541. actual = SpeedAccum + Fixed_To_Cardinal(maxspeed /2, Speed);
  542. } else {
  543. actual = SpeedAccum + Fixed_To_Cardinal(maxspeed, Speed);
  544. }
  545. if (actual > PIXEL_LEPTON_W) {
  546. TurnTrackType const *track; // Track control pointer.
  547. TrackType const *ptr; // Pointer to coord offset values.
  548. int tracknum; // The track number being processed.
  549. FacingType nextface; // Next facing queued in path.
  550. bool adj; // Is a turn coming up?
  551. track = &TrackControl[TrackNumber];
  552. if (IsOnShortTrack) {
  553. tracknum = track->StartTrack;
  554. } else {
  555. tracknum = track->Track;
  556. }
  557. ptr = RawTracks[tracknum-1].Track;
  558. nextface = Path[0];
  559. /*
  560. ** Determine if there is a turn coming up. If there is
  561. ** a turn, then track jumping might occur.
  562. */
  563. adj = false;
  564. if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
  565. adj = true;
  566. }
  567. /*
  568. ** Skip ahead the number of track steps required (limited only
  569. ** by track length). Set the unit to the new position and
  570. ** flag the unit accordingly.
  571. */
  572. Mark(MARK_UP);
  573. while (actual > PIXEL_LEPTON_W) {
  574. COORDINATE offset;
  575. DirType dir;
  576. actual -= PIXEL_LEPTON_W;
  577. offset = ptr[TrackIndex].Offset;
  578. if (offset || !TrackIndex) {
  579. dir = ptr[TrackIndex].Facing;
  580. Coord = Smooth_Turn(offset, &dir);
  581. PrimaryFacing.Set(dir);
  582. /*
  583. ** See if "per cell" processing is necessary.
  584. */
  585. if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) {
  586. Per_Cell_Process(false);
  587. if (!IsActive) {
  588. return(false);
  589. }
  590. }
  591. /*
  592. ** The unit could "jump tracks". Check to see if the unit should
  593. ** do so.
  594. */
  595. if (*this != UNIT_GUNBOAT && nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) {
  596. TurnTrackType const *newtrack; // Proposed jump-to track.
  597. int tnum;
  598. tnum = Dir_Facing(track->Facing)*FACING_COUNT + nextface;
  599. newtrack = &TrackControl[tnum];
  600. if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) {
  601. COORDINATE c = Head_To_Coord();
  602. int oldspeed = Speed;
  603. c = Adjacent_Cell(c, nextface);
  604. switch(Can_Enter_Cell(Coord_Cell(c), nextface)) {
  605. case MOVE_OK:
  606. IsOnShortTrack = false; // Shouldn't be necessary, but...
  607. TrackNumber = tnum;
  608. track = newtrack;
  609. // Mono_Printf("**Jumping from track %d to track %d. **\n", tracknum, track->Track);Keyboard::Get();
  610. tracknum = track->Track;
  611. TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment.
  612. ptr = RawTracks[tracknum-1].Track;
  613. adj = false;
  614. Stop_Driver();
  615. Per_Cell_Process(true);
  616. if (Start_Driver(c)) {
  617. Set_Speed(oldspeed);
  618. memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1);
  619. Path[CONQUER_PATH_MAX-1] = FACING_NONE;
  620. } else {
  621. Path[0] = FACING_NONE;
  622. TrackNumber = -1;
  623. actual = 0;
  624. }
  625. break;
  626. case MOVE_CLOAK:
  627. Map[Coord_Cell(c)].Shimmer();
  628. break;
  629. case MOVE_TEMP:
  630. if (*this == UNIT_HARVESTER || !House->IsHuman) {
  631. bool old = Special.IsScatter;
  632. Special.IsScatter = true;
  633. Map[Coord_Cell(c)].Incoming(0, true);
  634. Special.IsScatter = old;
  635. }
  636. break;
  637. }
  638. }
  639. }
  640. TrackIndex++;
  641. } else {
  642. actual = 0;
  643. Coord = Head_To_Coord();
  644. Stop_Driver();
  645. TrackNumber = -1;
  646. TrackIndex = NULL;
  647. /*
  648. ** Perform "per cell" activities.
  649. */
  650. Per_Cell_Process(true);
  651. break;
  652. }
  653. }
  654. if (IsActive) {
  655. Mark(MARK_DOWN);
  656. }
  657. }
  658. /*
  659. ** NEW 4/30/2019 7:59AM
  660. **
  661. ** When we don't have enough speed accumulated to move another pixel, it would be good to know at a sub-pixel (lepton) level
  662. ** how far we would move if we could. It didn't matter in the original when it was 320x200 pixels, but on a 3840x2160
  663. ** screen, what was half a pixel could now be several pixels.
  664. **
  665. ** ST
  666. **
  667. */
  668. if (actual && actual <= PIXEL_LEPTON_W) {
  669. TurnTrackType const *track; // Track control pointer.
  670. TrackType const *ptr; // Pointer to coord offset values.
  671. int tracknum; // The track number being processed.
  672. FacingType nextface; // Next facing queued in path.
  673. bool adj; // Is a turn coming up?
  674. track = &TrackControl[TrackNumber];
  675. if (IsOnShortTrack) {
  676. tracknum = track->StartTrack;
  677. } else {
  678. tracknum = track->Track;
  679. }
  680. ptr = RawTracks[tracknum-1].Track;
  681. nextface = Path[0];
  682. /*
  683. ** Determine if there is a turn coming up. If there is
  684. ** a turn, then track jumping might occur.
  685. */
  686. adj = false;
  687. if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
  688. adj = true;
  689. }
  690. COORDINATE simulated_pos = Coord;
  691. COORDINATE offset;
  692. DirType dir;
  693. offset = ptr[TrackIndex].Offset;
  694. if (offset || !TrackIndex) {
  695. dir = ptr[TrackIndex].Facing;
  696. simulated_pos = Smooth_Turn(offset, &dir);
  697. }
  698. int x_diff = Coord_X(simulated_pos) - Coord_X(Coord);
  699. int y_diff = Coord_Y(simulated_pos) - Coord_Y(Coord);
  700. SimLeptonX = (x_diff * actual) / PIXEL_LEPTON_W;
  701. SimLeptonY = (y_diff * actual) / PIXEL_LEPTON_W;
  702. } else {
  703. SimLeptonX = 0;
  704. SimLeptonY = 0;
  705. }
  706. /*
  707. ** Replace any remainder back into the unit's movement
  708. ** accumulator to be processed next pass.
  709. */
  710. SpeedAccum = actual;
  711. return(true);
  712. }
  713. /***********************************************************************************************
  714. * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
  715. * *
  716. * This routine is called when a unit has mostly or completely *
  717. * entered a cell. The unit might be in the middle of a movement track *
  718. * when this routine is called. It's primary purpose is to perform *
  719. * sighting and other "per cell" activities. *
  720. * *
  721. * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" *
  722. * to the center, then this parameter will be false. *
  723. * *
  724. * OUTPUT: none *
  725. * *
  726. * WARNINGS: none *
  727. * *
  728. * HISTORY: *
  729. * 11/03/1993 JLB : Created. *
  730. * 03/30/1994 JLB : Revamped for track system. *
  731. * 04/15/1994 JLB : Converted to member function. *
  732. * 06/18/1994 JLB : Converted to virtual function. *
  733. * 06/18/1994 JLB : Distinguishes between center and near-center conditions. *
  734. *=============================================================================================*/
  735. void DriveClass::Per_Cell_Process(bool center)
  736. {
  737. CELL cell = Coord_Cell(Coord);
  738. /*
  739. ** Check to see if it has reached its destination. If so, then clear the NavCom
  740. ** regardless of the remaining path list.
  741. */
  742. if (center && As_Cell(NavCom) == cell) {
  743. IsTurretLockedDown = false;
  744. NavCom = TARGET_NONE;
  745. Path[0] = FACING_NONE;
  746. }
  747. #ifdef NEVER
  748. /*
  749. ** A "lemon" vehicle will have a tendency to break down as
  750. ** it moves about the terrain.
  751. */
  752. if (Is_A_Lemon) {
  753. if (Random_Pick(1, 4) == 1) {
  754. Take_Damage(1);
  755. }
  756. }
  757. #endif
  758. Lay_Track();
  759. FootClass::Per_Cell_Process(center);
  760. }
  761. /***********************************************************************************************
  762. * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
  763. * *
  764. * This will try to start a unit advancing toward the cell it is *
  765. * facing. It will check for and handle legality and reserving of the *
  766. * necessary cell. *
  767. * *
  768. * INPUT: none *
  769. * *
  770. * OUTPUT: true/false; Should this routine be called again because *
  771. * initial start operation is temporarily delayed? *
  772. * *
  773. * WARNINGS: none *
  774. * *
  775. * HISTORY: *
  776. * 02/02/1992 JLB : Created. *
  777. * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. *
  778. * 03/16/1994 JLB : Revamped for track logic. *
  779. * 04/15/1994 JLB : Converted to member function. *
  780. * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. *
  781. * 07/13/1995 JLB : Handles bumping into cloaked objects. *
  782. *=============================================================================================*/
  783. bool DriveClass::Start_Of_Move(void)
  784. {
  785. FacingType facing; // Direction movement will commence.
  786. DirType dir; // Desired actual facing toward destination.
  787. int facediff; // Difference between current and desired facing.
  788. int speed; // Speed of unit.
  789. CELL destcell; // Cell of destination.
  790. LandType ground; // Ground unit is entering.
  791. COORDINATE dest; // Destination coordinate.
  792. facing = Path[0];
  793. if (!Target_Legal(NavCom) && facing == FACING_NONE) {
  794. IsTurretLockedDown = false;
  795. Stop_Driver();
  796. if (Mission == MISSION_MOVE) {
  797. Enter_Idle_Mode();
  798. }
  799. return(false); // Why is it calling this routine!?!
  800. }
  801. #ifdef NEVER
  802. /*
  803. ** Movement start logic can't begin until a unit that requires
  804. ** a locked down turret gets to a locked down state (i.e., the
  805. ** turret rotation stops.
  806. */
  807. if (ClassF & CLASSF_LOCKTURRET) {
  808. Set_Secondary_Facing(facing<<5);
  809. if (Is_Rotating) {
  810. return(true);
  811. }
  812. }
  813. #endif
  814. /*
  815. ** Reduce the path length if the target is a unit and the
  816. ** range to the unit is less than the precalculated path steps.
  817. */
  818. if (facing != FACING_NONE) {
  819. int dist;
  820. if (Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) {
  821. dist = Lepton_To_Cell(Distance(NavCom));
  822. // if (dist > CELL_LEPTON_W ||
  823. // !As_Techno(NavCom)->Techno_Type_Class()->IsCrushable ||
  824. // !Class->IsCrusher) {
  825. if (dist < CONQUER_PATH_MAX) {
  826. Path[dist] = FACING_NONE;
  827. facing = Path[0]; // Maybe needed.
  828. }
  829. // }
  830. }
  831. }
  832. /*
  833. ** If the path is invalid at this point, then generate one. If
  834. ** generating a new path fails, then abort NavCom.
  835. */
  836. if (facing == FACING_NONE) {
  837. /*
  838. ** If after a path search, there is still no valid path, then set the
  839. ** NavCom to null and let the script take care of assigning a new
  840. ** navigation target.
  841. */
  842. if (!PathDelay.Expired()) {
  843. return(false);
  844. }
  845. if (!Basic_Path()) {
  846. if (Distance(NavCom) < 0x0280 && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) {
  847. Assign_Destination(TARGET_NONE);
  848. } else {
  849. /*
  850. ** If a basic path could be found, but the immediate move destination is
  851. ** blocked by a friendly temporary blockage, then cause that blockage
  852. ** to scatter. If the destination is also one cell away, then scatter
  853. ** regardless of direction.
  854. */
  855. CELL ourcell = Coord_Cell(Center_Coord());
  856. CELL navcell = As_Cell(NavCom);
  857. CELL cell = -1;
  858. if (::Distance(ourcell, navcell) < 2) {
  859. cell = navcell;
  860. } else {
  861. cell = Adjacent_Cell(ourcell, PrimaryFacing.Current());
  862. }
  863. if (Map.In_Radar(cell)) {
  864. if (Can_Enter_Cell(cell) == MOVE_TEMP) {
  865. CellClass * cellptr = &Map[cell];
  866. TechnoClass * blockage = cellptr->Cell_Techno();
  867. if (blockage && House->Is_Ally(blockage)) {
  868. bool old = Special.IsScatter;
  869. Special.IsScatter = true;
  870. cellptr->Incoming(0, true);
  871. Special.IsScatter = old;
  872. }
  873. }
  874. }
  875. if (TryTryAgain) {
  876. TryTryAgain--;
  877. } else {
  878. Assign_Destination(TARGET_NONE);
  879. if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
  880. IsNewNavCom = false;
  881. }
  882. }
  883. Stop_Driver();
  884. TrackNumber = -1;
  885. IsTurretLockedDown = false;
  886. return(false);
  887. }
  888. /*
  889. ** If a basic path could be found, but the immediate move destination is
  890. ** blocked by a friendly temporary blockage, then cause that blockage
  891. ** to scatter.
  892. */
  893. CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]);
  894. if (Map.In_Radar(cell)) {
  895. if (Can_Enter_Cell(cell) == MOVE_TEMP) {
  896. CellClass * cellptr = &Map[cell];
  897. TechnoClass * blockage = cellptr->Cell_Techno();
  898. if (blockage && House->Is_Ally(blockage)) {
  899. bool old = Special.IsScatter;
  900. Special.IsScatter = true;
  901. cellptr->Incoming(0, true);
  902. Special.IsScatter = old;
  903. }
  904. }
  905. }
  906. TryTryAgain = PATH_RETRY;
  907. facing = Path[0];
  908. }
  909. if (Class->IsLockTurret || !Class->IsTurretEquipped) {
  910. IsTurretLockedDown = true;
  911. }
  912. #ifdef NEVER
  913. /*
  914. ** If the turret needs to match the body's facing before
  915. ** movement can occur, then start it's rotation and
  916. ** don't start a movement track until it is aligned.
  917. */
  918. if (!Ok_To_Move(BodyFacing)) {
  919. return(true);
  920. }
  921. #endif
  922. /*
  923. ** Determine the coordinate of the next cell to move into.
  924. */
  925. dest = Adjacent_Cell(Coord, facing);
  926. dir = Facing_Dir(facing);
  927. /*
  928. ** Set the facing correctly if it isn't already correct. This
  929. ** means starting a rotation track if necessary.
  930. */
  931. facediff = PrimaryFacing.Difference(dir);
  932. if (facediff) {
  933. /*
  934. ** Request a change of facing.
  935. */
  936. Do_Turn(dir);
  937. return(true);
  938. } else {
  939. /* NOTE: Beyond this point, actual track assignment can begin.
  940. **
  941. ** If the cell to move into is impassable (probably for some unexpected
  942. ** reason), then abort the path list and set the speed to zero. The
  943. ** next time this routine is called, a new path will be generated.
  944. */
  945. destcell = Coord_Cell(dest);
  946. Mark(MARK_UP);
  947. MoveType cando = Can_Enter_Cell(destcell, facing);
  948. Mark(MARK_DOWN);
  949. if (cando != MOVE_OK) {
  950. if (Mission == MISSION_MOVE && House->IsHuman && Distance(NavCom) < 0x0200) {
  951. Assign_Destination(TARGET_NONE);
  952. }
  953. /*
  954. ** If a temporary friendly object is blocking the path, then cause it to
  955. ** get out of the way.
  956. */
  957. if (cando == MOVE_TEMP) {
  958. bool old = Special.IsScatter;
  959. Special.IsScatter = true;
  960. Map[destcell].Incoming(0, true);
  961. Special.IsScatter = old;
  962. }
  963. /*
  964. ** If a cloaked object is blocking, then shimmer the cell.
  965. */
  966. if (cando == MOVE_CLOAK) {
  967. Map[destcell].Shimmer();
  968. }
  969. Stop_Driver();
  970. if (cando != MOVE_MOVING_BLOCK) {
  971. Path[0] = FACING_NONE; // Path is blocked!
  972. }
  973. /*
  974. ** If blocked by a moving block then just exit start of move and
  975. ** try again next tick.
  976. */
  977. if (cando == MOVE_DESTROYABLE) {
  978. if (Map[destcell].Cell_Object()) {
  979. if (!House->Is_Ally(Map[destcell].Cell_Object())) {
  980. Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
  981. }
  982. } else {
  983. if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
  984. Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
  985. }
  986. }
  987. } else {
  988. if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
  989. }
  990. IsNewNavCom = false;
  991. TrackNumber = -1;
  992. return(true);
  993. }
  994. /*
  995. ** Determine the speed that the unit can travel to the desired square.
  996. */
  997. ground = Map[destcell].Land_Type();
  998. speed = Ground[ground].Cost[Class->Speed];
  999. if (!speed) speed = 128;
  1000. #ifdef NEVER
  1001. /*
  1002. ** Set the jiggle flag if the terrain would cause the unit
  1003. ** to jiggle when travelled over.
  1004. */
  1005. BaseF &= ~BASEF_JIGGLE;
  1006. if (Ground[ground].Jiggle) {
  1007. BaseF |= BASEF_JIGGLE;
  1008. }
  1009. #endif
  1010. /*
  1011. ** A damaged unit has a reduced speed.
  1012. */
  1013. if ((Class->MaxStrength>>1) > Strength) {
  1014. speed -= (speed>>2); // Three quarters speed.
  1015. }
  1016. if ((speed != Speed)/* || !SpeedAdd*/) {
  1017. Set_Speed(speed); // Full speed.
  1018. }
  1019. /*
  1020. ** Adjust speed depending on distance to ultimate movement target. The
  1021. ** further away the target is, the faster the vehicle will travel.
  1022. */
  1023. int dist = Distance(NavCom);
  1024. if (dist < 0x0200) {
  1025. speed = Fixed_To_Cardinal(speed, 0x00A0);
  1026. } else {
  1027. if (dist < 0x0700) {
  1028. speed = Fixed_To_Cardinal(speed, 0x00D0);
  1029. }
  1030. }
  1031. /*
  1032. ** Reserve the destination cell so that it won't become
  1033. ** occupied AS this unit is moving into it.
  1034. */
  1035. if (cando != MOVE_OK) {
  1036. Path[0] = FACING_NONE; // Path is blocked!
  1037. TrackNumber = -1;
  1038. dest = NULL;
  1039. } else {
  1040. Overrun_Square(Coord_Cell(dest), true);
  1041. /*
  1042. ** Determine which track to use (based on recorded path).
  1043. */
  1044. FacingType nextface = Path[1];
  1045. if (nextface == FACING_NONE) nextface = facing;
  1046. IsOnShortTrack = false;
  1047. TrackNumber = facing * FACING_COUNT + nextface;
  1048. if (TrackControl[TrackNumber].Track == 0) {
  1049. Path[0] = FACING_NONE;
  1050. TrackNumber = -1;
  1051. return(true);
  1052. } else {
  1053. if (TrackControl[TrackNumber].Flag & F_D) {
  1054. /*
  1055. ** If the middle cell of a two cell track contains a crate,
  1056. ** the check for goodies before movement starts.
  1057. */
  1058. if (!Map[destcell].Goodie_Check(this)) {
  1059. cando = MOVE_NO;
  1060. } else {
  1061. dest = Adjacent_Cell(dest, nextface);
  1062. destcell = Coord_Cell(dest);
  1063. cando = Can_Enter_Cell(destcell);
  1064. }
  1065. if (cando != MOVE_OK) {
  1066. /*
  1067. ** If a temporary friendly object is blocking the path, then cause it to
  1068. ** get out of the way.
  1069. */
  1070. if (cando == MOVE_TEMP) {
  1071. bool old = Special.IsScatter;
  1072. Special.IsScatter = true;
  1073. Map[destcell].Incoming(0, true);
  1074. Special.IsScatter = old;
  1075. }
  1076. /*
  1077. ** If a cloaked object is blocking, then shimmer the cell.
  1078. */
  1079. if (cando == MOVE_CLOAK) {
  1080. Map[destcell].Shimmer();
  1081. }
  1082. Path[0] = FACING_NONE; // Path is blocked!
  1083. TrackNumber = -1;
  1084. dest = NULL;
  1085. if (cando == MOVE_DESTROYABLE) {
  1086. if (Map[destcell].Cell_Object()) {
  1087. if (!House->Is_Ally(Map[destcell].Cell_Object())) {
  1088. Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
  1089. }
  1090. } else {
  1091. if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
  1092. Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
  1093. }
  1094. }
  1095. IsNewNavCom = false;
  1096. TrackIndex = 0;
  1097. return(true);
  1098. }
  1099. } else {
  1100. memcpy(&Path[0], &Path[2], CONQUER_PATH_MAX-2);
  1101. Path[CONQUER_PATH_MAX-2] = FACING_NONE;
  1102. IsPlanningToLook = true;
  1103. }
  1104. } else {
  1105. memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1);
  1106. }
  1107. Path[CONQUER_PATH_MAX-1] = FACING_NONE;
  1108. }
  1109. }
  1110. IsNewNavCom = false;
  1111. TrackIndex = 0;
  1112. if (!Start_Driver(dest)) {
  1113. TrackNumber = -1;
  1114. Path[0] = FACING_NONE;
  1115. Set_Speed(0);
  1116. }
  1117. }
  1118. return(false);
  1119. }
  1120. /***********************************************************************************************
  1121. * DriveClass::AI -- Processes unit movement and rotation. *
  1122. * *
  1123. * This routine is used to process unit movement and rotation. It *
  1124. * functions autonomously from the script system. Thus, once a unit *
  1125. * is give rotation command or movement path, it will follow this *
  1126. * until specifically instructed to stop. The advantage of this *
  1127. * method is that it allows smooth movement of units, faster game *
  1128. * execution, and reduced script complexity (since actual movement *
  1129. * dynamics need not be controlled directly by the scripts). *
  1130. * *
  1131. * INPUT: none *
  1132. * *
  1133. * OUTPUT: none *
  1134. * *
  1135. * WARNINGS: This routine relies on the process control bits for the *
  1136. * specified unit (for speed reasons). Thus, only setting *
  1137. * movement, rotation, or path list will the unit perform *
  1138. * any physics. *
  1139. * *
  1140. * HISTORY: *
  1141. * 09/26/1993 JLB : Created. *
  1142. * 04/15/1994 JLB : Converted to member function. *
  1143. *=============================================================================================*/
  1144. void DriveClass::AI(void)
  1145. {
  1146. FootClass::AI();
  1147. /*
  1148. ** If the unit is following a track, then continue
  1149. ** to do so -- mindlessly.
  1150. */
  1151. if (TrackNumber != -1) {
  1152. /*
  1153. ** Perform the movement accumulation.
  1154. */
  1155. While_Moving();
  1156. if (!IsActive) return;
  1157. if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE)) {
  1158. Start_Of_Move();
  1159. While_Moving();
  1160. if (!IsActive) return;
  1161. }
  1162. } else {
  1163. /*
  1164. ** For tracked units that are rotating in place, perform the rotation now.
  1165. */
  1166. if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) {
  1167. if (PrimaryFacing.Rotation_Adjust((int)(Class->ROT * House->GroundspeedBias))) {
  1168. Mark(MARK_CHANGE);
  1169. }
  1170. if (!IsRotating) {
  1171. Per_Cell_Process(true);
  1172. if (!IsActive) return;
  1173. }
  1174. } else {
  1175. /*
  1176. ** The unit has no track to follow, but if there
  1177. ** is a navigation target or a remaining path,
  1178. ** then start on a new track.
  1179. */
  1180. if (Mission != MISSION_GUARD || NavCom != TARGET_NONE) {
  1181. if (Target_Legal(NavCom) || Path[0] != FACING_NONE) {
  1182. Start_Of_Move();
  1183. While_Moving();
  1184. if (!IsActive) return;
  1185. } else {
  1186. Stop_Driver();
  1187. }
  1188. }
  1189. }
  1190. }
  1191. }
  1192. /***********************************************************************************************
  1193. * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
  1194. * *
  1195. * This routine modifies the path of the specified unit so that it *
  1196. * will not start out with a rotation. This is necessary for those *
  1197. * vehicles that have difficulty with rotating in place. Typically, *
  1198. * this includes wheeled vehicles. *
  1199. * *
  1200. * INPUT: unit -- Pointer to the unit to adjust. *
  1201. * *
  1202. * path -- Pointer to path structure. *
  1203. * *
  1204. * OUTPUT: none *
  1205. * *
  1206. * WARNINGS: Only units that require a fixup get modified. The *
  1207. * modification only occurs, if there is a legal path to *
  1208. * do so. *
  1209. * *
  1210. * HISTORY: *
  1211. * 04/03/1994 JLB : Created. *
  1212. * 04/06/1994 JLB : Uses path structure. *
  1213. * 04/10/1994 JLB : Diagonal smooth turn added. *
  1214. * 04/15/1994 JLB : Converted to member function. *
  1215. *=============================================================================================*/
  1216. void DriveClass::Fixup_Path(PathType *path)
  1217. {
  1218. FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements.
  1219. int facediff; // The facing difference value (0..4 | 0..-4).
  1220. static FacingType _path[4][6] = {
  1221. {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0},
  1222. {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
  1223. {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
  1224. {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
  1225. };
  1226. static FacingType _dpath[4][6] = {
  1227. {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0},
  1228. {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
  1229. {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0},
  1230. {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
  1231. };
  1232. int index;
  1233. int counter; // Path addition
  1234. FacingType *ptr; // Path list pointer.
  1235. FacingType *ptr2; // Copy of new path list pointer.
  1236. FacingType nextpath; // Next path value.
  1237. CELL cell; // Working cell value.
  1238. bool ok;
  1239. /*
  1240. ** Verify that the unit is valid and there is a path problem to resolve.
  1241. */
  1242. if (!path || path->Command[0] == FACING_NONE) {
  1243. return;
  1244. }
  1245. /*
  1246. ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns.
  1247. */
  1248. if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) {
  1249. return;
  1250. }
  1251. /*
  1252. ** If the original path starts in the same direction as the unit, then
  1253. ** there is no problem to resolve -- abort.
  1254. */
  1255. facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5;
  1256. if (!facediff) return;
  1257. if (Dir_Facing(PrimaryFacing) & FACING_NE) {
  1258. ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
  1259. counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
  1260. } else {
  1261. ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
  1262. counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
  1263. }
  1264. ptr2 = ptr;
  1265. ok = true; // Presume adjustment is all ok.
  1266. cell = Coord_Cell(Coord); // Starting cell.
  1267. nextpath = Dir_Facing(PrimaryFacing); // Starting path.
  1268. for (index = 0; index < counter; index++) {
  1269. /*
  1270. ** Determine next path element and add it to the
  1271. ** working path list.
  1272. */
  1273. if (facediff > 0) {
  1274. nextpath = nextpath + *ptr++;
  1275. } else {
  1276. nextpath = nextpath - *ptr++;
  1277. }
  1278. stage[index] = nextpath;
  1279. cell = Adjacent_Cell(cell, nextpath);
  1280. //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
  1281. /*
  1282. ** If it can't enter this cell, then abort the path
  1283. ** building operation without adjusting the unit's
  1284. ** path.
  1285. */
  1286. if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
  1287. ok = false;
  1288. break;
  1289. }
  1290. }
  1291. /*
  1292. ** If veering to the left was not successful, then try veering
  1293. ** to the right. This only makes sense if the vehicle is trying
  1294. ** to turn 180 degrees.
  1295. */
  1296. if (!ok && ABS(facediff) == 4) {
  1297. ptr = ptr2; // Pointer to path adjust list.
  1298. facediff = -facediff;
  1299. ok = true; // Presume adjustment is all ok.
  1300. cell = Coord_Cell(Coord); // Starting cell.
  1301. nextpath = Dir_Facing(PrimaryFacing); // Starting path.
  1302. for (index = 0; index < counter; index++) {
  1303. /*
  1304. ** Determine next path element and add it to the
  1305. ** working path list.
  1306. */
  1307. if (facediff > 0) {
  1308. nextpath = nextpath + *ptr++;
  1309. } else {
  1310. nextpath = nextpath - *ptr++;
  1311. }
  1312. stage[index] = nextpath;
  1313. cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
  1314. /*
  1315. ** If it can't enter this cell, then abort the path
  1316. ** building operation without adjusting the unit's
  1317. ** path.
  1318. */
  1319. if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
  1320. ok = false;
  1321. break;
  1322. }
  1323. }
  1324. }
  1325. /*
  1326. ** If a legal path addition was created, then install it in place
  1327. ** of the first path value. The initial path entry is to be replaced
  1328. ** with a sequence of path entries that create smooth turning.
  1329. */
  1330. if (ok) {
  1331. if (path->Length <= 1) {
  1332. movmem(&stage[0], path->Command, MAX(counter, 1));
  1333. path->Length = counter;
  1334. } else {
  1335. /*
  1336. ** Optimize the transition path step from the smooth turn
  1337. ** first part as it joins with the rest of the normal
  1338. ** path. The normal prefix path steps are NOT to be optimized.
  1339. */
  1340. if (counter) {
  1341. counter--;
  1342. path->Command[0] = stage[counter];
  1343. Optimize_Moves(path, MOVE_OK);
  1344. }
  1345. /*
  1346. ** If there is more than one prefix path element, then
  1347. ** insert the rest now.
  1348. */
  1349. if (counter) {
  1350. movmem(&path->Command[0], &path->Command[counter], 40-counter);
  1351. movmem(&stage[0], &path->Command[0], counter);
  1352. path->Length += counter;
  1353. }
  1354. }
  1355. path->Command[path->Length] = FACING_NONE;
  1356. }
  1357. }
  1358. /***********************************************************************************************
  1359. * DriveClass::Lay_Track -- Handles track laying logic for the unit. *
  1360. * *
  1361. * This routine handles the track laying for the unit. This entails examining the unit's *
  1362. * current location as well as the direction and whether this unit is allowed to lay *
  1363. * tracks in the first place. *
  1364. * *
  1365. * INPUT: none *
  1366. * *
  1367. * OUTPUT: none *
  1368. * *
  1369. * WARNINGS: none *
  1370. * *
  1371. * HISTORY: *
  1372. * 05/28/1994 JLB : Created. *
  1373. *=============================================================================================*/
  1374. void DriveClass::Lay_Track(void)
  1375. {
  1376. #ifdef NEVER
  1377. static IconCommandType *_trackdirs[8] = {
  1378. TrackN_S,
  1379. TrackNE_SW,
  1380. TrackE_W,
  1381. TrackNW_SE,
  1382. TrackN_S,
  1383. TrackNE_SW,
  1384. TrackE_W,
  1385. TrackNW_SE
  1386. };
  1387. if (!(ClassF & CLASSF_TRACKS)) return;
  1388. Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]);
  1389. #endif
  1390. }
  1391. /***********************************************************************************************
  1392. * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
  1393. * *
  1394. * This routine will ensure that the midpoint (if any) of the track that the unit is *
  1395. * following, will be marked according to the mark type specified. *
  1396. * *
  1397. * INPUT: headto -- The head to coordinate. *
  1398. * *
  1399. * type -- The type of marking to perform. *
  1400. * *
  1401. * OUTPUT: none *
  1402. * *
  1403. * WARNINGS: none *
  1404. * *
  1405. * HISTORY: *
  1406. * 07/30/1995 JLB : Created. *
  1407. *=============================================================================================*/
  1408. void DriveClass::Mark_Track(COORDINATE headto, MarkType type)
  1409. {
  1410. int value;
  1411. if (type == MARK_UP) {
  1412. value = false;
  1413. } else {
  1414. value = true;
  1415. }
  1416. if (headto) {
  1417. if (!IsOnShortTrack && TrackNumber != -1) {
  1418. /*
  1419. ** If we have not passed the per cell process point we need
  1420. ** to deal with it.
  1421. */
  1422. int tracknum = TrackControl[TrackNumber].Track;
  1423. if (tracknum) {
  1424. TrackType const * ptr = RawTracks[tracknum - 1].Track;
  1425. int cellidx = RawTracks[tracknum - 1].Cell;
  1426. if (cellidx > -1) {
  1427. DirType dir = ptr[cellidx].Facing;
  1428. if (TrackIndex < cellidx && cellidx != -1) {
  1429. COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, &dir);
  1430. CELL cell = Coord_Cell(offset);
  1431. if ((unsigned)cell < MAP_CELL_TOTAL) {
  1432. Map[cell].Flag.Occupy.Vehicle = value;
  1433. }
  1434. }
  1435. }
  1436. }
  1437. }
  1438. CELL cell = Coord_Cell(headto);
  1439. if ((unsigned)cell < MAP_CELL_TOTAL) {
  1440. Map[cell].Flag.Occupy.Vehicle = value;
  1441. }
  1442. }
  1443. }
  1444. /***********************************************************************************************
  1445. * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
  1446. * *
  1447. * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple *
  1448. * calls to this routine are needed in order to fully offload all Tiberium. *
  1449. * *
  1450. * INPUT: none *
  1451. * *
  1452. * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,*
  1453. * then this indicates that all Tiberium has been offloaded. *
  1454. * *
  1455. * WARNINGS: none *
  1456. * *
  1457. * HISTORY: *
  1458. * 07/19/1995 JLB : Created. *
  1459. *=============================================================================================*/
  1460. int DriveClass::Offload_Tiberium_Bail(void)
  1461. {
  1462. if (Tiberium) {
  1463. Tiberium--;
  1464. if (House->IsHuman) {
  1465. return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); // 25 in debugger
  1466. }
  1467. // MBL 05.14.2020: AI harvested credits fix for multiplayer, since they are miscalculated, and it's noticed
  1468. //
  1469. // return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); 708 in debugger
  1470. //
  1471. if (GameToPlay == GAME_NORMAL) // Non-multiplayer game, keep the original calculation; 708 in debugger
  1472. {
  1473. return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); // Original (708), wrong calcualation but preserving to not break missions
  1474. }
  1475. else // Multiplayer game, apply the 1/3 bonus credits correction, so not be as extreme; 33 in debugger
  1476. {
  1477. return((UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3))/UnitTypeClass::STEP_COUNT); // Corrected calculation
  1478. }
  1479. }
  1480. return(0);
  1481. }
  1482. /***********************************************************************************************
  1483. * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
  1484. * *
  1485. * This routine is used to verify that this object is allowed to move. Some objects can *
  1486. * be temporarily occupied and thus cannot move until the situation permits. *
  1487. * *
  1488. * INPUT: direction -- The direction that movement would be desired. *
  1489. * *
  1490. * OUTPUT: Can the unit move in the direction specified? *
  1491. * *
  1492. * WARNINGS: none *
  1493. * *
  1494. * HISTORY: *
  1495. * 07/29/1995 JLB : Created. *
  1496. *=============================================================================================*/
  1497. bool DriveClass::Ok_To_Move(DirType ) const
  1498. {
  1499. return true;
  1500. }
  1501. /***********************************************************************************************
  1502. * DriveClass::Class_Of -- Fetches a reference to the class type for this object. *
  1503. * *
  1504. * This routine will fetch a reference to the TypeClass of this object. *
  1505. * *
  1506. * INPUT: none *
  1507. * *
  1508. * OUTPUT: Returns with reference to the type class of this object. *
  1509. * *
  1510. * WARNINGS: none *
  1511. * *
  1512. * HISTORY: *
  1513. * 07/29/1995 JLB : Created. *
  1514. *=============================================================================================*/
  1515. ObjectTypeClass const & DriveClass::Class_Of(void) const
  1516. {
  1517. return *Class;
  1518. }
  1519. /***************************************************************************
  1520. ** Smooth turn track tables. These are coordinate offsets from the center
  1521. ** of the destination cell. These are the raw tracks that are modified
  1522. ** by negating the X and Y portions as necessary. Also for reverse travelling
  1523. ** direction, the track list can be processed backward.
  1524. **
  1525. ** Track 1 = N
  1526. ** Track 2 = NE
  1527. ** Track 3 = N->NE 45 deg (double path consumption)
  1528. ** Track 4 = N->E 90 deg (double path consumption)
  1529. ** Track 5 = NE->SE 90 deg (double path consumption)
  1530. ** Track 6 = NE->N 45 deg (double path consumption)
  1531. ** Track 7 = N->NE (facing change only)
  1532. ** Track 8 = NE->E (facing change only)
  1533. ** Track 9 = N->E (facing change only)
  1534. ** Track 10= NE->SE (facing change only)
  1535. ** Track 11= back up into refinery
  1536. ** Track 12= drive out of refinery
  1537. */
  1538. //#pragma warn -ias
  1539. DriveClass::TrackType const DriveClass::Track1[24] = {
  1540. {0x00F50000L,(DirType)0},
  1541. {0x00EA0000L,(DirType)0},
  1542. {0x00DF0000L,(DirType)0},
  1543. {0x00D40000L,(DirType)0},
  1544. {0x00C90000L,(DirType)0},
  1545. {0x00BE0000L,(DirType)0},
  1546. {0x00B30000L,(DirType)0},
  1547. {0x00A80000L,(DirType)0},
  1548. {0x009D0000L,(DirType)0},
  1549. {0x00920000L,(DirType)0},
  1550. {0x00870000L,(DirType)0},
  1551. {0x007C0000L,(DirType)0}, // Track jump check here.
  1552. {0x00710000L,(DirType)0},
  1553. {0x00660000L,(DirType)0},
  1554. {0x005B0000L,(DirType)0},
  1555. {0x00500000L,(DirType)0},
  1556. {0x00450000L,(DirType)0},
  1557. {0x003A0000L,(DirType)0},
  1558. {0x002F0000L,(DirType)0},
  1559. {0x00240000L,(DirType)0},
  1560. {0x00190000L,(DirType)0},
  1561. {0x000E0000L,(DirType)0},
  1562. {0x00030000L,(DirType)0},
  1563. {0x00000000L,(DirType)0}
  1564. };
  1565. DriveClass::TrackType const DriveClass::Track2[] = {
  1566. {0x00F8FF08L,(DirType)32},
  1567. {0x00F0FF10L,(DirType)32},
  1568. {0x00E8FF18L,(DirType)32},
  1569. {0x00E0FF20L,(DirType)32},
  1570. {0x00D8FF28L,(DirType)32},
  1571. {0x00D0FF30L,(DirType)32},
  1572. {0x00C8FF38L,(DirType)32},
  1573. {0x00C0FF40L,(DirType)32},
  1574. {0x00B8FF48L,(DirType)32},
  1575. {0x00B0FF50L,(DirType)32},
  1576. {0x00A8FF58L,(DirType)32},
  1577. {0x00A0FF60L,(DirType)32},
  1578. {0x0098FF68L,(DirType)32},
  1579. {0x0090FF70L,(DirType)32},
  1580. {0x0088FF78L,(DirType)32},
  1581. {0x0080FF80L,(DirType)32}, // Track jump check here.
  1582. {0x0078FF88L,(DirType)32},
  1583. {0x0070FF90L,(DirType)32},
  1584. {0x0068FF98L,(DirType)32},
  1585. {0x0060FFA0L,(DirType)32},
  1586. {0x0058FFA8L,(DirType)32},
  1587. {0x0050FFB0L,(DirType)32},
  1588. {0x0048FFB8L,(DirType)32},
  1589. {0x0040FFC0L,(DirType)32},
  1590. {0x0038FFC8L,(DirType)32},
  1591. {0x0030FFD0L,(DirType)32},
  1592. {0x0028FFD8L,(DirType)32},
  1593. {0x0020FFE0L,(DirType)32},
  1594. {0x0018FFE8L,(DirType)32},
  1595. {0x0010FFF0L,(DirType)32},
  1596. {0x0008FFF8L,(DirType)32},
  1597. {0x00000000L,(DirType)32}
  1598. };
  1599. DriveClass::TrackType const DriveClass::Track3[] = {
  1600. {0x01F5FF00L,(DirType)0},
  1601. {0x01EAFF00L,(DirType)0},
  1602. {0x01DFFF00L,(DirType)0},
  1603. {0x01D4FF00L,(DirType)0},
  1604. {0x01C9FF00L,(DirType)0},
  1605. {0x01BEFF00L,(DirType)0},
  1606. {0x01B3FF00L,(DirType)0},
  1607. {0x01A8FF00L,(DirType)0},
  1608. {0x019DFF00L,(DirType)0},
  1609. {0x0192FF00L,(DirType)0},
  1610. {0x0187FF00L,(DirType)0},
  1611. {0x0180FF00L,(DirType)0},
  1612. {0x0175FF00L,(DirType)0}, // Jump entry point here.
  1613. {0x016BFF00L,(DirType)0},
  1614. {0x0160FF02L,(DirType)1},
  1615. {0x0155FF04L,(DirType)3},
  1616. {0x014CFF06L,(DirType)4},
  1617. {0x0141FF08L,(DirType)5},
  1618. {0x0137FF0BL,(DirType)7},
  1619. {0x012EFF0FL,(DirType)8},
  1620. {0x0124FF13L,(DirType)9},
  1621. {0x011AFF17L,(DirType)11},
  1622. {0x0110FF1BL,(DirType)12},
  1623. {0x0107FF1FL,(DirType)13}, // Center cell processing here.
  1624. {0x00FCFF24L,(DirType)15},
  1625. {0x00F3FF28L,(DirType)16},
  1626. {0x00ECFF2CL,(DirType)17},
  1627. {0x00E0FF32L,(DirType)19},
  1628. {0x00D7FF36L,(DirType)20},
  1629. {0x00CFFF3DL,(DirType)21},
  1630. {0x00C6FF42L,(DirType)23},
  1631. {0x00BAFF49L,(DirType)24},
  1632. {0x00B0FF4DL,(DirType)25},
  1633. {0x00A8FF58L,(DirType)27},
  1634. {0x00A0FF60L,(DirType)28},
  1635. {0x0098FF68L,(DirType)29},
  1636. {0x0090FF70L,(DirType)31},
  1637. {0x0088FF78L,(DirType)32},
  1638. {0x0080FF80L,(DirType)32}, // Track jump check here.
  1639. {0x0078FF88L,(DirType)32},
  1640. {0x0070FF90L,(DirType)32},
  1641. {0x0068FF98L,(DirType)32},
  1642. {0x0060FFA0L,(DirType)32},
  1643. {0x0058FFA8L,(DirType)32},
  1644. {0x0050FFB0L,(DirType)32},
  1645. {0x0048FFB8L,(DirType)32},
  1646. {0x0040FFC0L,(DirType)32},
  1647. {0x0038FFC8L,(DirType)32},
  1648. {0x0030FFD0L,(DirType)32},
  1649. {0x0028FFD8L,(DirType)32},
  1650. {0x0020FFE0L,(DirType)32},
  1651. {0x0018FFE8L,(DirType)32},
  1652. {0x0010FFF0L,(DirType)32},
  1653. {0x0008FFF8L,(DirType)32},
  1654. {0x00000000L,(DirType)32}
  1655. };
  1656. DriveClass::TrackType const DriveClass::Track4[] = {
  1657. {0x00F5FF00L,(DirType)0},
  1658. {0x00EBFF00L,(DirType)0},
  1659. {0x00E0FF00L,(DirType)0},
  1660. {0x00D5FF00L,(DirType)0},
  1661. {0x00CBFF01L,(DirType)0},
  1662. {0x00C0FF03L,(DirType)0},
  1663. {0x00B5FF05L,(DirType)1},
  1664. {0x00ABFF07L,(DirType)1},
  1665. {0x00A0FF0AL,(DirType)2},
  1666. {0x0095FF0DL,(DirType)3},
  1667. {0x008BFF10L,(DirType)4},
  1668. {0x0080FF14L,(DirType)5}, // Track entry here.
  1669. {0x0075FF18L,(DirType)8},
  1670. {0x006DFF1CL,(DirType)12},
  1671. {0x0063FF22L,(DirType)16},
  1672. {0x005AFF25L,(DirType)20},
  1673. {0x0052FF2BL,(DirType)23},
  1674. {0x0048FF32L,(DirType)27},
  1675. {0x0040FF37L,(DirType)32},
  1676. {0x0038FF3DL,(DirType)36},
  1677. {0x0030FF46L,(DirType)39},
  1678. {0x002BFF4FL,(DirType)43},
  1679. {0x0024FF58L,(DirType)47},
  1680. {0x0020FF60L,(DirType)51},
  1681. {0x001BFF6DL,(DirType)54},
  1682. {0x0017FF79L,(DirType)57},
  1683. {0x0014FF82L,(DirType)60}, // Track jump here.
  1684. {0x0011FF8FL,(DirType)62},
  1685. {0x000DFF98L,(DirType)63},
  1686. {0x0009FFA2L,(DirType)64},
  1687. {0x0006FFACL,(DirType)64},
  1688. {0x0004FFB5L,(DirType)66},
  1689. {0x0003FFC0L,(DirType)64},
  1690. {0x0002FFCBL,(DirType)64},
  1691. {0x0001FFD5L,(DirType)64},
  1692. {0x0000FFE0L,(DirType)64},
  1693. {0x0000FFEBL,(DirType)64},
  1694. {0x0000FFF5L,(DirType)64},
  1695. {0x00000000L,(DirType)64}
  1696. };
  1697. DriveClass::TrackType const DriveClass::Track5[] = {
  1698. {0xFFF8FE08L,(DirType)32},
  1699. {0xFFF0FE10L,(DirType)32},
  1700. {0xFFE8FE18L,(DirType)32},
  1701. {0xFFE0FE20L,(DirType)32},
  1702. {0xFFD8FE28L,(DirType)32},
  1703. {0xFFD0FE30L,(DirType)32},
  1704. {0xFFC8FE38L,(DirType)32},
  1705. {0xFFC0FE40L,(DirType)32},
  1706. {0xFFB8FE48L,(DirType)32},
  1707. {0xFFB0FE50L,(DirType)32},
  1708. {0xFFA8FE58L,(DirType)32},
  1709. {0xFFA0FE60L,(DirType)32},
  1710. {0xFF98FE68L,(DirType)32},
  1711. {0xFF90FE70L,(DirType)32},
  1712. {0xFF88FE78L,(DirType)32},
  1713. {0xFF80FE80L,(DirType)32}, // Track entry here.
  1714. {0xFF78FE88L,(DirType)32},
  1715. {0xFF71FE90L,(DirType)32},
  1716. {0xFF6AFE97L,(DirType)32},
  1717. {0xFF62FE9FL,(DirType)32},
  1718. {0xFF5AFEA8L,(DirType)32},
  1719. {0xFF53FEB0L,(DirType)35},
  1720. {0xFF4BFEB7L,(DirType)38},
  1721. {0xFF44FEBEL,(DirType)41},
  1722. {0xFF3EFEC4L,(DirType)44},
  1723. {0xFF39FECEL,(DirType)47},
  1724. {0xFF34FED8L,(DirType)50},
  1725. {0xFF30FEE0L,(DirType)53},
  1726. {0xFF2DFEEBL,(DirType)56},
  1727. {0xFF2CFEF5L,(DirType)59},
  1728. {0xFF2BFF00L,(DirType)62},
  1729. {0xFF2CFF0BL,(DirType)66},
  1730. {0xFF2DFF15L,(DirType)69},
  1731. {0xFF30FF1FL,(DirType)72},
  1732. {0xFF34FF28L,(DirType)75},
  1733. {0xFF39FF30L,(DirType)78},
  1734. {0xFF3EFF3AL,(DirType)81},
  1735. {0xFF44FF44L,(DirType)84},
  1736. {0xFF4BFF4BL,(DirType)87},
  1737. {0xFF53FF50L,(DirType)90},
  1738. {0xFF5AFF58L,(DirType)93},
  1739. {0xFF62FF60L,(DirType)96},
  1740. {0xFF6AFF68L,(DirType)96},
  1741. {0xFF71FF70L,(DirType)96},
  1742. {0xFF78FF78L,(DirType)96},
  1743. {0xFF80FF80L,(DirType)96}, // Track jump check here.
  1744. {0xFF88FF88L,(DirType)96},
  1745. {0xFF90FF90L,(DirType)96},
  1746. {0xFF98FF98L,(DirType)96},
  1747. {0xFFA0FFA0L,(DirType)96},
  1748. {0xFFA8FFA8L,(DirType)96},
  1749. {0xFFB0FFB0L,(DirType)96},
  1750. {0xFFB8FFB8L,(DirType)96},
  1751. {0xFFC0FFC0L,(DirType)96},
  1752. {0xFFC8FFC8L,(DirType)96},
  1753. {0xFFD0FFD0L,(DirType)96},
  1754. {0xFFD8FFD8L,(DirType)96},
  1755. {0xFFE0FFE0L,(DirType)96},
  1756. {0xFFE8FFE8L,(DirType)96},
  1757. {0xFFF0FFF0L,(DirType)96},
  1758. {0xFFF8FFF8L,(DirType)96},
  1759. {0x00000000L,(DirType)96}
  1760. };
  1761. DriveClass::TrackType const DriveClass::Track6[] = {
  1762. {0x0100FE00L,(DirType)32},
  1763. {0x00F8FE08L,(DirType)32},
  1764. {0x00F0FE10L,(DirType)32},
  1765. {0x00E8FE18L,(DirType)32},
  1766. {0x00E0FE20L,(DirType)32},
  1767. {0x00D8FE28L,(DirType)32},
  1768. {0x00D0FE30L,(DirType)32},
  1769. {0x00C8FE38L,(DirType)32},
  1770. {0x00C0FE40L,(DirType)32},
  1771. {0x00B8FE48L,(DirType)32},
  1772. {0x00B0FE50L,(DirType)32},
  1773. {0x00A8FE58L,(DirType)32},
  1774. {0x00A0FE60L,(DirType)32},
  1775. {0x0098FE68L,(DirType)32},
  1776. {0x0090FE70L,(DirType)32},
  1777. {0x0088FE78L,(DirType)32},
  1778. {0x0080FE80L,(DirType)32}, // Jump entry point here.
  1779. {0x0078FE88L,(DirType)32},
  1780. {0x0070FE90L,(DirType)32},
  1781. {0x0068FE98L,(DirType)32},
  1782. {0x0060FEA0L,(DirType)32},
  1783. {0x0058FEA8L,(DirType)32},
  1784. {0x0055FEAEL,(DirType)32},
  1785. {0x004EFEB8L,(DirType)35},
  1786. {0x0048FEC0L,(DirType)37},
  1787. {0x0042FEC9L,(DirType)40},
  1788. {0x003BFED2L,(DirType)43},
  1789. {0x0037FEDAL,(DirType)45},
  1790. {0x0032FEE3L,(DirType)48},
  1791. {0x002BFEEBL,(DirType)51},
  1792. {0x0026FEF5L,(DirType)53},
  1793. {0x0022FEFEL,(DirType)56},
  1794. {0x001CFF08L,(DirType)59},
  1795. {0x0019FF12L,(DirType)61},
  1796. {0x0015FF1BL,(DirType)64},
  1797. {0x0011FF26L,(DirType)64},
  1798. {0x000EFF30L,(DirType)64},
  1799. {0x000BFF39L,(DirType)64},
  1800. {0x0009FF43L,(DirType)64},
  1801. {0x0007FF4EL,(DirType)64},
  1802. {0x0005FF57L,(DirType)64},
  1803. {0x0003FF62L,(DirType)64},
  1804. {0x0001FF6DL,(DirType)64},
  1805. {0x0000FF77L,(DirType)64},
  1806. {0x0000FF80L,(DirType)64}, // Track jump check here.
  1807. {0x0000FF8BL,(DirType)64},
  1808. {0x0000FF95L,(DirType)64},
  1809. {0x0000FFA0L,(DirType)64},
  1810. {0x0000FFABL,(DirType)64},
  1811. {0x0000FFB5L,(DirType)64},
  1812. {0x0000FFC0L,(DirType)64},
  1813. {0x0000FFCBL,(DirType)64},
  1814. {0x0000FFD5L,(DirType)64},
  1815. {0x0000FFE0L,(DirType)64},
  1816. {0x0000FFEBL,(DirType)64},
  1817. {0x0000FFF5L,(DirType)64},
  1818. {0x00000000L,(DirType)64}
  1819. };
  1820. DriveClass::TrackType const DriveClass::Track7[] = {
  1821. {0x0006FFFFL,(DirType)0},
  1822. {0x000CFFFEL,(DirType)4},
  1823. {0x0011FFFCL,(DirType)8},
  1824. {0x0018FFFAL,(DirType)12},
  1825. {0x001FFFF6L,(DirType)16},
  1826. {0x0024FFF3L,(DirType)19},
  1827. {0x002BFFF0L,(DirType)22},
  1828. {0x0030FFFDL,(DirType)23},
  1829. {0x0035FFEBL,(DirType)24},
  1830. {0x0038FFE8L,(DirType)25},
  1831. {0x003CFFE6L,(DirType)26},
  1832. {0x0040FFE3L,(DirType)27},
  1833. {0x0043FFE0L,(DirType)28},
  1834. {0x0046FFDDL,(DirType)29},
  1835. {0x0043FFDFL,(DirType)30},
  1836. {0x0040FFE1L,(DirType)30},
  1837. {0x003CFFE3L,(DirType)30},
  1838. {0x0038FFE5L,(DirType)30},
  1839. {0x0035FFE7L,(DirType)31},
  1840. {0x0030FFE9L,(DirType)31},
  1841. {0x002BFFEBL,(DirType)31},
  1842. {0x0024FFEDL,(DirType)31},
  1843. {0x001FFFF1L,(DirType)31},
  1844. {0x0018FFF4L,(DirType)32},
  1845. {0x0011FFF7L,(DirType)32},
  1846. {0x000CFFFAL,(DirType)32},
  1847. {0x0006FFFDL,(DirType)32},
  1848. {0x00000000L,(DirType)32}
  1849. };
  1850. DriveClass::TrackType const DriveClass::Track8[] = {
  1851. {0x0003FFFCL,(DirType)32},
  1852. {0x0006FFF7L,(DirType)36},
  1853. {0x000AFFF1L,(DirType)40},
  1854. {0x000CFFEBL,(DirType)44},
  1855. {0x000DFFE4L,(DirType)46},
  1856. {0x000EFFDCL,(DirType)48},
  1857. {0x000FFFD5L,(DirType)50},
  1858. {0x0010FFD0L,(DirType)52},
  1859. {0x0011FFC9L,(DirType)54},
  1860. {0x0012FFC2L,(DirType)56},
  1861. {0x0011FFC0L,(DirType)58},
  1862. {0x0010FFC2L,(DirType)60},
  1863. {0x000EFFC9L,(DirType)62},
  1864. {0x000CFFCFL,(DirType)64},
  1865. {0x000AFFD5L,(DirType)64},
  1866. {0x0008FFDAL,(DirType)64},
  1867. {0x0006FFE2L,(DirType)64},
  1868. {0x0004FFE9L,(DirType)64},
  1869. {0x0002FFEFL,(DirType)64},
  1870. {0x0001FFF5L,(DirType)64},
  1871. {0x0000FFF9L,(DirType)64},
  1872. {0x00000000L,(DirType)64}
  1873. };
  1874. DriveClass::TrackType const DriveClass::Track9[] = {
  1875. {0xFFF50002L,(DirType)0},
  1876. {0xFFEB0004L,(DirType)2},
  1877. {0xFFE00006L,(DirType)4},
  1878. {0xFFD50009L,(DirType)6},
  1879. {0xFFCE000CL,(DirType)9},
  1880. {0xFFC8000FL,(DirType)11},
  1881. {0xFFC00012L,(DirType)13},
  1882. {0xFFB80015L,(DirType)16},
  1883. {0xFFC00012L,(DirType)18},
  1884. {0xFFC8000EL,(DirType)20},
  1885. {0xFFCE000AL,(DirType)22},
  1886. {0xFFD50004L,(DirType)24},
  1887. {0xFFDE0000L,(DirType)26},
  1888. {0xFFE9FFF8L,(DirType)28},
  1889. {0xFFEEFFF2L,(DirType)30},
  1890. {0xFFF5FFEBL,(DirType)32},
  1891. {0xFFFDFFE1L,(DirType)34},
  1892. {0x0002FFD8L,(DirType)36},
  1893. {0x0007FFD2L,(DirType)39},
  1894. {0x000BFFCBL,(DirType)41},
  1895. {0x0010FFC5L,(DirType)43},
  1896. {0x0013FFBEL,(DirType)45},
  1897. {0x0015FFB7L,(DirType)48},
  1898. {0x0013FFBEL,(DirType)50},
  1899. {0x0011FFC5L,(DirType)52},
  1900. {0x000BFFCCL,(DirType)54},
  1901. {0x0008FFD4L,(DirType)56},
  1902. {0x0005FFDFL,(DirType)58},
  1903. {0x0003FFEBL,(DirType)62},
  1904. {0x0001FFF5L,(DirType)64},
  1905. {0x00000000L,(DirType)64}
  1906. };
  1907. DriveClass::TrackType const DriveClass::Track10[] = {
  1908. {0xFFF6000BL,(DirType)32},
  1909. {0xFFF00015L,(DirType)37},
  1910. {0xFFEB0020L,(DirType)42},
  1911. {0xFFE9002BL,(DirType)47},
  1912. {0xFFE50032L,(DirType)52},
  1913. {0xFFE30038L,(DirType)57},
  1914. {0xFFE00040L,(DirType)60},
  1915. {0xFFE20038L,(DirType)62},
  1916. {0xFFE40032L,(DirType)64},
  1917. {0xFFE5002AL,(DirType)68},
  1918. {0xFFE6001EL,(DirType)70},
  1919. {0xFFE70015L,(DirType)72},
  1920. {0xFFE8000BL,(DirType)74},
  1921. {0xFFE90000L,(DirType)76},
  1922. {0xFFE8FFF5L,(DirType)78},
  1923. {0xFFE7FFEBL,(DirType)80},
  1924. {0xFFE6FFE0L,(DirType)82},
  1925. {0xFFE5FFD5L,(DirType)84},
  1926. {0xFFE4FFCEL,(DirType)86},
  1927. {0xFFE2FFC5L,(DirType)88},
  1928. {0xFFE0FFC0L,(DirType)90},
  1929. {0xFFE3FFC5L,(DirType)92},
  1930. {0xFFE5FFCEL,(DirType)94},
  1931. {0xFFE9FFD5L,(DirType)95},
  1932. {0xFFEBFFE0L,(DirType)96},
  1933. {0xFFF0FFEBL,(DirType)96},
  1934. {0xFFF6FFF5L,(DirType)96},
  1935. {0x00000000L,(DirType)96}
  1936. };
  1937. DriveClass::TrackType const DriveClass::Track11[] = {
  1938. {0x01000000L,DIR_SW},
  1939. {0x00F30008L,DIR_SW},
  1940. {0x00E50010L,DIR_SW_X1},
  1941. {0x00D60018L,DIR_SW_X1},
  1942. {0x00C80020L,DIR_SW_X1},
  1943. {0x00B90028L,DIR_SW_X1},
  1944. {0x00AB0030L,DIR_SW_X2},
  1945. {0x009C0038L,DIR_SW_X2},
  1946. {0x008D0040L,DIR_SW_X2},
  1947. {0x007F0048L,DIR_SW_X2},
  1948. {0x00710050L,DIR_SW_X2},
  1949. {0x00640058L,DIR_SW_X2},
  1950. {0x00550060L,DIR_SW_X2},
  1951. {0x00000000L,DIR_SW_X2}
  1952. };
  1953. DriveClass::TrackType const DriveClass::Track12[] = {
  1954. {0xFF550060L,DIR_SW_X2},
  1955. {0xFF640058L,DIR_SW_X2},
  1956. {0xFF710050L,DIR_SW_X2},
  1957. {0xFF7F0048L,DIR_SW_X2},
  1958. {0xFF8D0040L,DIR_SW_X2},
  1959. {0xFF9C0038L,DIR_SW_X2},
  1960. {0xFFAB0030L,DIR_SW_X2},
  1961. {0xFFB90028L,DIR_SW_X1},
  1962. {0xFFC80020L,DIR_SW_X1},
  1963. {0xFFD60018L,DIR_SW_X1},
  1964. {0xFFE50010L,DIR_SW_X1},
  1965. {0xFFF30008L,DIR_SW},
  1966. {0x00000000L,DIR_SW}
  1967. };
  1968. /*
  1969. ** Drive out of weapon's factory.
  1970. */
  1971. DriveClass::TrackType const DriveClass::Track13[] = {
  1972. {XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
  1973. {XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
  1974. {XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
  1975. {XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
  1976. {XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
  1977. {XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
  1978. {XYP_COORD(9,-17),(DirType)(DIR_SW-10)},
  1979. {XYP_COORD(8,-16),(DirType)(DIR_SW-10)},
  1980. {XYP_COORD(8,-15),(DirType)(DIR_SW-10)},
  1981. {XYP_COORD(7,-14),(DirType)(DIR_SW-10)},
  1982. {XYP_COORD(7,-13),(DirType)(DIR_SW-10)},
  1983. {XYP_COORD(6,-12),(DirType)(DIR_SW-10)},
  1984. {XYP_COORD(6,-11),(DirType)(DIR_SW-10)},
  1985. {XYP_COORD(5,-10),(DirType)(DIR_SW-10)},
  1986. {XYP_COORD(5,-9),(DirType)(DIR_SW-10)},
  1987. {XYP_COORD(4,-8),(DirType)(DIR_SW-10)},
  1988. {XYP_COORD(4,-7),(DirType)(DIR_SW-10)},
  1989. {XYP_COORD(3,-6),(DirType)(DIR_SW-10)},
  1990. {XYP_COORD(3,-5),(DirType)(DIR_SW-9)},
  1991. {XYP_COORD(2,-4),(DirType)(DIR_SW-7)},
  1992. {XYP_COORD(2,-3),(DirType)(DIR_SW-5)},
  1993. {XYP_COORD(1,-2),(DirType)(DIR_SW-3)},
  1994. {XYP_COORD(1,-1),(DirType)(DIR_SW-1)},
  1995. {0x00000000L,DIR_SW}
  1996. };
  1997. /*
  1998. ** There are a limited basic number of tracks that a vehicle can follow. These
  1999. ** are they. Each track can be interpreted differently but this is controlled
  2000. ** by the TrackControl structure elaborated elsewhere.
  2001. */
  2002. DriveClass::RawTrackType const DriveClass::RawTracks[13] = {
  2003. {Track1, -1, 0, -1},
  2004. {Track2, -1, 0, -1},
  2005. {Track3, 37, 12, 22},
  2006. {Track4, 26, 11, 19},
  2007. {Track5, 45, 15, 31},
  2008. {Track6, 44, 16, 27},
  2009. {Track7, -1, 0, -1},
  2010. {Track8, -1, 0, -1},
  2011. {Track9, -1, 0, -1},
  2012. {Track10, -1, 0, -1},
  2013. {Track11, -1, 0, -1},
  2014. {Track12, -1, 0, -1},
  2015. {Track13, -1, 0, -1}
  2016. };
  2017. /***************************************************************************
  2018. ** Smooth turning control table. Given two directions in a path list, this
  2019. ** table determines which track to use and what modifying operations need
  2020. ** be performed on the track data.
  2021. */
  2022. DriveClass::TurnTrackType const DriveClass::TrackControl[67] = {
  2023. {1, 0, DIR_N, F_}, // 0-0
  2024. {3, 7, DIR_NE, F_D}, // 0-1 (raw chart)
  2025. {4, 9, DIR_E, F_D}, // 0-2 (raw chart)
  2026. {0, 0, DIR_SE, F_}, // 0-3 !
  2027. {0, 0, DIR_S, F_}, // 0-4 !
  2028. {0, 0, DIR_SW, F_}, // 0-5 !
  2029. {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6
  2030. {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7
  2031. {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0
  2032. {2, 0, DIR_NE, F_}, // 1-1 (raw chart)
  2033. {6, 8, DIR_E, F_D}, // 1-2 (raw chart)
  2034. {5, 10, DIR_SE, F_D}, // 1-3 (raw chart)
  2035. {0, 0, DIR_S, F_}, // 1-4 !
  2036. {0, 0, DIR_SW, F_}, // 1-5 !
  2037. {0, 0, DIR_W, F_}, // 1-6 !
  2038. {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7
  2039. {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0
  2040. {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1
  2041. {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2
  2042. {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3
  2043. {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4
  2044. {0, 0, DIR_SW, F_}, // 2-5 !
  2045. {0, 0, DIR_W, F_}, // 2-6 !
  2046. {0, 0, DIR_NW, F_}, // 2-7 !
  2047. {0, 0, DIR_N, F_}, // 3-0 !
  2048. {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1
  2049. {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2
  2050. {2, 0, DIR_SE, F_Y}, // 3-3
  2051. {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4
  2052. {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5
  2053. {0, 0, DIR_W, F_}, // 3-6 !
  2054. {0, 0, DIR_NW, F_}, // 3-7 !
  2055. {0, 0, DIR_N, F_}, // 4-0 !
  2056. {0, 0, DIR_NE, F_}, // 4-1 !
  2057. {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2
  2058. {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3
  2059. {1, 0, DIR_S, F_Y}, // 4-4
  2060. {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5
  2061. {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6
  2062. {0, 0, DIR_NW, F_}, // 4-7 !
  2063. {0, 0, DIR_N, F_}, // 5-0 !
  2064. {0, 0, DIR_NE, F_}, // 5-1 !
  2065. {0, 0, DIR_E, F_}, // 5-2 !
  2066. {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3
  2067. {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4
  2068. {2, 0, DIR_SW, F_T}, // 5-5
  2069. {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6
  2070. {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7
  2071. {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0
  2072. {0, 0, DIR_NE, F_}, // 6-1 !
  2073. {0, 0, DIR_E, F_}, // 6-2 !
  2074. {0, 0, DIR_SE, F_}, // 6-3 !
  2075. {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4
  2076. {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5
  2077. {1, 0, DIR_W, F_T}, // 6-6
  2078. {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7
  2079. {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0
  2080. {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1
  2081. {0, 0, DIR_E, F_}, // 7-2 !
  2082. {0, 0, DIR_SE, F_}, // 7-3 !
  2083. {0, 0, DIR_S, F_}, // 7-4 !
  2084. {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5
  2085. {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6
  2086. {2, 0, DIR_NW, F_X}, // 7-7
  2087. {11, 11, DIR_SW, F_}, // Backup harvester into refinery.
  2088. {12, 12, DIR_SW_X2, F_}, // Drive back into refinery.
  2089. {13, 13, DIR_SW, F_} // Drive out of weapons factory.
  2090. };