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