FOOT.CPP (128486B)
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/FOOT.CPP 2 3/06/97 1:46p 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 : FOOT.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : April 22, 1994 * 28 * * 29 * Last Update : October 5, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * FootClass::AI -- Handle general movement AI. * 34 * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * 35 * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * 36 * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * 37 * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * 38 * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * 39 * FootClass::Basic_Path -- Finds the basic path for a ground object. * 40 * FootClass::Body_Facing -- Set the body rotation/facing. * 41 * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * 42 * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * 43 * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * 44 * FootClass::Death_Announcement -- Announces the death of a unit. * 45 * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * 46 * FootClass::Detach -- Detaches a target from tracking systems. * 47 * FootClass::Detach_All -- Removes this object from the game system. * 48 * FootClass::Enters_Building -- When unit enters a building for some reason. * 49 * FootClass::FootClass -- Normal constructor for the foot class object. * 50 * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * 51 * FootClass::Handle_Navigation_List -- Processes the navigation queue. * 52 * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * 53 * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority* 54 * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * 55 * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * 56 * FootClass::Mark -- Unit interface to map rendering system. * 57 * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * 58 * FootClass::Mission_Capture -- Handles the capture mission. * 59 * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * 60 * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * 61 * FootClass::Mission_Hunt -- Handles the default hunt order. * 62 * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * 63 * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * 64 * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * 65 * FootClass::Override_Mission -- temporarily overrides a units mission * 66 * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * 67 * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * 68 * FootClass::Receive_Message -- Movement related radio messages are handled here. * 69 * FootClass::Rescue_Mission -- Calls this unit to the rescue. * 70 * FootClass::Restore_Mission -- Restores an overridden mission * 71 * FootClass::Sell_Back -- Causes this object to be sold back. * 72 * FootClass::Set_Speed -- Initiate unit movement physics. * 73 * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * 74 * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * 75 * FootClass::Stop_Driver -- This routine clears the driving state of the object. * 76 * FootClass::Stun -- Prepares a ground travelling object for removal. * 77 * FootClass::Take_Damage -- Handles taking damage to this object. * 78 * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * 79 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 80 81 #include "function.h" 82 83 84 /*********************************************************************************************** 85 * FootClass::FootClass -- Default constructor for foot class objects. * 86 * * 87 * This is the default constructor for FootClass objects. It sets the foot class values to * 88 * their default starting settings. * 89 * * 90 * INPUT: none * 91 * * 92 * OUTPUT: none * 93 * * 94 * WARNINGS: none * 95 * * 96 * HISTORY: * 97 * 11/23/1994 JLB : Created. * 98 *=============================================================================================*/ 99 FootClass::FootClass(RTTIType rtti, int id, HousesType house) : 100 TechnoClass(rtti, id, house), 101 IsScanLimited(false), 102 IsInitiated(false), 103 IsNewNavCom(false), 104 IsPlanningToLook(false), 105 IsDeploying(false), 106 IsFiring(false), 107 IsRotating(false), 108 IsDriving(false), 109 IsUnloading(false), 110 IsFormationMove(false), 111 IsNavQueueLoop(false), 112 IsScattering(false), 113 IsMovingOntoBridge(false), 114 Speed(0), 115 SpeedBias(1), 116 XFormOffset(0x80000000), 117 YFormOffset(0x80000000), 118 NavCom(TARGET_NONE), 119 SuspendedNavCom(TARGET_NONE), 120 Team(0), 121 Group(255), 122 Member(0), 123 PathThreshhold(MOVE_CLOAK), 124 PathDelay(0), 125 TryTryAgain(PATH_RETRY), 126 BaseAttackTimer(0), 127 FormationSpeed(SPEED_FOOT), 128 FormationMaxSpeed(MPH_IMMOBILE), 129 HeadToCoord(0) 130 { 131 Path[0] = FACING_NONE; 132 for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { 133 NavQueue[index] = TARGET_NONE; 134 } 135 } 136 137 138 #ifdef CHEAT_KEYS 139 /*********************************************************************************************** 140 * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * 141 * * 142 * This routine is used to output the current status of the foot class to the mono * 143 * monitor. Through this display bugs may be tracked down or eliminated. * 144 * * 145 * INPUT: none * 146 * * 147 * OUTPUT: none * 148 * * 149 * WARNINGS: none * 150 * * 151 * HISTORY: * 152 * 06/02/1994 JLB : Created. * 153 * 07/04/1995 JLB : Handles aircraft special case. * 154 *=============================================================================================*/ 155 void FootClass::Debug_Dump(MonoClass * mono) const 156 { 157 assert(IsActive); 158 159 mono->Fill_Attrib(53, 13, 12, 1, IsInitiated ? MonoClass::INVERSE : MonoClass::NORMAL); 160 mono->Fill_Attrib(1, 18, 12, 1, IsPlanningToLook ? MonoClass::INVERSE : MonoClass::NORMAL); 161 mono->Fill_Attrib(53, 14, 12, 1, IsDeploying ? MonoClass::INVERSE : MonoClass::NORMAL); 162 mono->Fill_Attrib(53, 15, 12, 1, IsFiring ? MonoClass::INVERSE : MonoClass::NORMAL); 163 mono->Fill_Attrib(53, 16, 12, 1, IsRotating ? MonoClass::INVERSE : MonoClass::NORMAL); 164 mono->Fill_Attrib(53, 17, 12, 1, IsDriving ? MonoClass::INVERSE : MonoClass::NORMAL); 165 mono->Fill_Attrib(53, 18, 12, 1, IsUnloading ? MonoClass::INVERSE : MonoClass::NORMAL); 166 mono->Fill_Attrib(27, 18, 12, 1, IsFormationMove ? MonoClass::INVERSE : MonoClass::NORMAL); 167 168 mono->Set_Cursor(45, 1);mono->Printf("%02X", Speed); 169 if (NavCom) { 170 mono->Set_Cursor(29, 5); 171 mono->Printf("%08X", NavCom); 172 } 173 if (SuspendedNavCom) { 174 mono->Set_Cursor(38, 5); 175 mono->Printf("%08X", SuspendedNavCom); 176 } 177 178 if (Team) Team->Debug_Dump(mono); 179 if (Group != 255) { 180 mono->Set_Cursor(59, 1);mono->Printf("%d", Group); 181 } 182 183 static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; 184 for (int index = 0; index < min(12, ARRAY_SIZE(Path)); index++) { 185 mono->Set_Cursor(54+index, 3); 186 mono->Printf("%s", _p2c[((ABS((int)Path[index]+1)) % ARRAY_SIZE(_p2c))]); 187 } 188 mono->Set_Cursor(54, 5);mono->Printf("%2d", PathThreshhold); 189 mono->Set_Cursor(72, 3);mono->Printf("%4d", (long)PathDelay); 190 mono->Set_Cursor(67, 3);mono->Printf("%3d", TryTryAgain); 191 if (HeadToCoord) { 192 mono->Set_Cursor(60, 5);mono->Printf("%08X", HeadToCoord); 193 } 194 195 TechnoClass::Debug_Dump(mono); 196 } 197 #endif 198 199 200 /*********************************************************************************************** 201 * FootClass::Set_Speed -- Initiate unit movement physics. * 202 * * 203 * This routine is used to set a unit's velocity control structure. * 204 * The game will then process the unit's movement during the momentum * 205 * physics calculation. * 206 * * 207 * INPUT: unit -- Pointer to the unit to alter. * 208 * * 209 * speed -- Throttle setting (0=stop, 255=full throttle). * 210 * * 211 * OUTPUT: none * 212 * * 213 * WARNINGS: none * 214 * * 215 * HISTORY: * 216 * 09/07/1992 JLB : Created. * 217 * 09/24/1993 JLB : Revised for faster speed. * 218 * 04/02/1994 JLB : Revised for new system. * 219 * 04/15/1994 JLB : Converted to member function. * 220 * 07/21/1994 JLB : Simplified. * 221 *=============================================================================================*/ 222 void FootClass::Set_Speed(int speed) 223 { 224 assert(IsActive); 225 226 speed &= 0xFF; 227 ((unsigned char &)Speed) = speed; 228 } 229 230 231 /*********************************************************************************************** 232 * FootClass::Mark -- Unit interface to map rendering system. * 233 * * 234 * This routine is the interface function for units as they relate to * 235 * the map rendering system. Whenever a unit's imagery changes, this * 236 * function is called. * 237 * * 238 * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * 239 * MARK_UP -- Unit is removed. * 240 * MARK_CHANGE -- Unit alters image but doesn't move. * 241 * MARK_DOWN -- Unit is overlaid onto existing icons. * 242 * * 243 * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * 244 * down when it is already down, or visa versa. * 245 * * 246 * WARNINGS: none * 247 * * 248 * HISTORY: * 249 * 09/14/1991 JLB : Created. * 250 * 04/15/1994 JLB : Converted to member function. * 251 * 12/23/1994 JLB : Performs low level check before processing. * 252 *=============================================================================================*/ 253 bool FootClass::Mark(MarkType mark) 254 { 255 assert(this != 0); 256 assert(IsActive); 257 258 if (TechnoClass::Mark(mark)) { 259 // short list[32]; 260 CELL cell = Coord_Cell(Coord); 261 262 #ifndef PARTIAL 263 if (In_Which_Layer() != LAYER_GROUND && (mark == MARK_UP || mark == MARK_DOWN)) mark = MARK_CHANGE; 264 #endif 265 266 /* 267 ** Inform the map of the refresh, occupation, and overlap 268 ** request. 269 */ 270 switch (mark) { 271 case MARK_UP: 272 Map.Pick_Up(cell, this); 273 break; 274 275 case MARK_DOWN: 276 Map.Place_Down(cell, this); 277 break; 278 279 case MARK_CHANGE_REDRAW: 280 Map.Refresh_Cells(cell, Overlap_List(true)); 281 Map.Refresh_Cells(cell, Occupy_List()); 282 break; 283 284 default: 285 Map.Refresh_Cells(cell, Overlap_List()); 286 Map.Refresh_Cells(cell, Occupy_List()); 287 break; 288 } 289 return(true); 290 } 291 return(false); 292 } 293 294 295 /*********************************************************************************************** 296 * FootClass::Basic_Path -- Finds the basic path for a ground object. * 297 * * 298 * This is a common routine used by both infantry and other ground travelling units. It * 299 * will fill in the unit's basic path to the NavCom destination. * 300 * * 301 * INPUT: none * 302 * * 303 * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * 304 * be found or the terrain prohibits the unit's movement. * 305 * * 306 * WARNINGS: none * 307 * * 308 * HISTORY: * 309 * 10/17/1994 JLB : Created. * 310 *=============================================================================================*/ 311 bool FootClass::Basic_Path(void) 312 { 313 assert(IsActive); 314 315 PathType * path; // Pointer to path control structure. 316 CELL cell; 317 int skip_path = false; 318 319 Path[0] = FACING_NONE; 320 321 if (Target_Legal(NavCom)) { 322 cell = As_Cell(NavCom); 323 324 /* 325 ** When the navigation computer is set to a location that is impassible, then 326 ** find a nearby cell that can be entered and try to head toward that instead. 327 ** EXCEPT when that cell is very close -- then just bail. 328 */ 329 int dist = Distance(NavCom); 330 int checkdist = Team.Is_Valid() ? Rule.StrayDistance : Rule.CloseEnoughDistance; 331 if (Can_Enter_Cell(cell) > MOVE_CLOAK && dist > checkdist) { 332 CELL cell2 = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); 333 if (cell2 != 0 && ::Distance(Cell_Coord(cell), Cell_Coord(cell2)) < dist) cell = cell2; 334 } 335 336 if (What_Am_I() == RTTI_INFANTRY) { 337 CELL mycell = Coord_Cell(Center_Coord()); 338 ObjectClass * obj = Map[mycell].Cell_Occupier(); 339 while (obj) { 340 if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { 341 InfantryClass * inf = (InfantryClass *)obj; 342 if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { 343 if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { 344 Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); 345 } else { 346 Mem_Copy(inf->Path, Path, sizeof(Path)); 347 } 348 if (Path[0] != FACING_NONE) { 349 skip_path = true; 350 } 351 break; 352 } 353 } 354 obj = obj->Next; 355 } 356 } 357 358 if (!skip_path) { 359 Mark(MARK_UP); 360 Path[0] = FACING_NONE; // Probably not necessary, but... 361 362 /* 363 ** Try to find a path to the destination. If a failure occurs, then keep trying 364 ** with greater determination until either a complete failure occurs, or a decent 365 ** path was found. 366 */ 367 bool found1=false; // Found a best path yet? 368 PathType path1; 369 FacingType workpath1[200]; // Staging area for path list. 370 // FacingType workpath2[200]; // Staging area for path list. 371 MoveType maxtype = MOVE_TEMP; 372 if (!House->IsHuman) { 373 maxtype = MOVE_TEMP; 374 // maxtype = MOVE_DESTROYABLE; 375 } else { 376 377 /* 378 ** For simple movement missions by the human player, then don't 379 ** consider friendly units as passable if close to the destination. 380 ** This will prevent a human controlled unit from just sitting next 381 ** to a destination just because there is another friendly unit 382 ** occupying the destination location. 383 */ 384 if (Mission == MISSION_MOVE && Distance(NavCom) < Rule.CloseEnoughDistance) { 385 maxtype = MOVE_DESTROYABLE; 386 } 387 } 388 389 /* 390 ** Try to find a path to the destination. If there is a path 391 ** failure, then try a more severe path method until the 392 ** maximum severity is reached. 393 */ 394 for (;;) { 395 path = Find_Path(cell, &workpath1[0], sizeof(workpath1), PathThreshhold); 396 if (path && path->Cost) { 397 memcpy(&path1, path, sizeof(path1)); 398 found1 = true; 399 break; 400 } 401 402 /* 403 ** A valid path was not found. Try the next greater path severity 404 ** level if the severity can be increased. If not, then consider this 405 ** a total failure. 406 */ 407 PathThreshhold++; 408 if (PathThreshhold > maxtype) break; 409 } 410 411 #ifdef NEVER 412 /* 413 ** Determine if ANY path could be calculated by first examining the most 414 ** aggressive case. If this fails, then no path will succeed. Further 415 ** scanning is unnecessary. 416 */ 417 path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); 418 if (path && path->Cost) { 419 memcpy(&path1, path, sizeof(path1)); 420 found1 = true; 421 422 /* 423 ** Scan for the best path possible. If this succeeds, then do a simple 424 ** comparison with the most aggressive path. If they are very close, then 425 ** go with the best (easiest) path method. 426 */ 427 path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); 428 if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { 429 memcpy(&path1, path, sizeof(path1)); 430 memcpy(workpath1, workpath2, sizeof(workpath1)); 431 } else { 432 433 /* 434 ** The easiest path method didn't result in a satisfactory path. Scan through 435 ** the rest of the path options, looking for the best one. 436 */ 437 for (MoveType move = (MoveType)(MOVE_CLOAK+1); move < (MoveType)(maxtype-1); move++) { 438 // for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype-1; move++) { 439 path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); 440 if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { 441 memcpy(&path1, path, sizeof(path1)); 442 memcpy(workpath1, workpath2, sizeof(workpath1)); 443 } 444 } 445 } 446 } 447 #endif 448 449 /* 450 ** If a good path was found, then record it in the object's path 451 ** list. 452 */ 453 if (found1) { 454 Fixup_Path(&path1); 455 memcpy(&Path[0], &workpath1[0], min(path->Length, (int)sizeof(Path))); 456 } 457 458 Mark(MARK_DOWN); 459 } 460 461 PathDelay = Rule.PathDelay * TICKS_PER_MINUTE; 462 if (Path[0] != FACING_NONE) return(true); 463 464 /* 465 ** If a basic path couldn't be determined, then abort the navigation process. 466 */ 467 Stop_Driver(); 468 } 469 return(false); 470 } 471 472 473 /*********************************************************************************************** 474 * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * 475 * * 476 * This simple AI script handles moving the vehicle to its desired destination. Since * 477 * simple movement is handled directly by the engine, this routine merely waits until * 478 * the unit has reached its destination, and then causes the unit to enter idle mode. * 479 * * 480 * INPUT: none * 481 * * 482 * OUTPUT: Returns with the delay before calling this routine again. * 483 * * 484 * WARNINGS: none * 485 * * 486 * HISTORY: * 487 * 07/18/1994 JLB : Created. * 488 * 10/02/1996 JLB : Player controlled or human owned units don't scan for targets. * 489 *=============================================================================================*/ 490 int FootClass::Mission_Move(void) 491 { 492 assert(IsActive); 493 494 if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { 495 Enter_Idle_Mode(); 496 return(1); 497 } 498 // if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman) { 499 if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman && (!Team.Is_Valid() || !Team->Class->IsSuicide)) { 500 Target_Something_Nearby(THREAT_RANGE); 501 } 502 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 503 } 504 505 506 /*********************************************************************************************** 507 * FootClass::Mission_Capture -- Handles the capture mission. * 508 * * 509 * Capture missions are nearly the same as normal movement missions. The only difference * 510 * is that the final destination is handled in a special way so that it is not marked as * 511 * impassable. This allows the object (usually infantry) the ability to walk onto the * 512 * object and thus capture it. * 513 * * 514 * INPUT: none * 515 * * 516 * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * 517 * * 518 * WARNINGS: none * 519 * * 520 * HISTORY: * 521 * 03/19/1995 JLB : Created. * 522 *=============================================================================================*/ 523 int FootClass::Mission_Capture(void) 524 { 525 assert(IsActive); 526 527 /* 528 ** If there is a valid TarCom but the NavCom isn't set, then set the NavCom accordingly. 529 */ 530 if (Is_Target_Building(TarCom) && !Target_Legal(NavCom) && What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber) { 531 Assign_Destination(TarCom); 532 } 533 534 if (!Target_Legal(NavCom) /*&& !In_Radio_Contact()*/) { 535 Enter_Idle_Mode(); 536 if (Map[Center_Coord()].Cell_Building()) { 537 Scatter(0, true); 538 } 539 } 540 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 541 } 542 543 544 /*********************************************************************************************** 545 * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * 546 * * 547 * This AI routine handles heading to within range of the target and then firing upon * 548 * it until it is destroyed. If the target is destroyed, then the unit will change * 549 * missions to match its "idle mode" of operation (usually guarding). * 550 * * 551 * INPUT: none * 552 * * 553 * OUTPUT: Returns with the delay before calling this routine again. * 554 * * 555 * WARNINGS: none * 556 * * 557 * HISTORY: * 558 * 07/18/1994 JLB : Created. * 559 *=============================================================================================*/ 560 int FootClass::Mission_Attack(void) 561 { 562 assert(IsActive); 563 if (Target_Legal(TarCom)) { 564 Approach_Target(); 565 } else { 566 Enter_Idle_Mode(); 567 } 568 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 569 } 570 571 572 /*********************************************************************************************** 573 * FootClass::Mission_Guard -- Handles the AI for guarding in place. * 574 * * 575 * Units that are performing stationary guard duty use this AI process. They will sit * 576 * still and target any enemies that get within range. * 577 * * 578 * INPUT: none * 579 * * 580 * OUTPUT: Returns with the delay before calling this routine again. * 581 * * 582 * WARNINGS: none * 583 * * 584 * HISTORY: * 585 * 07/18/1994 JLB : Created. * 586 *=============================================================================================*/ 587 int FootClass::Mission_Guard(void) 588 { 589 assert(IsActive); 590 591 /* 592 ** If this unit is on an impassable cell for any reason, it needs to scatter immediately 593 */ 594 if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { 595 LandType land = Map[Coord].Land_Type(); 596 if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { 597 Scatter(0, true, true); 598 Shorten_Mission_Timer(); 599 } 600 } 601 602 if (!Target_Something_Nearby(THREAT_RANGE)) { 603 Random_Animate(); 604 } 605 606 int dtime = MissionControl[Mission].Normal_Delay(); 607 if (What_Am_I() == RTTI_VESSEL) { 608 switch (((VesselClass *)this)->Class->Type) { 609 case VESSEL_DD: 610 case VESSEL_PT: 611 dtime = MissionControl[Mission].AA_Delay(); 612 break; 613 614 case VESSEL_CA: 615 dtime *= 2; 616 break; 617 618 default: 619 break; 620 } 621 } 622 if (What_Am_I() == RTTI_INFANTRY) { 623 624 /* 625 ** If this is a bomber type infantry and the current target is a building, then go into 626 ** sabotage mode if not already. 627 */ 628 if (!House->IsHuman && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { 629 Assign_Mission(MISSION_SABOTAGE); 630 } 631 632 switch (((InfantryClass *)this)->Class->Type) { 633 case INFANTRY_E1: 634 case INFANTRY_E3: 635 dtime = MissionControl[Mission].AA_Delay(); 636 break; 637 638 default: 639 break; 640 } 641 } 642 643 return((Arm != 0) ? (int)Arm : (dtime+Random_Pick(0, 2))); 644 } 645 646 647 /*********************************************************************************************** 648 * FootClass::Mission_Hunt -- Handles the default hunt order. * 649 * * 650 * This routine is the default hunt order for game objects. It handles searching for a * 651 * nearby object and heading toward it. The act of targeting will cause it to attack * 652 * the target it selects. * 653 * * 654 * INPUT: none * 655 * * 656 * OUTPUT: Returns the game tick delay before calling this routine again. * 657 * * 658 * WARNINGS: none * 659 * * 660 * HISTORY: * 661 * 10/17/1994 JLB : Created. * 662 *=============================================================================================*/ 663 int FootClass::Mission_Hunt(void) 664 { 665 assert(IsActive); 666 if (!Target_Something_Nearby(THREAT_NORMAL)) { 667 #if(0) 668 #ifdef FIXIT_CSII // checked - ajw 9/28/98 669 if (What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this == INFANTRY_GENERAL && House->Class->House==HOUSE_UKRAINE && Scen.Scenario==47) { 670 for(int index=0; index < Buildings.Count(); index++) { 671 if(Buildings.Ptr(index)->IsOwnedByPlayer) { 672 Assign_Target(Buildings.Ptr(index)->As_Target()); 673 break; 674 } 675 } 676 for(index=0; index < Units.Count(); index++) { 677 if(Units.Ptr(index)->IsOwnedByPlayer) { 678 Assign_Target(Units.Ptr(index)->As_Target()); 679 break; 680 } 681 } 682 for(index=0; index < Infantry.Count(); index++) { 683 if(Infantry.Ptr(index)->IsOwnedByPlayer) { 684 Assign_Target(Infantry.Ptr(index)->As_Target()); 685 break; 686 } 687 } 688 for(index=0; index < Aircraft.Count(); index++) { 689 if(Aircraft.Ptr(index)->IsOwnedByPlayer) { 690 Assign_Target(Aircraft.Ptr(index)->As_Target()); 691 break; 692 } 693 } 694 } 695 #endif 696 #endif 697 Random_Animate(); 698 } else { 699 if (What_Am_I() == RTTI_INFANTRY && ( ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_RENOVATOR || ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_THIEF) ) { 700 Assign_Destination(TarCom); 701 Assign_Mission(MISSION_CAPTURE); 702 } else { 703 if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber && Is_Target_Building(TarCom)) { 704 Assign_Destination(TarCom); 705 Assign_Mission(MISSION_SABOTAGE); 706 } else { 707 Approach_Target(); 708 } 709 } 710 } 711 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 712 } 713 714 715 /*********************************************************************************************** 716 * FootClass::Stop_Driver -- This routine clears the driving state of the object. * 717 * * 718 * This is the counterpart routine to the Start_Driver function. It clears the driving * 719 * status flags and destination coordinate record. * 720 * * 721 * INPUT: none * 722 * * 723 * OUTPUT: bool; Was driving stopped? * 724 * * 725 * WARNINGS: none * 726 * * 727 * HISTORY: * 728 * 10/17/1994 JLB : Created. * 729 * 12/12/1994 JLB : Greatly simplified. * 730 *=============================================================================================*/ 731 bool FootClass::Stop_Driver(void) 732 { 733 assert(IsActive); 734 735 if (HeadToCoord) { 736 HeadToCoord = NULL; 737 Set_Speed(0); 738 IsDriving = false; 739 IsMovingOntoBridge = false; 740 return(true); 741 } 742 return(false); 743 } 744 745 746 /*********************************************************************************************** 747 * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * 748 * * 749 * Before a unit can move it must be started by this routine. This routine handles * 750 * reserving the cell and setting the driving flag. * 751 * * 752 * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * 753 * away from the unit's current location. * 754 * * 755 * OUTPUT: bool; Was driving initiated? * 756 * * 757 * WARNINGS: none * 758 * * 759 * HISTORY: * 760 * 10/17/1994 JLB : Created. * 761 * 12/12/1994 JLB : Uses simple spot index finder. * 762 *=============================================================================================*/ 763 bool FootClass::Start_Driver(COORDINATE &headto) 764 { 765 assert(IsActive); 766 767 Stop_Driver(); 768 if (headto) { 769 HeadToCoord = headto; 770 IsDriving = true; 771 772 CellClass * cellptr = &Map[headto]; 773 TemplateType ttype = cellptr->TType; 774 IsMovingOntoBridge = (ttype >= TEMPLATE_BRIDGE1 && ttype <= TEMPLATE_BRIDGE2D) || (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3F); 775 776 /* 777 ** Check for crate goodie finder here. 778 */ 779 if (Map[headto].Goodie_Check(this)) { 780 return(true); 781 } 782 if (!IsActive) return(false); 783 784 HeadToCoord = NULL; 785 IsDriving = false; 786 } 787 return(false); 788 } 789 790 791 /*********************************************************************************************** 792 * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * 793 * * 794 * This routine will determine the sort coordinate for foot class object. This coordinate * 795 * is usually the coordinate of the object. The exception is if the object is tethered. * 796 * In this case (presumes offloading to the north), the sorting coordinate is adjusted * 797 * so that the object will be drawn on top of the transport unit. * 798 * * 799 * INPUT: none * 800 * * 801 * OUTPUT: Returns with the coordinate to use for sorting. * 802 * * 803 * WARNINGS: none * 804 * * 805 * HISTORY: * 806 * 10/17/1994 JLB : Created. * 807 * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * 808 *=============================================================================================*/ 809 COORDINATE FootClass::Sort_Y(void) const 810 { 811 assert(IsActive); 812 813 if (IsUnloading) { 814 return(Coord_Add(Coord, 0x01000000L)); 815 } 816 if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { 817 return(Coord_Add(Coord, 0x01000000L)); 818 } 819 return(Coord_Add(Coord, 0x00300000L)); 820 } 821 822 823 /*********************************************************************************************** 824 * FootClass::Stun -- Prepares a ground travelling object for removal. * 825 * * 826 * This routine clears the units' navigation computer in preparation for removal from the * 827 * game. This is probably called as a result of unit destruction in combat. Clearing the * 828 * navigation computer ensures that the normal AI process won't start it moving again while * 829 * the object is undergoing any death animations. * 830 * * 831 * INPUT: none * 832 * * 833 * OUTPUT: none * 834 * * 835 * WARNINGS: none * 836 * * 837 * HISTORY: * 838 * 12/23/1994 JLB : Created. * 839 *=============================================================================================*/ 840 void FootClass::Stun(void) 841 { 842 assert(IsActive); 843 844 Assign_Destination(TARGET_NONE); 845 Path[0] = FACING_NONE; 846 Stop_Driver(); 847 TechnoClass::Stun(); 848 } 849 850 851 /*********************************************************************************************** 852 * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * 853 * * 854 * This routine will set the navigation computer to approach the target indicated by the * 855 * targeting computer. It is through this function that the unit nears the target so * 856 * that weapon firing may occur. * 857 * * 858 * INPUT: none * 859 * * 860 * OUTPUT: none * 861 * * 862 * WARNINGS: none * 863 * * 864 * HISTORY: * 865 * 05/31/1994 JLB : Created. * 866 * 12/13/1994 JLB : Made part of TechnoClass. * 867 * 12/22/1994 JLB : Enhanced search algorithm. * 868 * 05/20/1995 JLB : Always approaches if the object is off the map. * 869 *=============================================================================================*/ 870 void FootClass::Approach_Target(void) 871 { 872 assert(IsActive); 873 874 /* 875 ** Determine that if there is an existing target it is still legal 876 ** and within range. 877 */ 878 if (Target_Legal(TarCom)) { 879 int primary = What_Weapon_Should_I_Use(TarCom); 880 881 /* 882 ** If the target is too far away then head toward it. 883 */ 884 int maxrange = Weapon_Range(primary); 885 // int maxrange = max(Weapon_Range(0), Weapon_Range(1)); 886 887 if (!Target_Legal(NavCom) && (!In_Range(TarCom, primary) || !IsLocked)) { 888 // if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { 889 890 /* 891 ** If the object that we are attacking is a building adjust the unit's 892 ** max range so that people can stand far away from the buildings and 893 ** hit them. 894 */ 895 BuildingClass * obj = As_Building(TarCom); 896 if (obj) { 897 maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); 898 } 899 900 /* 901 ** Adjust the max range of an infantry unit for where he is standing 902 ** in the room. 903 */ 904 maxrange -= 0x00B7; 905 #ifdef OBSOLETE 906 if (What_Am_I() == RTTI_INFANTRY) { 907 maxrange -= 0x0111; 908 } else { 909 maxrange -= 0x00B7; 910 } 911 #endif 912 maxrange = max(maxrange, 0); 913 914 COORDINATE tcoord = ::As_Coord(TarCom); 915 COORDINATE trycoord = 0; 916 CELL tcell = Coord_Cell(tcoord); 917 CELL trycell = tcell; 918 DirType dir = Direction256(tcoord, Center_Coord()); 919 bool found = false; 920 921 /* 922 ** Sweep through the cells between the target and the unit, looking for 923 ** a cell that the unit can enter but which is also within weapon range 924 ** of the target. If after a reasonable search, no appropriate cell could 925 ** be found, then the target will be assigned as the movement destination 926 ** and let "the chips fall where they may." 927 */ 928 for (int range = maxrange; range > 0x0080; range -= 0x0100) { 929 static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; 930 931 for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { 932 trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); 933 934 if (::Distance(trycoord, tcoord) < range) { 935 trycell = Coord_Cell(trycoord); 936 if (Map.In_Radar(trycell) && Map[trycell].Is_Clear_To_Move(Techno_Type_Class()->Speed, false, false, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)) { 937 // if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { 938 found = true; 939 break; 940 } 941 } 942 } 943 if (found) break; 944 } 945 946 /* 947 ** If a suitable intermediate location was found, then head toward it. 948 ** Otherwise, head toward the enemy unit directly. 949 ** Infantry always head towards the target since they can enter a cell 950 ** in range, but still not be able to hit the target if the spot is out of range. 951 */ 952 if (What_Am_I() == RTTI_INFANTRY) { 953 Assign_Destination(TarCom); 954 } else if (found) { 955 Assign_Destination(::As_Target(trycell)); 956 } else { 957 958 trycell = Map.Nearby_Location(trycell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); 959 Assign_Destination(::As_Target(trycell)); 960 // Assign_Destination(TarCom); 961 } 962 } 963 } 964 } 965 966 967 /*********************************************************************************************** 968 * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * 969 * * 970 * This mission routine causes the unit to scan for targets out to twice its weapon range * 971 * from the home point. If a target was found, then it will be attacked. The unit will * 972 * chase the target until it gets up to to its weapon range from the home position. * 973 * In that case, it will return to home position and start scanning for another target. * 974 * * 975 * INPUT: none * 976 * * 977 * OUTPUT: Returns with time delay before calling this routine again. * 978 * * 979 * WARNINGS: none * 980 * * 981 * HISTORY: * 982 * 12/23/1994 JLB : Created. * 983 * 07/27/1995 JLB : Greatly simplified. * 984 *=============================================================================================*/ 985 int FootClass::Mission_Guard_Area(void) 986 { 987 assert(IsActive); 988 989 /* 990 ** If this unit is on an impassable cell for any reason, it needs to scatter immediately 991 */ 992 if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { 993 LandType land = Map[Coord].Land_Type(); 994 if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { 995 Scatter(0, true, true); 996 Shorten_Mission_Timer(); 997 } 998 } 999 1000 if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { 1001 Assign_Mission(MISSION_HARVEST); 1002 return(1+Random_Pick(1, 10)); 1003 } 1004 1005 /* 1006 ** Ensure that the archive target is valid. 1007 */ 1008 if (!Target_Legal(ArchiveTarget)) { 1009 ArchiveTarget = ::As_Target(Coord); 1010 } 1011 1012 /* 1013 ** Ensure units aren't trying to guard cells off the map. 1014 */ 1015 if (Target_Legal(NavCom) && Is_Target_Cell(NavCom)) { 1016 CELL cell = As_Cell(NavCom); 1017 int x = Cell_X(cell); 1018 int y = Cell_Y(cell); 1019 if (x < Map.MapCellX || y < Map.MapCellY || x >= (Map.MapCellX + Map.MapCellWidth) || y >= (Map.MapCellY + Map.MapCellHeight)) { 1020 Assign_Target(TARGET_NONE); 1021 Assign_Destination(TARGET_NONE); 1022 ArchiveTarget = ::As_Target(Coord); 1023 } 1024 } 1025 1026 /* 1027 ** If this is a bomber type infantry and the current target is a building, then go into 1028 ** sabotage mode if not already. 1029 */ 1030 if (!House->IsHuman && What_Am_I() == RTTI_INFANTRY && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { 1031 Assign_Mission(MISSION_SABOTAGE); 1032 return(1); 1033 } 1034 1035 /* 1036 ** Make sure that the unit has not strayed too far from the home position. 1037 ** If it has, then race back to it. 1038 */ 1039 int maxrange = Threat_Range(1)/2; 1040 1041 if (!IsFiring && !Target_Legal(NavCom) && Distance(ArchiveTarget) > maxrange) { 1042 Assign_Target(TARGET_NONE); 1043 Assign_Destination(ArchiveTarget); 1044 } 1045 1046 if (!Target_Legal(TarCom)) { 1047 COORDINATE old = Coord; 1048 Coord = As_Coord(ArchiveTarget); 1049 Target_Something_Nearby(THREAT_AREA); 1050 Coord = old; 1051 if (Target_Legal(TarCom)) { 1052 return(1); 1053 } 1054 Random_Animate(); 1055 } else { 1056 Approach_Target(); 1057 } 1058 1059 int dtime = MissionControl[Mission].Normal_Delay(); 1060 if (What_Am_I() == RTTI_AIRCRAFT) { 1061 dtime *= 2; 1062 } 1063 return(dtime + Random_Pick(1, 5)); 1064 } 1065 1066 1067 /*********************************************************************************************** 1068 * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * 1069 * * 1070 * This routine will make sure that the home position for the foot class object gets * 1071 * reset. This is necessary since the home position may change depending on the unit's * 1072 * transition between limbo and non-limbo condition. * 1073 * * 1074 * INPUT: coord -- The coordinate to unlimbo the unit at. * 1075 * * 1076 * dir -- The initial direction to give the unit. * 1077 * * 1078 * OUTPUT: bool; Was the unit unlimboed successfully? * 1079 * * 1080 * WARNINGS: none * 1081 * * 1082 * HISTORY: * 1083 * 12/23/1994 JLB : Created. * 1084 *=============================================================================================*/ 1085 bool FootClass::Unlimbo(COORDINATE coord, DirType dir) 1086 { 1087 assert(IsActive); 1088 1089 /* 1090 ** Try to unlimbo the unit. 1091 */ 1092 if (TechnoClass::Unlimbo(coord, dir)) { 1093 1094 /* 1095 ** Mobile units are always revealed to the house that owns them. 1096 */ 1097 Revealed(House); 1098 1099 /* 1100 ** Start in a still (non-moving) state. 1101 */ 1102 Path[0] = FACING_NONE; 1103 return(true); 1104 } 1105 return(false); 1106 } 1107 1108 1109 /*********************************************************************************************** 1110 * FootClass::Take_Damage -- Handles taking damage to this object. * 1111 * * 1112 * This routine intercepts the damage assigned to this object and if this object is * 1113 * a member of a team, it informs the team that the damage has occurred. The team may * 1114 * change it's priority or action based on this event. * 1115 * * 1116 * INPUT: damage -- The damage points inflicted on the unit. * 1117 * * 1118 * distance -- The distance from the point of damage to the unit itself. * 1119 * * 1120 * warhead -- The type of damage that is inflicted. * 1121 * * 1122 * source -- The perpetrator of the damage. By knowing who caused the damage, * 1123 * the team know's who to "get even with". * 1124 * * 1125 * OUTPUT: Returns with the result type of the damage. * 1126 * * 1127 * WARNINGS: none * 1128 * * 1129 * HISTORY: * 1130 * 12/30/1994 JLB : Created. * 1131 *=============================================================================================*/ 1132 ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) 1133 { 1134 assert(IsActive); 1135 1136 ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); 1137 1138 if (result != RESULT_NONE && Team) { 1139 1140 Team->Took_Damage(this, result, source); 1141 1142 } else { 1143 1144 if (result != RESULT_DESTROYED && result != RESULT_NONE) { 1145 1146 /* 1147 ** Determine if the target that is currently being attacked has a weapon that can 1148 ** do harm to a ground based unit. This information is needed so that an appropriate 1149 ** response will occur when damage is taken. 1150 */ 1151 // bool tweap = false; 1152 // if (As_Techno(TarCom)) { 1153 // tweap = (As_Techno(TarCom)->Techno_Type_Class()->PrimaryWeapon != NULL); 1154 // } 1155 1156 /* 1157 ** This ensures that if a unit is in sticky mode, then it will snap out of 1158 ** it when it takes damage. 1159 */ 1160 if (source != NULL && MissionControl[Mission].IsNoThreat && !MissionControl[Mission].IsZombie) { 1161 Enter_Idle_Mode(); 1162 } 1163 1164 /* 1165 ** If this object is not part of a team and it can retaliate for the damage, then have 1166 ** it try to do so. This prevents it from just sitting there and taking damage. 1167 */ 1168 if (Is_Allowed_To_Retaliate(source)) { 1169 1170 int primary = What_Weapon_Should_I_Use(source->As_Target()); 1171 if (In_Range(source, primary) || !House->IsHuman) { 1172 Assign_Target(source->As_Target()); 1173 } 1174 1175 if (Mission == MISSION_AMBUSH) { 1176 Assign_Mission(MISSION_HUNT); 1177 } 1178 1179 /* 1180 ** Simple retaliation cannot occur because the source of the damage 1181 ** is too far away. If scatter logic is enabled, then scatter now. 1182 */ 1183 if (!Target_Legal(TarCom) && !Target_Legal(NavCom) && Rule.IsScatter) { 1184 Scatter(0, true); 1185 } 1186 1187 } else { 1188 1189 /* 1190 ** If this object isn't doing anything important, then scatter. 1191 */ 1192 if (MissionControl[Mission].IsScatter && !IsTethered && !IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && What_Am_I() != RTTI_AIRCRAFT && What_Am_I() != RTTI_VESSEL) { 1193 if (!House->IsHuman || Rule.IsScatter) { 1194 Scatter(0, true); 1195 } 1196 } 1197 } 1198 } 1199 } 1200 return(result); 1201 } 1202 1203 1204 /*********************************************************************************************** 1205 * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * 1206 * * 1207 * At this level, the object is known to have the ability to attack or move to the * 1208 * target specified (in theory). Perform the attack or move as indicated. * 1209 * * 1210 * INPUT: target -- The target clicked upon that will precipitate action. * 1211 * * 1212 * OUTPUT: Returns with the type of action performed. * 1213 * * 1214 * WARNINGS: none * 1215 * * 1216 * HISTORY: * 1217 * 01/06/1995 JLB : Created. * 1218 *=============================================================================================*/ 1219 void FootClass::Active_Click_With(ActionType action, ObjectClass * object) 1220 { 1221 assert(IsActive); 1222 assert(object != NULL); 1223 1224 switch (action) { 1225 case ACTION_GUARD_AREA: 1226 if (Can_Player_Fire() && Can_Player_Move()) { 1227 if (What_Am_I() == RTTI_INFANTRY && 1228 ((InfantryClass *)this)->Class->IsBomber && 1229 object->What_Am_I() == RTTI_BUILDING && 1230 !House->Is_Ally(object)) { 1231 1232 Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); 1233 } else { 1234 Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); 1235 } 1236 } 1237 break; 1238 1239 case ACTION_SELF: 1240 Player_Assign_Mission(MISSION_UNLOAD); 1241 break; 1242 1243 case ACTION_ATTACK: 1244 if (Can_Player_Fire()) { 1245 Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); 1246 } 1247 break; 1248 1249 case ACTION_ENTER: 1250 if (Can_Player_Move() && object && object->Is_Techno() /*&& !((RadioClass *)object)->In_Radio_Contact()*/) { 1251 Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); 1252 } 1253 break; 1254 1255 case ACTION_CAPTURE: 1256 if (Can_Player_Move()) { 1257 Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); 1258 } 1259 break; 1260 1261 case ACTION_SABOTAGE: 1262 if (Can_Player_Move()) { 1263 Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); 1264 } 1265 break; 1266 1267 case ACTION_NOMOVE: 1268 case ACTION_MOVE: 1269 if (Can_Player_Move()) { 1270 1271 TARGET targ = object->As_Target(); 1272 1273 /* 1274 ** If the destination object is not the same zone, then pick a nearby location. 1275 */ 1276 if (object->What_Am_I() != RTTI_AIRCRAFT && Techno_Type_Class()->Speed != SPEED_WINGED && Map[Coord].Zones[Techno_Type_Class()->MZone] != Map[object->Center_Coord()].Zones[Techno_Type_Class()->MZone]) { 1277 1278 #ifdef FIXIT_MINE_PASSABLE 1279 // Fixes units not driving onto mines. 1280 if (Can_Enter_Cell(Coord_Cell(object->Center_Coord())) > MOVE_OK) { 1281 targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); 1282 } 1283 #else 1284 targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); 1285 #endif 1286 } 1287 1288 Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, targ); 1289 } 1290 break; 1291 1292 case ACTION_NO_DEPLOY: 1293 Speak(VOX_DEPLOY); 1294 break; 1295 1296 default: 1297 break; 1298 } 1299 } 1300 1301 1302 /*********************************************************************************************** 1303 * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * 1304 * * 1305 * This routine performs the action requested when the left mouse button was clicked over * 1306 * a cell. Typically, this is just a move command. * 1307 * * 1308 * INPUT: action -- The predetermined action that should occur. * 1309 * * 1310 * cell -- The cell number that the action should occur at. * 1311 * * 1312 * OUTPUT: none * 1313 * * 1314 * WARNINGS: none * 1315 * * 1316 * HISTORY: * 1317 * 01/19/1995 JLB : Created. * 1318 *=============================================================================================*/ 1319 void FootClass::Active_Click_With(ActionType action, CELL cell) 1320 { 1321 assert(IsActive); 1322 1323 action = What_Action(cell); 1324 switch (action) { 1325 case ACTION_HARVEST: 1326 Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); 1327 break; 1328 1329 case ACTION_MOVE: 1330 if (AllowVoice) { 1331 COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); 1332 OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord, 1 << PlayerPtr->Class->House)); 1333 } 1334 // Fall into next case. 1335 1336 case ACTION_NOMOVE: 1337 //using function for IsVisible so we have different results for different players - JAS 2019/09/30 1338 if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].Is_Visible(PlayerPtr)) { 1339 1340 /* 1341 ** Find the closest same-zoned cell to where the unit currently is. 1342 ** This will allow the unit to come as close to the destination cell 1343 ** as is reasonably possible, when clicking on an impassable cell 1344 ** (as is likely when clicking in the shroud.) It looks for the 1345 ** nearest cell using an expanding-radius box, and ignores cells 1346 ** off the edge of the map. 1347 */ 1348 CellClass const * cellptr = &Map[::As_Cell(::As_Target(Center_Coord()))]; 1349 if (What_Am_I() != RTTI_AIRCRAFT) { 1350 1351 if (Can_Enter_Cell(Coord_Cell(Center_Coord())) == MOVE_OK) { 1352 cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); 1353 } else { 1354 cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); 1355 } 1356 #ifdef OBSOLETE 1357 cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); 1358 #endif 1359 } 1360 1361 Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); 1362 } 1363 break; 1364 1365 case ACTION_ATTACK: 1366 Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); 1367 break; 1368 1369 /* 1370 ** Engineer attempting to capture bridge to repair it 1371 */ 1372 case ACTION_CAPTURE: 1373 if (Can_Player_Move()) { 1374 Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, ::As_Target(cell)); 1375 } 1376 break; 1377 1378 case ACTION_SABOTAGE: 1379 Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, ::As_Target(cell) ); 1380 break; 1381 1382 // MBL 05.15.2020 - Adding support for CTRL+ALT clicking the ground to have units move to an area and guard it 1383 case ACTION_GUARD_AREA: 1384 if (Can_Player_Fire() && Can_Player_Move()) { 1385 Player_Assign_Mission(MISSION_GUARD_AREA, ::As_Target(cell)); 1386 } 1387 break; 1388 // END MBL 05.15.2020 1389 } 1390 } 1391 1392 1393 /*********************************************************************************************** 1394 * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * 1395 * * 1396 * This routine is called as this object moves from cell to cell. When the center of the * 1397 * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * 1398 * the path to the distance to the target. This forces path recalculation in an effort to * 1399 * avoid units passing each other. * 1400 * * 1401 * INPUT: why -- Specifies the circumstances under which this routine was called. * 1402 * * 1403 * OUTPUT: none * 1404 * * 1405 * WARNINGS: none * 1406 * * 1407 * HISTORY: * 1408 * 05/08/1995 JLB : Created. * 1409 * 07/08/1995 JLB : Handles generic enter trigger event. * 1410 * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * 1411 *=============================================================================================*/ 1412 void FootClass::Per_Cell_Process(PCPType why) 1413 { 1414 assert(IsActive); 1415 1416 if (why == PCP_END) { 1417 1418 IsScattering = false; 1419 1420 /* 1421 ** Clear any unloading flag if necessary. 1422 */ 1423 IsUnloading = false; 1424 1425 /* 1426 ** If adjacent to an enemy techno that has the ability to reveal a sub, 1427 ** then shimmer the cloaked object. 1428 */ 1429 if (Cloak == CLOAKED) { 1430 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 1431 CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); 1432 1433 if (Map.In_Radar(cell)) { 1434 TechnoClass const * techno = Map[cell].Cell_Techno(); 1435 1436 if (techno && !techno->House->Is_Ally(this) && techno->Techno_Type_Class()->IsScanner) { 1437 Do_Shimmer(); 1438 break; 1439 } 1440 } 1441 } 1442 } 1443 1444 /* 1445 ** Shorten the path if the target is now within weapon range of this 1446 ** unit and this unit is on an attack type mission. But only if the target 1447 ** is slow enough for leading to make sense. 1448 */ 1449 if (Target_Legal(TarCom) && (What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)this)->Class->IsDog)) { 1450 int primary = What_Weapon_Should_I_Use(TarCom); 1451 bool inrange = In_Range(TarCom, primary); 1452 TechnoClass const * techno = As_Techno(TarCom); 1453 if (techno != NULL && techno->Is_Foot()) { 1454 FootClass const * foot = (FootClass const *)techno; 1455 MPHType speed = ((TechnoTypeClass const &)techno->Class_Of()).MaxSpeed; 1456 COORDINATE rangecoord = (speed > MPH_SLOW) ? foot->Likely_Coord() : foot->Target_Coord(); 1457 inrange = In_Range(rangecoord, primary); 1458 } 1459 1460 if ((Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { 1461 Assign_Destination(TARGET_NONE); 1462 Path[0] = FACING_NONE; 1463 } 1464 } 1465 1466 /* 1467 ** Trigger event associated with the player entering the cell. 1468 */ 1469 if (Cloak != CLOAKED) { 1470 TriggerClass * trigger = Map[Coord].Trigger; 1471 if (trigger != NULL) { 1472 trigger->Spring(TEVENT_PLAYER_ENTERED, this, Coord_Cell(Coord)); 1473 if (!IsActive) return; 1474 } 1475 1476 /* 1477 ** Check for horizontal trigger crossing. 1478 */ 1479 int x = Cell_X(Coord_Cell(Coord)); 1480 int y = Cell_Y(Coord_Cell(Coord)); 1481 int index; 1482 for (index = 0; index < Map.MapCellWidth; index++) { 1483 trigger = Map[XY_Cell(index+Map.MapCellX, y)].Trigger; 1484 if (trigger != NULL) { 1485 if (trigger->Class->Event1.Event == TEVENT_CROSS_HORIZONTAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_HORIZONTAL)) { 1486 trigger->Spring(TEVENT_CROSS_HORIZONTAL, this, Coord_Cell(Coord)); 1487 if (!IsActive) return; 1488 } 1489 } 1490 } 1491 1492 /* 1493 ** Check for vertical trigger crossing. 1494 */ 1495 for (index = 0; index < Map.MapCellHeight; index++) { 1496 trigger = Map[XY_Cell(x, index+Map.MapCellY)].Trigger; 1497 if (trigger != NULL) { 1498 if (trigger->Class->Event1.Event == TEVENT_CROSS_VERTICAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_VERTICAL)) { 1499 trigger->Spring(TEVENT_CROSS_VERTICAL, this, Coord_Cell(Coord)); 1500 if (!IsActive) return; 1501 } 1502 } 1503 } 1504 1505 /* 1506 ** Check for zone entry trigger events. 1507 */ 1508 for (MapTriggerID = 0; MapTriggerID < MapTriggers.Count(); MapTriggerID++) { 1509 trigger = MapTriggers[MapTriggerID]; 1510 if (trigger->Class->Event1.Event == TEVENT_ENTERS_ZONE || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_ENTERS_ZONE)) { 1511 if (Map[trigger->Cell].Zones[Techno_Type_Class()->MZone] == Map[Coord].Zones[Techno_Type_Class()->MZone]) { 1512 trigger->Spring(TEVENT_ENTERS_ZONE, this, Coord_Cell(Coord)); 1513 if (!IsActive) return; 1514 } 1515 } 1516 } 1517 1518 /* 1519 ** If any of these triggers cause this unit to be destroyed, then 1520 ** stop all further processing for this unit. 1521 */ 1522 if (!IsActive) return; 1523 } 1524 1525 #ifdef OBSOLETE 1526 /* 1527 ** Flag any gap generators to re-draw 1528 */ 1529 for (int index = 0; index <Buildings.Count(); index++) { 1530 BuildingClass * obj = Buildings.Ptr(index); 1531 if (obj && *obj == STRUCT_GAP && !obj->IsInLimbo && (HouseClass *)obj->House != PlayerPtr) { 1532 int dist = Distance(obj) / CELL_LEPTON_W; 1533 if (dist < (6 + Rule.GapShroudRadius) ) { 1534 // if (dist < (6 + obj->Class->SightRange) ) { 1535 obj->IsJamming = false; // lie so it'll re-jam now 1536 } 1537 } 1538 } 1539 #endif 1540 1541 } 1542 1543 TechnoClass::Per_Cell_Process(why); 1544 } 1545 1546 1547 /*************************************************************************** 1548 * FootClass::Override_Mission -- temporarily overrides a units mission * 1549 * * 1550 * * 1551 * * 1552 * INPUT: MissionType mission - the mission we want to override * 1553 * TARGET tarcom - the new target we want to override * 1554 * TARGET navcom - the new navigation point to override * 1555 * * 1556 * OUTPUT: none * 1557 * * 1558 * WARNINGS: If a mission is already overridden, the current mission is * 1559 * just re-assigned. * 1560 * * 1561 * HISTORY: * 1562 * 04/28/1995 PWG : Created. * 1563 *=========================================================================*/ 1564 void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) 1565 { 1566 assert(IsActive); 1567 1568 SuspendedNavCom = NavCom; 1569 TechnoClass::Override_Mission(mission, tarcom, navcom); 1570 1571 Assign_Destination(navcom); 1572 } 1573 1574 1575 /*************************************************************************** 1576 * FootClass::Restore_Mission -- Restores an overridden mission * 1577 * * 1578 * INPUT: none * 1579 * * 1580 * OUTPUT: none * 1581 * * 1582 * WARNINGS: none * 1583 * * 1584 * HISTORY: * 1585 * 04/28/1995 PWG : Created. * 1586 *=========================================================================*/ 1587 bool FootClass::Restore_Mission(void) 1588 { 1589 assert(IsActive); 1590 1591 if (TechnoClass::Restore_Mission()) { 1592 Assign_Destination(SuspendedNavCom); 1593 return(true); 1594 } 1595 return(false); 1596 } 1597 1598 1599 /*********************************************************************************************** 1600 * FootClass::Receive_Message -- Movement related radio messages are handled here. * 1601 * * 1602 * This routine handles radio message that are related to movement. These are used for * 1603 * complex coordinated maneuvers. * 1604 * * 1605 * INPUT: from -- Pointer to the originator of this radio message. * 1606 * * 1607 * message -- The radio message that is being received. * 1608 * * 1609 * param -- The optional parameter (could be a movement destination). * 1610 * * 1611 * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * 1612 * response is RADIO_ROGER. * 1613 * * 1614 * WARNINGS: none * 1615 * * 1616 * HISTORY: * 1617 * 05/14/1995 JLB : Created. * 1618 *=============================================================================================*/ 1619 RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) 1620 { 1621 assert(IsActive); 1622 1623 switch (message) { 1624 1625 /* 1626 ** Answers if this object is located on top of a service depot. 1627 */ 1628 case RADIO_ON_DEPOT: 1629 if (Map[Center_Coord()].Cell_Building() != NULL) { 1630 BuildingClass const * building = Map[Center_Coord()].Cell_Building(); 1631 if (*building == STRUCT_REPAIR) { 1632 return(RADIO_ROGER); 1633 } 1634 } 1635 return(RADIO_NEGATIVE); 1636 1637 /* 1638 ** Intercept the repair request and if this object is moving, then no repair 1639 ** is possible. 1640 */ 1641 case RADIO_REPAIR: 1642 if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); 1643 break; 1644 1645 /* 1646 ** Something bad has happened to the object in contact with. Abort any coordinated 1647 ** activity with this object. Basically, ... run away! Run away! 1648 */ 1649 case RADIO_RUN_AWAY: 1650 if (In_Radio_Contact()) { 1651 if (NavCom == Contact_With_Whom()->As_Target()) { 1652 Assign_Destination(TARGET_NONE); 1653 } 1654 } 1655 if (Mission == MISSION_SLEEP) { 1656 Assign_Mission(MISSION_GUARD); 1657 Commence(); 1658 } 1659 if (Mission == MISSION_ENTER) { 1660 Assign_Mission(MISSION_GUARD); 1661 } 1662 if (!IsRotating && !Target_Legal(NavCom)) { 1663 Scatter(0, true, true); 1664 } 1665 break; 1666 1667 /* 1668 ** Checks to see if this unit needs to move somewhere. If it is already in motion, 1669 ** then it doesn't need further movement instructions. 1670 */ 1671 case RADIO_NEED_TO_MOVE: 1672 param = (long)NavCom; 1673 if (!Target_Legal(NavCom)) { 1674 return(RADIO_ROGER); 1675 } 1676 return(RADIO_NEGATIVE); 1677 1678 /* 1679 ** Radio request to move to location specified. Typically this is used 1680 ** for complex loading and unloading missions. 1681 */ 1682 case RADIO_MOVE_HERE: 1683 if (NavCom != (TARGET)param) { 1684 if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { 1685 return(RADIO_YEA_NOW_WHAT); 1686 } else { 1687 if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { 1688 Assign_Mission(MISSION_MOVE); 1689 } 1690 Assign_Destination((TARGET)param); 1691 Shorten_Mission_Timer(); 1692 } 1693 } 1694 return(RADIO_ROGER); 1695 1696 /* 1697 ** Requests if this unit is trying to cooperatively load up. Typically, this occurs 1698 ** for passengers and when vehicles need to be repaired. 1699 */ 1700 case RADIO_TRYING_TO_LOAD: 1701 if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { 1702 TechnoClass::Receive_Message(from, message, param); 1703 return(RADIO_ROGER); 1704 } 1705 break; 1706 } 1707 return(TechnoClass::Receive_Message(from, message, param)); 1708 } 1709 1710 1711 /*********************************************************************************************** 1712 * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * 1713 * * 1714 * This mission handler will cooperatively coordinate the object to maneuver into the * 1715 * object it is in radio contact with. This is used by infantry when they wish to load * 1716 * into an APC as well as by vehicles when they wish to enter a repair facility. * 1717 * * 1718 * INPUT: none * 1719 * * 1720 * OUTPUT: Returns the number of game ticks before this routine should be called again. * 1721 * * 1722 * WARNINGS: none * 1723 * * 1724 * HISTORY: * 1725 * 05/15/1995 JLB : Created. * 1726 * 09/22/1995 JLB : Modified to handle the "on hold" condition. * 1727 *=============================================================================================*/ 1728 int FootClass::Mission_Enter(void) 1729 { 1730 assert(IsActive); 1731 1732 /* 1733 ** Find out who to coordinate with. If in radio contact, then this the transporter is 1734 ** defined. If not in radio contact, then try the archive target value to see if that 1735 ** is suitable. 1736 */ 1737 TechnoClass * contact = Contact_With_Whom(); 1738 if (contact == NULL) { 1739 contact = As_Techno(ArchiveTarget); 1740 } 1741 1742 /* 1743 ** If in contact, then let the transporter handle the movement coordination. 1744 */ 1745 if (contact != NULL) { 1746 1747 /* 1748 ** If the transport says to "bug off", then abort the enter mission. The transport may 1749 ** likely say all is 'ok' with the "RADIO ROGER", then try again later. 1750 */ 1751 if (Transmit_Message(RADIO_DOCKING, contact) != RADIO_ROGER && !IsTethered) { 1752 Transmit_Message(RADIO_OVER_OUT); 1753 Enter_Idle_Mode(); 1754 } 1755 1756 } else { 1757 1758 /* 1759 ** Since there is no potential object to enter, then abort this 1760 ** mission with some default standby mission. 1761 */ 1762 if (MissionQueue == MISSION_NONE) { 1763 /* 1764 ** If this is a harvester, then return to harvesting. 1765 ** Set a hacky target so we know to skip to the proper state. 1766 */ 1767 if (What_Am_I() == RTTI_UNIT && ((UnitClass*)this)->Class->IsToHarvest) { 1768 Assign_Mission(MISSION_HARVEST); 1769 Assign_Target(As_Target()); 1770 Assign_Destination(TARGET_NONE); 1771 } else { 1772 Enter_Idle_Mode(); 1773 } 1774 } 1775 } 1776 1777 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 1778 } 1779 1780 1781 /*********************************************************************************************** 1782 * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * 1783 * * 1784 * This routine will assign the specified target to the navigation computer. No legality * 1785 * checks are performed. * 1786 * * 1787 * INPUT: target -- The target value to assign to the navigation computer. * 1788 * * 1789 * OUTPUT: none * 1790 * * 1791 * WARNINGS: none * 1792 * * 1793 * HISTORY: * 1794 * 07/08/1995 JLB : Created. * 1795 *=============================================================================================*/ 1796 void FootClass::Assign_Destination(TARGET target) 1797 { 1798 assert(IsActive); 1799 1800 NavCom = target; 1801 1802 /* 1803 ** Presume that the easiest path is tried first. As the findpath proceeds, when 1804 ** a failure occurs, this threshhold will be increased until path failure 1805 ** cannot be prevent. At this point, all movement should cease. 1806 */ 1807 PathThreshhold = MOVE_CLOAK; 1808 } 1809 1810 1811 /*********************************************************************************************** 1812 * FootClass::Detach_All -- Removes this object from the game system. * 1813 * * 1814 * This routine will remove this object from the game system. This routine is called when * 1815 * this object is about to be deleted. All other objects should no longer reference this * 1816 * object in that case. * 1817 * * 1818 * INPUT: none * 1819 * * 1820 * OUTPUT: none * 1821 * * 1822 * WARNINGS: none * 1823 * * 1824 * HISTORY: * 1825 * 07/08/1995 JLB : Created. * 1826 *=============================================================================================*/ 1827 void FootClass::Detach_All(bool all) 1828 { 1829 assert(IsActive); 1830 1831 if (Team && !ScenarioInit) { 1832 Team->Remove(this); 1833 Team = NULL; 1834 } 1835 1836 TechnoClass::Detach_All(all); 1837 } 1838 1839 1840 /*********************************************************************************************** 1841 * FootClass::Rescue_Mission -- Calls this unit to the rescue. * 1842 * * 1843 * This routine is called when the house determines that it should attack the specified * 1844 * target. This routine will determine if it can attack the target specified and if so, * 1845 * the amount of power it can throw at it. This returned power value is used to allow * 1846 * intelligent distribution of retaliation. * 1847 * * 1848 * INPUT: target -- The target that this object just might be assigned to attack and thus * 1849 * how much power it can bring to bear should be returned. * 1850 * * 1851 * OUTPUT: Returns with the amount of power that this object can bring to bear against the * 1852 * potential target specified. * 1853 * * 1854 * WARNINGS: none * 1855 * * 1856 * HISTORY: * 1857 * 07/08/1995 JLB : Created. * 1858 *=============================================================================================*/ 1859 int FootClass::Rescue_Mission(TARGET tarcom) 1860 { 1861 assert(IsActive); 1862 1863 /* 1864 ** If the target specified is not legal, then it cannot be attacked. Always return 1865 ** zero in this case. 1866 */ 1867 if (!Target_Legal(tarcom)) return(0); 1868 1869 /* 1870 ** If the unit is already assigned to destroy the tarcom then we need 1871 ** to return a negative value which tells the computer to lower the 1872 ** desired threat rating. 1873 */ 1874 if (TarCom == tarcom) { 1875 return(-Risk()); 1876 } 1877 1878 /* 1879 ** If the unit is currently attacking a target that has a weapon then we 1880 ** cannot abandon it as it will destroy us if we return to base. 1881 */ 1882 if (Target_Legal(TarCom)) { 1883 TechnoClass * techno = As_Techno(TarCom); 1884 if (techno != NULL && techno->Is_Weapon_Equipped()) { 1885 return(0); 1886 } 1887 } 1888 1889 /* 1890 ** If the unit is in a harvest mission or is currently attacking 1891 ** something, or is not very effective, then it will be of no help 1892 ** at all. 1893 */ 1894 if (Team.Is_Valid() || Mission == MISSION_HARVEST || !Risk()) { 1895 return(0); 1896 } 1897 1898 /* 1899 ** Find the distance to the target modified by the range. If the 1900 ** the distance is 0, then things are ok. 1901 */ 1902 int dist = Distance(tarcom) - Weapon_Range(0); 1903 int threat = Risk() * 1024; 1904 int speed = -1; 1905 if (dist > 0) { 1906 1907 /* 1908 ** Next we need to figure out how fast the unit moves because this 1909 ** decreases the distance penalty. 1910 */ 1911 speed = max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); 1912 1913 int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; 1914 1915 /* 1916 ** Finally modify the threat by the distance the unit is away. 1917 */ 1918 threat = max(threat/ratio, 1); 1919 } 1920 return(threat); 1921 } 1922 1923 1924 /*********************************************************************************************** 1925 * FootClass::Death_Announcement -- Announces the death of a unit. * 1926 * * 1927 * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * 1928 * * 1929 * INPUT: source -- The perpetrator of this death. * 1930 * * 1931 * OUTPUT: none * 1932 * * 1933 * WARNINGS: none * 1934 * * 1935 * HISTORY: * 1936 * 07/01/1995 JLB : Created. * 1937 *=============================================================================================*/ 1938 void FootClass::Death_Announcement(TechnoClass const * ) const 1939 { 1940 assert(IsActive); 1941 1942 //if (IsOwnedByPlayer) { 1943 if ((Session.Type == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) || (Session.Type != GAME_GLYPHX_MULTIPLAYER && IsOwnedByPlayer)) { 1944 if (What_Am_I() == RTTI_VESSEL) { 1945 // Speak(VOX_SHIP_LOST); // MBL 02.06.2020 1946 Speak(VOX_SHIP_LOST, House, Center_Coord()); 1947 } else { 1948 // Speak(VOX_UNIT_LOST); // MBL 02.06.2020 1949 Speak(VOX_UNIT_LOST, House, Center_Coord()); 1950 } 1951 } 1952 } 1953 1954 1955 /*********************************************************************************************** 1956 * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * 1957 * * 1958 * This routine will return with the greatest threat (best target) for this object. For * 1959 * movable ground object, they won't automatically return ANY target if this object is * 1960 * cloaked. Otherwise, cloaking is relatively useless. * 1961 * * 1962 * INPUT: method -- The request method (bit flags) to use when scanning for a target. * 1963 * * 1964 * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * 1965 * TARGET_NONE is returned. * 1966 * * 1967 * WARNINGS: none * 1968 * * 1969 * HISTORY: * 1970 * 07/08/1995 JLB : Created. * 1971 * 07/10/1996 JLB : Handles scan range limitation. * 1972 *=============================================================================================*/ 1973 TARGET FootClass::Greatest_Threat(ThreatType method) const 1974 { 1975 assert(IsActive); 1976 1977 /* 1978 ** If the scan is forced to be limited, then limit the scan now. 1979 */ 1980 if (IsScanLimited) { 1981 method = method & ~THREAT_AREA; 1982 method = method | THREAT_RANGE; 1983 } 1984 1985 /* 1986 ** If this object can cloak, then it won't select a target automatically. 1987 */ 1988 if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { 1989 return(TARGET_NONE); 1990 } 1991 1992 if (!(method & (THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_TIBERIUM|THREAT_BOATS|THREAT_CIVILIANS|THREAT_POWER|THREAT_FAKES|THREAT_FACTORIES|THREAT_BASE_DEFENSE))) { 1993 if (What_Am_I() != RTTI_VESSEL) { 1994 method = method | THREAT_GROUND; 1995 } else { 1996 method = method | THREAT_BOATS|THREAT_GROUND; 1997 } 1998 } 1999 2000 /* 2001 ** Perform the search for the target. 2002 */ 2003 TARGET target = TechnoClass::Greatest_Threat(method); 2004 2005 /* 2006 ** If no target could be located and this object is under scan range 2007 ** restrictions, then this restriction must be lifted now. 2008 */ 2009 if (IsScanLimited && target == TARGET_NONE) { 2010 const_cast<FootClass*>(this)->IsScanLimited = false; // const_cast ST - 5/8/2019 2011 } 2012 2013 /* 2014 ** Return with final target found. 2015 */ 2016 return(target); 2017 } 2018 2019 2020 /*********************************************************************************************** 2021 * FootClass::Detach -- Detaches a target from tracking systems. * 2022 * * 2023 * This routine will detach the specified target from the tracking systems of this object. * 2024 * It will be removed from the navigation computer and any queued mission record. * 2025 * * 2026 * INPUT: target -- The target to be removed from this object. * 2027 * * 2028 * all -- Is the unit really about to be eliminated? If this is true then even * 2029 * friendly contact (i.e., radio) must be eliminated. * 2030 * * 2031 * OUTPUT: none * 2032 * * 2033 * WARNINGS: none * 2034 * * 2035 * HISTORY: * 2036 * 07/18/1995 JLB : Created. * 2037 * 07/24/1996 JLB : Removes target from NavQueue list. * 2038 *=============================================================================================*/ 2039 void FootClass::Detach(TARGET target, bool all) 2040 { 2041 assert(IsActive); 2042 2043 TechnoClass::Detach(target, all); 2044 2045 if (!SpecialFlag) { 2046 if (ArchiveTarget == target) { 2047 ArchiveTarget = TARGET_NONE; 2048 } 2049 } 2050 2051 if (SuspendedNavCom == target) { 2052 SuspendedNavCom = TARGET_NONE; 2053 SuspendedMission = MISSION_NONE; 2054 } 2055 2056 /* 2057 ** If the navigation computer is assigned to the target, then the navigation 2058 ** computer must be cleared. 2059 */ 2060 if (NavCom == target) { 2061 NavCom = TARGET_NONE; 2062 Path[0] = FACING_NONE; 2063 Restore_Mission(); 2064 } 2065 2066 /* 2067 ** Remove the target from the NavQueue list as well. 2068 */ 2069 int loop_count = 0; 2070 for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { 2071 if (NavQueue[index] == target) { 2072 NavQueue[index] = TARGET_NONE; 2073 if (index < ARRAY_SIZE(NavQueue)-1) { 2074 memmove(&NavQueue[index], &NavQueue[index+1], ((ARRAY_SIZE(NavQueue)-index)-1) * sizeof(NavQueue[0])); 2075 NavQueue[ARRAY_SIZE(NavQueue)-1] = TARGET_NONE; 2076 index--; 2077 } 2078 } 2079 /* 2080 ** Extra safety check 2081 */ 2082 loop_count++; 2083 if (loop_count > ARRAY_SIZE(NavQueue)) { 2084 break; 2085 } 2086 } 2087 2088 /* 2089 ** If targeting the specified object and this unit is obviously heading 2090 ** toward the target to get within range, then abort the path. 2091 */ 2092 if (TarCom == target && House->IsHuman) { 2093 Path[0] = FACING_NONE; 2094 } 2095 } 2096 2097 2098 /*********************************************************************************************** 2099 * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * 2100 * * 2101 * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * 2102 * from the object. This function is overridden for those objects that can contain * 2103 * Tiberium. * 2104 * * 2105 * INPUT: none * 2106 * * 2107 * OUTPUT: Returns with the number of credits offloaded from the object. * 2108 * * 2109 * WARNINGS: This routine must be called multiple times in order to completely offload the * 2110 * Tiberium. When this routine return 0, all Tiberium has been offloaded. * 2111 * * 2112 * HISTORY: * 2113 * 07/19/1995 JLB : Created. * 2114 *=============================================================================================*/ 2115 int FootClass::Offload_Tiberium_Bail(void) 2116 { 2117 assert(IsActive); 2118 2119 return(0); 2120 } 2121 2122 2123 /*********************************************************************************************** 2124 * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * 2125 * * 2126 * This routine examines the specified cell to see if the object can enter it. This * 2127 * function is to be overridden for objects that could have the possibility of not being * 2128 * allowed to enter the cell. Typical objects at the FootClass level always return * 2129 * MOVE_OK. * 2130 * * 2131 * INPUT: cell -- The cell to examine. * 2132 * * 2133 * facing -- The direction that this cell might be entered from. * 2134 * * 2135 * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * 2136 * blockage. There are various other values that represent other blockage types. * 2137 * The value returned will indicated the most severe reason why entry into the cell * 2138 * is blocked. * 2139 * * 2140 * WARNINGS: none * 2141 * * 2142 * HISTORY: * 2143 * 07/19/1995 JLB : Created. * 2144 *=============================================================================================*/ 2145 MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const 2146 { 2147 assert(IsActive); 2148 2149 return MOVE_OK; 2150 } 2151 2152 2153 /*********************************************************************************************** 2154 * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * 2155 * * 2156 * This routine determines if it is legal to sell the object back. A foot class object can * 2157 * only be sold back if it is sitting on a repair bay. * 2158 * * 2159 * INPUT: none * 2160 * * 2161 * OUTPUT: Was the object successfully sold back? * 2162 * * 2163 * WARNINGS: none * 2164 * * 2165 * HISTORY: * 2166 * 08/13/1995 JLB : Created. * 2167 *=============================================================================================*/ 2168 bool FootClass::Can_Demolish(void) const 2169 { 2170 assert(IsActive); 2171 2172 StructType sell_struct = STRUCT_NONE; 2173 switch (What_Am_I()) { 2174 case RTTI_UNIT: 2175 sell_struct = STRUCT_REPAIR; 2176 break; 2177 case RTTI_AIRCRAFT: 2178 sell_struct = STRUCT_AIRSTRIP; 2179 break; 2180 default: 2181 break; 2182 } 2183 if (sell_struct != STRUCT_NONE) { 2184 if (In_Radio_Contact() && 2185 Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && 2186 *((BuildingClass *)Contact_With_Whom()) == sell_struct && 2187 Distance(Contact_With_Whom()) < 0x0080) { 2188 return(true); 2189 } 2190 } 2191 return(TechnoClass::Can_Demolish()); 2192 } 2193 2194 2195 /*********************************************************************************************** 2196 * FootClass::Sell_Back -- Causes this object to be sold back. * 2197 * * 2198 * When an object is sold back, a certain amount of money is refunded to the owner and then * 2199 * the object is removed from the game system. * 2200 * * 2201 * INPUT: control -- The action to perform. The only supported action is "1", which means * 2202 * to sell back. * 2203 * * 2204 * OUTPUT: none * 2205 * * 2206 * WARNINGS: none * 2207 * * 2208 * HISTORY: * 2209 * 08/13/1995 JLB : Created. * 2210 *=============================================================================================*/ 2211 void FootClass::Sell_Back(int control) 2212 { 2213 assert(IsActive); 2214 2215 if (control != 0) { 2216 if (House == PlayerPtr) { 2217 Speak(VOX_UNIT_SOLD); 2218 Sound_Effect(VOC_CASHTURN); 2219 } 2220 House->Refund_Money(Refund_Amount()); 2221 Stun(); 2222 Limbo(); 2223 delete this; 2224 } 2225 } 2226 2227 2228 /*********************************************************************************************** 2229 * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * 2230 * * 2231 * This routine comes in handy when determining where a travelling object will be at * 2232 * when considering the amount of time it would take for a normal unit to travel one cell. * 2233 * Using this information, an intelligent "approach target" logic can be employed. * 2234 * * 2235 * INPUT: none * 2236 * * 2237 * OUTPUT: Returns with the coordinate the object is at or soon will be. * 2238 * * 2239 * WARNINGS: none * 2240 * * 2241 * HISTORY: * 2242 * 08/13/1995 JLB : Created. * 2243 *=============================================================================================*/ 2244 COORDINATE FootClass::Likely_Coord(void) const 2245 { 2246 assert(IsActive); 2247 2248 if (Head_To_Coord()) { 2249 return(Head_To_Coord()); 2250 } 2251 return(Target_Coord()); 2252 } 2253 2254 2255 /*********************************************************************************************** 2256 * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * 2257 * * 2258 * This routine modify the specified cell if the unit is part of a formation. The * 2259 * adjustment will take into consideration the formation relative offset from the * 2260 * (presumed) center cell specified. * 2261 * * 2262 * INPUT: cell -- The cell to presume as the desired center point of the formation. * 2263 * * 2264 * OUTPUT: Returns with the cell that should be used as the actual destination. If this * 2265 * object is part of a formation, then the cell location will be appropriately * 2266 * adjusted. * 2267 * * 2268 * WARNINGS: none * 2269 * * 2270 * HISTORY: * 2271 * 03/11/1996 JLB : Created. * 2272 *=============================================================================================*/ 2273 CELL FootClass::Adjust_Dest(CELL cell) const 2274 { 2275 assert(IsActive); 2276 2277 if (IsFormationMove) { 2278 int xdest = Cell_X(cell); 2279 int ydest = Cell_Y(cell); 2280 2281 int newx = Bound(XFormOffset + xdest, Map.MapCellX, Map.MapCellX + Map.MapCellWidth -1); 2282 int newy = Bound(YFormOffset + ydest, Map.MapCellY, Map.MapCellY + Map.MapCellHeight -1); 2283 2284 cell = XY_Cell(newx, newy); 2285 } 2286 return(cell); 2287 } 2288 2289 2290 /*********************************************************************************************** 2291 * FootClass::Handle_Navigation_List -- Processes the navigation queue. * 2292 * * 2293 * This routine will process the navigation queue. If the queue is present and valid and * 2294 * there is currently no navigation target assigned to this object, then the first entry * 2295 * of the queue will be assigned. The remaining entries will move down. If the queue is * 2296 * to be processed as a circular list, then the first entry is appended to the end. * 2297 * * 2298 * INPUT: none * 2299 * * 2300 * OUTPUT: none * 2301 * * 2302 * WARNINGS: This routine might end up assigning a movement destination. * 2303 * * 2304 * HISTORY: * 2305 * 07/18/1996 JLB : Created. * 2306 *=============================================================================================*/ 2307 void FootClass::Handle_Navigation_List(void) 2308 { 2309 /* 2310 ** The navigation queue only needs to be processed if there is 2311 ** currently no navigation target for this object. 2312 */ 2313 if (!Target_Legal(NavCom)) { 2314 TARGET target = NavQueue[0]; 2315 2316 /* 2317 ** Check to see if the navigation queue even exists and 2318 ** has at least one valid entry. If it does, then process it by 2319 ** assigning the object's NavCom to the first entry on the list. 2320 */ 2321 if (Target_Legal(target)) { 2322 Assign_Destination(target); 2323 memmove(&NavQueue[0], &NavQueue[1], sizeof(NavQueue)-sizeof(NavQueue[0])); 2324 NavQueue[ARRAY_SIZE(NavQueue)-1] = TARGET_NONE; 2325 2326 /* 2327 ** If the navigation queue is to loop (indefinately), then append the 2328 ** target value from the first part to the end of the queue. 2329 */ 2330 if (IsNavQueueLoop) { 2331 for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { 2332 if (NavQueue[index] == TARGET_NONE) { 2333 NavQueue[index] = target; 2334 break; 2335 } 2336 } 2337 } 2338 } 2339 } 2340 } 2341 2342 2343 /*********************************************************************************************** 2344 * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * 2345 * * 2346 * This routine will append the destination target to the object's NavQueue list. After * 2347 * doing so, if the object is not doing anything important, then it will be started on * 2348 * that destination. This is functionally the same as Assign_Destination, but it stores * 2349 * the target to the NavQueue first. * 2350 * * 2351 * INPUT: target -- The movement target destination to append the queue. * 2352 * * 2353 * OUTPUT: none * 2354 * * 2355 * WARNINGS: The queue is of finite size and any queue requests that would exceed that size * 2356 * are ignored. If there are no queue entries pending and the unit is not * 2357 * otherwise occupied, then the queue target might be carried directly into the * 2358 * NavCom. * 2359 * * 2360 * HISTORY: * 2361 * 07/18/1996 JLB : Created. * 2362 *=============================================================================================*/ 2363 void FootClass::Queue_Navigation_List(TARGET target) 2364 { 2365 if (Target_Legal(target)) { 2366 int count; 2367 for (count = 0; count < ARRAY_SIZE(NavQueue); count++) { 2368 if (!Target_Legal(NavQueue[count])) break; 2369 } 2370 2371 /* 2372 ** If the target is this object itself, then this indicates that the 2373 ** queue list is to be processed as a loop. Otherwise, just tack the 2374 ** navigation target to the end of the list. 2375 */ 2376 if (target == As_Target() && count > 0) { 2377 IsNavQueueLoop = true; 2378 } else { 2379 if (count == 0) { 2380 IsNavQueueLoop = false; 2381 } 2382 if (count < ARRAY_SIZE(NavQueue)) { 2383 NavQueue[count] = target; 2384 } 2385 } 2386 2387 /* 2388 ** If this object isn't doing anything, then start acting on the 2389 ** navigation queue now. 2390 */ 2391 if (!Target_Legal(NavCom) && Mission == MISSION_GUARD) { 2392 Enter_Idle_Mode(); 2393 } 2394 } 2395 } 2396 2397 2398 /*********************************************************************************************** 2399 * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * 2400 * * 2401 * This routine will clear out any values in the navigation queue. This is the preferred * 2402 * way of aborting a navigation queue for a unit. If the unit is already travelling, it * 2403 * won't be interrupted by this routine. * 2404 * * 2405 * INPUT: none * 2406 * * 2407 * OUTPUT: none * 2408 * * 2409 * WARNINGS: This will clear the navigation list but not the navigation computer. Thus a * 2410 * unit will still travel to its current immediate destination. * 2411 * * 2412 * HISTORY: * 2413 * 07/30/1996 JLB : Created. * 2414 *=============================================================================================*/ 2415 void FootClass::Clear_Navigation_List(void) 2416 { 2417 for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { 2418 NavQueue[index] = TARGET_NONE; 2419 } 2420 } 2421 2422 2423 /*********************************************************************************************** 2424 * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * 2425 * * 2426 * This routine will determine if this object has permission to leave the map and thus * 2427 * leave the game. Typical objects with this permission are transports used to drop of * 2428 * reinforcements. * 2429 * * 2430 * INPUT: none * 2431 * * 2432 * OUTPUT: bool; Does this object have permission to travel off the map edge and leave the * 2433 * game? * 2434 * * 2435 * WARNINGS: none * 2436 * * 2437 * HISTORY: * 2438 * 08/05/1996 JLB : Created. * 2439 *=============================================================================================*/ 2440 bool FootClass::Is_Allowed_To_Leave_Map(void) const 2441 { 2442 /* 2443 ** If the unit hasn't entered the map yet, then don't allow leave the game. 2444 */ 2445 if (!IsLocked) return(false); 2446 2447 /* 2448 ** A unit that isn't marked as a loaner is a gift to the player. Such objects can never 2449 ** leave the map unless they are part of a team that gives it special permision. 2450 */ 2451 if (!IsALoaner && Mission != MISSION_RETREAT && (!Team.Is_Valid() || !Team->Is_Leaving_Map())) return(false); 2452 2453 return(true); 2454 } 2455 2456 2457 /*********************************************************************************************** 2458 * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * 2459 * * 2460 * This will examine this object to determine if it is suitable as a team recruit. Some * 2461 * objects are disqualified if they are otherwise premptively occupied. * 2462 * * 2463 * INPUT: house -- Pointer to the house that is trying to recruit this object. * 2464 * * 2465 * OUTPUT: bool; Is this object suitable for recruitment by a team. * 2466 * * 2467 * WARNINGS: none * 2468 * * 2469 * HISTORY: * 2470 * 09/14/1996 JLB : Created. * 2471 *=============================================================================================*/ 2472 bool FootClass::Is_Recruitable(HouseClass const * house) const 2473 { 2474 /* 2475 ** If not of the correct house presuasion, then recruitment is not allowed. 2476 */ 2477 if (house != NULL && house != House) { 2478 return(false); 2479 } 2480 2481 /* 2482 ** If the object is not a playing member of the game, then don't consider it available. 2483 */ 2484 if (IsInLimbo) { 2485 return(false); 2486 } 2487 2488 /* 2489 ** If it is already part of another team, then it is not available for 2490 ** general recruitment. 2491 */ 2492 if (Team.Is_Valid()) { 2493 return(false); 2494 } 2495 2496 /* 2497 ** If it is currently in a mission the precludes recruitment into a team, then 2498 ** return with this information. 2499 */ 2500 if (!Is_Recruitable_Mission(Mission)) { 2501 return(false); 2502 } 2503 2504 /* 2505 ** It was not disqualified for general team recruitment, so return that 2506 ** it is available. 2507 */ 2508 return(true); 2509 } 2510 2511 2512 /*********************************************************************************************** 2513 * FootClass::AI -- Handle general movement AI. * 2514 * * 2515 * This basically just sees if this object is within weapon range of the target and if * 2516 * so, it will stop movement so that firing may commence. This prevents the occasional * 2517 * case of an attacker driving right up to the defender before firing. * 2518 * * 2519 * INPUT: none * 2520 * * 2521 * OUTPUT: none * 2522 * * 2523 * WARNINGS: none * 2524 * * 2525 * HISTORY: * 2526 * 09/17/1996 JLB : Created. * 2527 *=============================================================================================*/ 2528 void FootClass::AI(void) 2529 { 2530 TechnoClass::AI(); 2531 2532 // FootClass::Per_Cell_Process does this function already. 2533 #ifdef OBSOLETE 2534 if (IsActive) { 2535 if (!IsScattering && !IsTethered && !IsInLimbo && What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && In_Range(TarCom)) { 2536 Assign_Destination(TARGET_NONE); 2537 } 2538 } 2539 #endif 2540 } 2541 2542 2543 /*********************************************************************************************** 2544 * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority. * 2545 * * 2546 * Some objects are on an important mission that must succeed. If the object is on such * 2547 * a mission, then it will be more aggressive in its movement action. * 2548 * * 2549 * INPUT: none * 2550 * * 2551 * OUTPUT: bool; Is this object on a priority mission? * 2552 * * 2553 * WARNINGS: none * 2554 * * 2555 * HISTORY: * 2556 * 09/30/1996 JLB : Created. * 2557 *=============================================================================================*/ 2558 bool FootClass::Is_On_Priority_Mission(void) const 2559 { 2560 if (Mission == MISSION_ENTER) return(true); 2561 return(false); 2562 } 2563 2564 2565 /*********************************************************************************************** 2566 * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * 2567 * * 2568 * This will try to make this mobile object leave the map. It does this by assigning a * 2569 * movement destination that is located off the edge of the map. * 2570 * * 2571 * INPUT: none * 2572 * * 2573 * OUTPUT: Returns with the number of game frames to delay before calling this routine * 2574 * again. * 2575 * * 2576 * WARNINGS: none * 2577 * * 2578 * HISTORY: * 2579 * 10/05/1996 JLB : Created. * 2580 *=============================================================================================*/ 2581 int FootClass::Mission_Retreat(void) 2582 { 2583 assert(IsActive); 2584 2585 enum { 2586 FIND_EDGE, 2587 TRAVELLING 2588 }; 2589 2590 switch (Status) { 2591 2592 /* 2593 ** Find a suitable edge to travel to and then assign destination there. 2594 */ 2595 case FIND_EDGE: 2596 if (Target_Legal(NavCom)) { 2597 Status = TRAVELLING; 2598 } else { 2599 2600 CELL cell = 0; 2601 2602 /* 2603 ** If this is part of a team, then pick the edge where the team as likely 2604 ** entered from. 2605 */ 2606 if (Team.Is_Valid() && Team->Class->Origin != -1) { 2607 cell = Map.Calculated_Cell(House->Control.Edge, Team->Class->Origin, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); 2608 } 2609 2610 /* 2611 ** If an edge hasn't been found, then try to find one that is not based on any 2612 ** team information. 2613 */ 2614 if (cell == 0) { 2615 cell = Map.Calculated_Cell(House->Control.Edge, -1, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); 2616 } 2617 2618 assert(cell == 0); // An edge cell must be found! 2619 2620 Assign_Destination(::As_Target(cell)); 2621 Status = TRAVELLING; 2622 } 2623 break; 2624 2625 /* 2626 ** While travelling, monitor that all is proceeding according to plan. 2627 */ 2628 case TRAVELLING: 2629 if (!Target_Legal(NavCom)) { 2630 Status = FIND_EDGE; 2631 } 2632 break; 2633 } 2634 2635 return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); 2636 }