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