INFANTRY.CPP (143612B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\infantry.cpv 2.19 16 Oct 1995 16:50:30 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 : INFANTRY.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : August 15, 1994 * 28 * * 29 * Last Update : August 15, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * 34 * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * 35 * InfantryClass::As_Target -- Converts the infantry unit into a target value. * 36 * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * 37 * InfantryClass::Assign_Mission -- Make sure he's out of boxing mode first * 38 * InfantryClass::Assign_Target -- Gives the infantry a combat target. * 39 * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * 40 * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * 41 * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * 42 * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * 43 * InfantryClass::Detach -- Removes the specified target from targeting computer. * 44 * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * 45 * InfantryClass::Draw_It -- Draws a unit object. * 46 * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * 47 * InfantryClass::Fire_At -- Fires projectile from infantry unit. * 48 * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * 49 * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * 50 * InfantryClass::InfantryClass -- The constructor for infantry objects. * 51 * InfantryClass::Init -- Initialize the infantry object system. * 52 * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * 53 * InfantryClass::Look -- The infantry performs a look operation. * 54 * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * 55 * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* 56 * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * 57 * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * 58 * InfantryClass::Read_INI -- Reads units from scenario INI file. * 59 * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing * 60 * InfantryClass::Receive_Message -- Process radio messages * 61 * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * 62 * InfantryClass::Response_Move -- Plays infantry response to movement order. * 63 * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * 64 * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * 65 * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * 66 * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* 67 * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * 68 * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * 69 * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * 70 * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * 71 * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * 72 * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * 73 * InfantryClass::operator delete -- Returns the infantry object back to the free pool * 74 * InfantryClass::operator new -- Allocates an infantry object from the free pool. * 75 * InfantryClass::~InfantryClass -- Default destructor for infantry units. * 76 * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * 77 * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * 78 * InfantryClass::Validate -- validates infantry pointer. * 79 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 80 81 #include "function.h" 82 83 84 int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; 85 86 int Infantry_Kick_Damage[] = {10,15}; 87 int Infantry_Punch_Damage[] = { 4, 7}; 88 89 /* 90 ** This contains the value of the Virtual Function Table Pointer 91 */ 92 void * InfantryClass::VTable; 93 94 95 /*************************************************************************** 96 ** This is the array of constant data associated with infantry maneuvers. It 97 ** specifies the frame rate as well as if the animation can be aborted. 98 */ 99 // interruptable, mobile, randomstart, rate 100 DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { 101 {true, false, false, 0}, // DO_STAND_READY 102 {true, false, false, 0}, // DO_STAND_GUARD 103 {true, false, false, 0}, // DO_PRONE 104 {true, true, true, 2}, // DO_WALK 105 {true, false, false, 1}, // DO_FIRE_WEAPON 106 {false, true, false, 2}, // DO_LIE_DOWN 107 {true, true, true, 2}, // DO_CRAWL 108 {false, false, false, 3}, // DO_GET_UP 109 {true, false, false, 1}, // DO_FIRE_PRONE 110 {true, false, false, 2}, // DO_IDLE1 111 {true, false, false, 2}, // DO_IDLE2 112 {false, false, false, 2}, // DO_ON_GUARD 113 {true, false, false, 2}, // DO_FIGHT_READY 114 {false, false, false, 2}, // DO_PUNCH 115 {false, false, false, 2}, // DO_KICK 116 {false, false, false, 2}, // DO_PUNCH_HIT1 117 {false, false, false, 2}, // DO_PUNCH_HIT2 118 {false, false, false, 1}, // DO_PUNCH_DEATH 119 {false, false, false, 2}, // DO_KICK_HIT1 120 {false, false, false, 2}, // DO_KICK_HIT2 121 {false, false, false, 1}, // DO_KICK_DEATH 122 {false, false, false, 2}, // DO_READY_WEAPON 123 {false, false, false, 2}, // DO_GUN_DEATH 124 {false, false, false, 2}, // DO_EXPLOSION_DEATH 125 {false, false, false, 2}, // DO_EXPLOSION2_DEATH 126 {false, false, false, 2}, // DO_GRENADE_DEATH 127 {false, false, false, 2}, // DO_FIRE_DEATH 128 {false, false, false, 2}, // DO_GESTURE1 129 {false, false, false, 2}, // DO_SALUTE1 130 {false, false, false, 2}, // DO_GESTURE2 131 {false, false, false, 2}, // DO_SALUTE2 132 {true, false, false, 2}, // DO_PULL_GUN 133 {true, false, false, 2}, // DO_PLEAD 134 {true, false, false, 2}, // DO_PLEAD_DEATH 135 }; 136 137 138 /*********************************************************************************************** 139 * InfantryClass::Validate -- validates infantry pointer. * 140 * * 141 * INPUT: * 142 * none. * 143 * * 144 * OUTPUT: * 145 * 1 = ok, 0 = error * 146 * * 147 * WARNINGS: * 148 * none. * 149 * * 150 * HISTORY: * 151 * 08/09/1995 BRR : Created. * 152 *=============================================================================================*/ 153 #ifdef CHEAT_KEYS 154 int InfantryClass::Validate(void) const 155 { 156 int num; 157 158 num = Infantry.ID(this); 159 if (num < 0 || num >= INFANTRY_MAX) { 160 Validate_Error("INFANTRY"); 161 return (0); 162 } 163 else 164 return (1); 165 } 166 #else 167 #define Validate() 168 #endif 169 170 171 #ifdef CHEAT_KEYS 172 /*********************************************************************************************** 173 * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * 174 * * 175 * This routine is used by the debug version to display pertinent information about the * 176 * infantry unit. * 177 * * 178 * INPUT: mono -- The monochrome screen to display the debug information to. * 179 * * 180 * OUTPUT: none * 181 * * 182 * WARNINGS: none * 183 * * 184 * HISTORY: * 185 * 09/01/1994 JLB : Created. * 186 *=============================================================================================*/ 187 void InfantryClass::Debug_Dump(MonoClass *mono) const 188 { 189 Validate(); 190 mono->Set_Cursor(0, 0);mono->Print( 191 "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n" 192 "³ ³ ³ ³ ³ ³ ³ ³ ³\n" 193 "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" 194 "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" 195 "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" 196 "³Owned.........³ ³ ³Last Message: ³\n" 197 "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" 198 "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" 199 "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" 200 "³Locked on Map.³ ³ ³ \n" 201 "³Is Prone......³ ³ ³ \n" 202 "³Is A Loner....³ ³ ³ \n" 203 "³Deploying.....³ ³ ³ \n" 204 "³Rotating......³ ³ ³ \n" 205 "³Firing........³ ³ ³ \n" 206 "³Driving.......³ ³ ³ \n" 207 "³To Look.......³ ³ ³ \n" 208 "³Recoiling.....³ ³ ³ \n" 209 "³To Display....³ ³ ³ \n" 210 "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); 211 mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); 212 mono->Text_Print("X", 16 + (IsProne?2:0), 10); 213 mono->Set_Cursor(33, 7);mono->Printf("%2d", Fear); 214 mono->Set_Cursor(41, 7);mono->Printf("%2d", Doing); 215 FootClass::Debug_Dump(mono); 216 } 217 #endif 218 219 InfantryClass::InfantryClass(void) : Class(0) {}; // Default constructor does nothing. 220 221 222 /*********************************************************************************************** 223 * InfantryClass::InfantryClass -- The constructor for infantry objects. * 224 * * 225 * This is the constructor used when creating an infantry unit. All values are required * 226 * except for facing and position. If these are absent, then the infantry is created in * 227 * a state of limbo -- not placed upon the map. * 228 * * 229 * INPUT: see below... * 230 * * 231 * OUTPUT: none * 232 * * 233 * WARNINGS: none * 234 * * 235 * HISTORY: * 236 * 09/01/1994 JLB : Created. * 237 *=============================================================================================*/ 238 InfantryClass::InfantryClass(InfantryType classid, HousesType house) : 239 Class(&InfantryTypeClass::As_Reference(classid)), 240 FootClass(house) 241 { 242 243 /* 244 ** For two shooters, clear out the second shot flag -- it will be set the first time 245 ** the object fires. For non two shooters, set the flag since it will never be cleared 246 ** and the second shot flag tells the system that normal rearm times apply -- this is 247 ** what is desired for non two shooters. 248 */ 249 if (Class->IsTwoShooter) { 250 IsSecondShot = false; 251 } else { 252 IsSecondShot = true; 253 } 254 Doing = DO_NOTHING; 255 Fear = 0; // Starts completely brave. 256 IsProne = false; 257 IsStoked = false; 258 IsBoxing = false; 259 IsTechnician = false; 260 Strength = Class->MaxStrength; 261 262 /* 263 ** Civilians carry much less ammo than soldiers do. 264 */ 265 Ammo = Class->MaxAmmo; 266 267 /* 268 ** Keep count of the number of units created. Dont track civilians. 269 */ 270 if (!Class->IsCivilian && GameToPlay == GAME_INTERNET){ 271 House->InfantryTotals->Increment_Unit_Total((int)classid); 272 } 273 274 #ifdef USE_RA_AI 275 // 276 // Added for RA AI in TD. ST - 7/26/2019 9:12AM 277 // 278 House->Tracking_Add(this); 279 #endif // USE_RA_AI 280 281 StopDriverFrame = -1; 282 } 283 284 285 /*********************************************************************************************** 286 * InfantryClass::~InfantryClass -- Default destructor for infantry units. * 287 * * 288 * This is the default destructor for infantry type units. It will put the infantry into * 289 * a limbo state if it isn't already in that state and the game is still active. * 290 * * 291 * INPUT: none * 292 * * 293 * OUTPUT: none * 294 * * 295 * WARNINGS: none * 296 * * 297 * HISTORY: * 298 * 01/10/1995 JLB : Created. * 299 *=============================================================================================*/ 300 InfantryClass::~InfantryClass(void) 301 { 302 if (GameActive && Class) { 303 #ifdef USE_RA_AI 304 // 305 // Added for RA AI in TD. ST - 7/26/2019 9:12AM 306 // 307 House->Tracking_Remove(this); 308 #endif //USE_RA_AI 309 Limbo(); 310 } 311 if (GameActive && Team) Team->Remove(this); 312 } 313 314 315 /*********************************************************************************************** 316 * InfantryClass::operator new -- Allocates an infantry object from the free pool. * 317 * * 318 * This will allocate an infantry object from the infantry object free pool. If there is * 319 * no available slot, then NULL is returned. * 320 * * 321 * INPUT: none * 322 * * 323 * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * 324 * allocated. * 325 * * 326 * WARNINGS: none * 327 * * 328 * HISTORY: * 329 * 09/01/1994 JLB : Created. * 330 *=============================================================================================*/ 331 void * InfantryClass::operator new(size_t) 332 { 333 void * ptr = Infantry.Allocate(); 334 if (ptr) { 335 ((InfantryClass *)ptr)->Set_Active(); 336 } 337 return(ptr); 338 } 339 340 341 /*********************************************************************************************** 342 * InfantryClass::operator delete -- Returns the infantry object back to the free pool * 343 * * 344 * This routine is used return an infantry object back to the system. * 345 * * 346 * INPUT: ptr -- Pointer to the infantry object to delete. * 347 * * 348 * OUTPUT: none * 349 * * 350 * WARNINGS: none * 351 * * 352 * HISTORY: * 353 * 09/08/1994 JLB : Created. * 354 *=============================================================================================*/ 355 void InfantryClass::operator delete(void *ptr) 356 { 357 if (ptr) { 358 ((InfantryClass *)ptr)->IsActive = false; 359 } 360 Infantry.Free((InfantryClass *)ptr); 361 362 //Map.Validate(); 363 } 364 365 366 /*********************************************************************************************** 367 * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * 368 * * 369 * This routine applies the damage specified to the infantry object. It is possible that * 370 * this routine will DESTROY the infantry unit in the process. * 371 * * 372 * INPUT: damage -- The damage points to inflict. * 373 * * 374 * distance -- The distance from the damage center point to the object's center point.* 375 * * 376 * warhead -- The warhead type that is inflicting the damage. * 377 * * 378 * source -- Who is responsible for inflicting the damage. * 379 * * 380 * OUTPUT: bool; Was the infantry unit destroyed by this damage? * 381 * * 382 * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * 383 * for this in the code that follows the call to Take_Damage(). * 384 * * 385 * HISTORY: * 386 * 09/08/1994 JLB : Created. * 387 * 11/22/1994 JLB : Shares base damage handler for techno objects. * 388 * 03/31/1995 JLB : Revenge factor. * 389 *=============================================================================================*/ 390 ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) 391 { 392 Validate(); 393 ResultType res = RESULT_NONE; 394 395 IsFiring = false; 396 397 /* 398 ** Prone infantry take only half damage, but never below one damage point. 399 */ 400 if (IsProne && damage) { 401 damage >>= 1; 402 // damage = MAX(damage, 1); 403 } 404 405 406 //Mono_Printf("Infantry Take_Damage(%d, %d, %d, %p)\r", damage, distance, warhead, source); 407 //Get_Key(); 408 409 res = FootClass::Take_Damage(damage, distance, warhead, source); 410 411 /* 412 ** Flame thrower guys take more damage because of the exposed pilot light 413 ** on their flame gun. 414 */ 415 if (damage && res != RESULT_DESTROYED && *this == INFANTRY_E4) { 416 damage = 5; 417 ResultType newres = FootClass::Take_Damage(damage, distance, warhead, source); 418 res = MAX(res, newres); 419 } 420 421 if (res == RESULT_NONE) return(res); 422 423 if (res == RESULT_DESTROYED) { 424 Death_Announcement(source); 425 Stop_Driver(); 426 Stun(); 427 Mission = MISSION_NONE; 428 Assign_Mission(MISSION_GUARD); 429 Commence(); 430 431 /* 432 ** Flame thrower infantry always go out with a bang. 433 */ 434 if (*this == INFANTRY_E4) { 435 new AnimClass(ANIM_NAPALM1, Coord); 436 Explosion_Damage(Coord, 80, 0, WARHEAD_FIRE); 437 } 438 439 if (*this == INFANTRY_E2) { 440 new AnimClass(ANIM_ART_EXP1, Coord); 441 Explosion_Damage(Coord, 30, 0, WARHEAD_HE); 442 } 443 444 if (*this == INFANTRY_E5) { 445 new AnimClass(ANIM_CHEM_BALL, Coord); 446 Explosion_Damage(Coord, 80, 0, WARHEAD_HE); 447 } 448 449 VocType sound; 450 VocType altsound; 451 if (*this == INFANTRY_RAMBO) { 452 // if (Sim_Random_Pick(0, 3) != 1) { 453 sound = VOC_RAMBO_YELL; 454 // } else { 455 // sound = VOC_RAMBO_OHSH; 456 // } 457 altsound = sound; 458 } else { 459 sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM5); 460 altsound = VOC_YELL1; 461 } 462 463 /* 464 ** The type of warhead determines the animation the infantry 465 ** will perform when killed. 466 */ 467 switch (warhead) { 468 case WARHEAD_FEEDME: 469 if (source) { 470 source->Strength += 30; 471 if (source->Strength > source->Class_Of().MaxStrength) { 472 source->Strength = source->Class_Of().MaxStrength; 473 } 474 } 475 // Fall thru to WARHEAD_SA: 476 477 case WARHEAD_HEADBUTT: 478 case WARHEAD_SPORE: 479 case WARHEAD_HOLLOW_POINT: 480 case WARHEAD_SA: 481 Sound_Effect(sound, Coord); 482 Do_Action(DO_GUN_DEATH, true); 483 break; 484 485 case WARHEAD_HE: 486 Sound_Effect(sound, Coord); 487 Do_Action(DO_EXPLOSION_DEATH, true); 488 break; 489 490 case WARHEAD_AP: 491 Sound_Effect(sound, Coord); 492 Do_Action(DO_GRENADE_DEATH, true); 493 break; 494 495 case WARHEAD_PB: 496 case WARHEAD_LASER: 497 case WARHEAD_FIRE: 498 Sound_Effect(altsound, Coord); 499 Do_Action(DO_FIRE_DEATH, true); 500 break; 501 502 case WARHEAD_FIST: 503 Sound_Effect(sound, Coord); 504 Do_Action(DO_PUNCH_DEATH,true); 505 break; 506 507 case WARHEAD_FOOT: 508 Sound_Effect(sound, Coord); 509 Do_Action(DO_KICK_DEATH,true); 510 break; 511 } 512 513 return(res); 514 } 515 516 /* 517 ** When infantry gets hit, it gets scared. 518 */ 519 if (res != RESULT_DESTROYED) { 520 COORDINATE c4 = (source) ? source->Coord : NULL; 521 if (source) { 522 Scatter(c4); 523 } 524 525 #ifdef BOXING 526 if (IsBoxing) { 527 int addval = 0; 528 529 switch (warhead) { 530 case WARHEAD_FIST: 531 if (damage == Infantry_Punch_Damage[1]) addval++; 532 Do_Action( (DoType) ( (int)DO_PUNCH_HIT1 + addval),true); 533 break; 534 535 case WARHEAD_FOOT: 536 if (damage == Infantry_Kick_Damage[1]) addval++; 537 Do_Action( (DoType) ( (int)DO_KICK_HIT1 + addval),true); 538 break; 539 } 540 } else { 541 #endif 542 if (source && Fear < FEAR_SCARED) { 543 if (Class->IsFraidyCat) { 544 Fear = FEAR_PANIC; 545 } else { 546 Fear = FEAR_SCARED; 547 } 548 } else { 549 int morefear = FEAR_ANXIOUS; 550 if (Health_Ratio() > 0x0080) morefear /= 4; 551 Fear = MIN((int)Fear + morefear, FEAR_MAXIMUM); 552 } 553 #ifdef BOXING 554 } 555 #endif 556 } 557 return(res); 558 } 559 560 561 /*********************************************************************************************** 562 * InfantryClass::Draw_It -- Draws a unit object. * 563 * * 564 * This routine is the one that actually draws a unit object. It displays the unit * 565 * according to its current state flags and centered at the location specified. * 566 * * 567 * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * 568 * * 569 * window -- The clipping window to use. * 570 * * 571 * OUTPUT: none * 572 * * 573 * WARNINGS: none * 574 * * 575 * HISTORY: * 576 * 06/20/1994 JLB : Created. * 577 * 06/27/1994 JLB : Takes a window parameter. * 578 * 08/15/1994 JLB : Converted to infantry support. * 579 *=============================================================================================*/ 580 void InfantryClass::Draw_It(int x, int y, WindowNumberType window) 581 { 582 Validate(); 583 void const * shapefile; // Working shape file pointer. 584 int facing = Facing_To_32(PrimaryFacing.Current()); 585 586 /* 587 ** Verify the legality of the unit class. 588 */ 589 shapefile = Class->Get_Image_Data(); 590 if (!shapefile) return; 591 592 y += 4; 593 x -= 2; 594 595 /* 596 ** Fetch the basic body shape pointer. This requires taking into account 597 ** the current animation stage. 598 */ 599 int shapenum; 600 int facenum; 601 602 shapenum = 0; 603 facenum = HumanShape[facing]; 604 605 /* 606 ** Fetch the shape pointer to use for the infantry. This is controlled by what 607 ** choreograph sequence the infantry is performing, it's facing, and whether it 608 ** is prone. 609 */ 610 DoType doit = Doing; 611 if (doit == DO_NOTHING) doit = DO_STAND_READY; 612 613 /* 614 ** Hold the walk pose for a couple of frames after we come to a stop to try and avoid the problem where a moving infantry 615 ** goes into the stand pose for a single frame when pausing in the assigned cell destination. ST - 9/4/2019 1:39PM 616 */ 617 if (doit == DO_STAND_READY) { 618 if (window == WINDOW_VIRTUAL) { 619 if (StopDriverFrame != -1) { 620 if (Frame - StopDriverFrame <= 2) { 621 if (Path[0] != FACING_NONE) { 622 doit = DO_WALK; 623 } 624 } 625 } 626 } 627 } 628 629 shapenum = Class->DoControls[doit].Count; 630 shapenum = Fetch_Stage() % MAX(shapenum, 1); 631 if (Class->DoControls[doit].Jump) { 632 shapenum += facenum * Class->DoControls[doit].Jump; 633 } 634 shapenum += Class->DoControls[doit].Frame; 635 636 #ifdef BOXING 637 // BG hack to get him to face right when he's supposed to. 638 if (IsBoxing && BodyFacing<128) shapenum += 47; 639 #endif 640 641 /* 642 ** Actually draw the root body of the unit. 643 */ 644 Techno_Draw_Object(shapefile, shapenum, x, y, window); 645 // CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, House->Remap_Table(IsBlushing, true), Map.UnitShadow); 646 647 FootClass::Draw_It(x, y, window); 648 } 649 650 651 /*********************************************************************************************** 652 * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * 653 * * 654 * This routine will handle any special operations that need to be performed once each * 655 * cell travelled. This includes radioing a transport that is is now clear and the * 656 * transport is free to leave. * 657 * * 658 * INPUT: none * 659 * * 660 * OUTPUT: none * 661 * * 662 * WARNINGS: none * 663 * * 664 * HISTORY: * 665 * 09/08/1994 JLB : Created. * 666 * 03/01/1995 JLB : Capture building options. * 667 * 05/31/1995 JLB : Capture is always successful now. * 668 *=============================================================================================*/ 669 void InfantryClass::Per_Cell_Process(bool center) 670 { 671 Validate(); 672 CellClass *cellptr = &Map[Coord_Cell(Coord)]; 673 674 /* 675 ** If the infantry unit is entering a cell that contains the building it is trying to 676 ** capture, then capture it. 677 */ 678 if (center && Mission == MISSION_CAPTURE) { 679 TechnoClass * tech = cellptr->Cell_Techno(); 680 if (tech && tech->As_Target() == NavCom && tech->What_Am_I() == RTTI_BUILDING && !tech->Can_Capture()) { 681 tech = NULL; 682 Assign_Destination(TARGET_NONE); 683 } 684 if (tech && tech->As_Target() == NavCom) { 685 tech->Captured(House); 686 Delete_This(); 687 return; 688 } else { 689 //#ifdef NEVER 690 if (!Target_Legal(NavCom)) { 691 Enter_Idle_Mode(); 692 if (Map[Coord_Cell(Coord)].Cell_Building()) { 693 Scatter(0, true); 694 } 695 } 696 //#endif 697 } 698 } 699 700 /* 701 ** Infantry entering a transport vehicle will break radio contact 702 ** at attach itself to the transporter. 703 */ 704 TechnoClass * techno = Contact_With_Whom(); 705 if (center && Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { 706 if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { 707 Limbo(); 708 techno->Attach(this); 709 } 710 return; 711 } 712 713 /* 714 ** If the infantry unit is entering a cell that contains the building it is trying to 715 ** sabotage, then sabotage it. 716 */ 717 if (center && Mission == MISSION_SABOTAGE) { 718 BuildingClass *building = cellptr->Cell_Building(); 719 if (building && building->As_Target() == NavCom) { 720 int temp = Special.IsScatter; 721 722 building->IsGoingToBlow = true; 723 building->Clicked_As_Target(PlayerPtr->Class->House, 20); // 2019/09/20 JAS - Added record of who clicked on the object 724 building->Clicked_As_Target(building->Owner(), 20); 725 building->CountDown.Set(20); 726 building->WhomToRepay = As_Target(); 727 Special.IsScatter = true; 728 NavCom = TARGET_NONE; 729 Do_Uncloak(); 730 Arm = Rearm_Delay(true); 731 Scatter(building->Center_Coord(), true); // RUN AWAY! 732 Special.IsScatter = temp; 733 return; 734 } 735 } 736 737 /* 738 ** If this unit is on a teather, then cut it at this time so that 739 ** the "parent" unit is free to proceed. Note that the parent 740 ** unit might actually be a building. 741 */ 742 if (center && IsTethered) { 743 Transmit_Message(RADIO_UNLOADED); 744 if (House->Class->House == HOUSE_GOOD) { 745 Do_Action(DO_GESTURE1); 746 } else { 747 Do_Action(DO_GESTURE2); 748 } 749 750 /* 751 ** Rambo types give a gung-ho comment when unloaded. 752 */ 753 if (*this == INFANTRY_RAMBO) { 754 Sound_Effect(VOC_RAMBO_ROCK, Coord); 755 } 756 757 /* 758 ** If the cell is now full of infantry, tell them all to scatter 759 ** in order to make room for more. 760 */ 761 if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { 762 cellptr->Incoming(0, true); 763 } 764 } 765 766 /* 767 ** When the infantry reaches the center of the cell, it may begin a new mission. 768 */ 769 if (center) { 770 Commence(); 771 } 772 773 Look(true); 774 FootClass::Per_Cell_Process(center); 775 776 /* 777 ** If over Tiberium, then this infantry unit will take damage. 778 */ 779 if (IsActive && !IsInLimbo && center && cellptr->Land_Type() == LAND_TIBERIUM && *this != INFANTRY_E5) { 780 int damage = 2; 781 Take_Damage(damage, 0, WARHEAD_FIRE); 782 } 783 } 784 785 786 /*********************************************************************************************** 787 * InfantryClass::Detach -- Removes the specified target from targeting computer. * 788 * * 789 * This is a support routine that removes the target specified from any targeting or * 790 * navigation computers. When a target is destroyed or removed from the game system, * 791 * the target must be removed from any tracking systems of the other units. This routine * 792 * handles removal for infantry units. * 793 * * 794 * INPUT: target -- The target to remove from the infantry unit's tracking systems. * 795 * * 796 * all -- Is the target going away for good as opposed to just cloaking/hiding? * 797 * * 798 * OUTPUT: none * 799 * * 800 * WARNINGS: none * 801 * * 802 * HISTORY: * 803 * 09/08/1994 JLB : Created. * 804 *=============================================================================================*/ 805 void InfantryClass::Detach(TARGET target, bool all) 806 { 807 Validate(); 808 if (TarCom == target) { 809 IsFiring = false; 810 } 811 FootClass::Detach(target, all); 812 } 813 814 815 /*********************************************************************************************** 816 * InfantryClass::As_Target -- Converts the infantry unit into a target value. * 817 * * 818 * This support routine is used to convert the infantry object (as a pointer) into a target * 819 * value (which is a number). * 820 * * 821 * INPUT: none * 822 * * 823 * OUTPUT: Returns the infantry unit as a target value. * 824 * * 825 * WARNINGS: none * 826 * * 827 * HISTORY: * 828 * 09/08/1994 JLB : Created. * 829 *=============================================================================================*/ 830 TARGET InfantryClass::As_Target(void) const 831 { 832 Validate(); 833 return(Build_Target(KIND_INFANTRY, Infantry.ID(this))); 834 } 835 836 837 /*********************************************************************************************** 838 * InfantryClass::Init -- Initialize the infantry object system. * 839 * * 840 * This routine will force the infantry object system into its empty initial state. It * 841 * is called when the scenario needs to be cleared in preparation for a scenario load. * 842 * * 843 * INPUT: none * 844 * * 845 * OUTPUT: none * 846 * * 847 * WARNINGS: none * 848 * * 849 * HISTORY: * 850 * 09/08/1994 JLB : Created. * 851 *=============================================================================================*/ 852 void InfantryClass::Init(void) 853 { 854 InfantryClass *ptr; 855 856 Infantry.Free_All(); 857 858 ptr = new InfantryClass(); 859 VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; 860 delete ptr; 861 } 862 863 864 /*********************************************************************************************** 865 * InfantryClass::Look -- The infantry performs a look operation. * 866 * * 867 * This routine will cause the infantry unit to "look". For player owned infantry, this * 868 * causes the dark shroud to be pushed back. * 869 * * 870 * INPUT: incremental -- If it is known that the infantry performed a look in the last cell * 871 * it was in AND it has only moved one cell, then setting this * 872 * parameter to true will perform a faster "incremental" look * 873 * operation. * 874 * * 875 * OUTPUT: none * 876 * * 877 * WARNINGS: This is a relatively slow routine. Call ONLY when necessary. * 878 * * 879 * HISTORY: * 880 * 09/08/1994 JLB : Created. * 881 *=============================================================================================*/ 882 void InfantryClass::Look(bool incremental) 883 { 884 Validate(); 885 int sight; // Number of cells to sight. 886 887 if (!IsInLimbo) { 888 //if (IsOwnedByPlayer) { // Changed for multiple player mapping. ST - 3/6/2019 1:27PM 889 if (House->IsHuman || GameToPlay != GAME_NORMAL) { 890 sight = Class->SightRange; 891 892 if (sight) { 893 Map.Sight_From(House, Coord_Cell(Coord), sight, incremental); // Passed our house into Map.Sight_From since it now needs to know who it is performing the action on behalf of. ST - 3/28/2019 2:55PM 894 } 895 } 896 } 897 } 898 899 900 /*********************************************************************************************** 901 * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * 902 * * 903 * This routine updates the infantry's navigation computer so that the infantry will * 904 * travel to the destination target specified. * 905 * * 906 * INPUT: target -- The target to have the infantry unit move to. * 907 * * 908 * OUTPUT: none * 909 * * 910 * WARNINGS: none * 911 * * 912 * HISTORY: * 913 * 09/08/1994 JLB : Created. * 914 *=============================================================================================*/ 915 void InfantryClass::Assign_Destination(TARGET target) 916 { 917 Validate(); 918 /* 919 ** Special flag so that infantry will start heading in the right direction immediately. 920 */ 921 if (Target_Legal(target)) { 922 Stop_Driver(); 923 } 924 925 /* 926 ** When telling an infantry soldier to move to a location twice, then this 927 ** means that movement is more important than safety. Get up and run! 928 */ 929 if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat) { 930 Do_Action(DO_GET_UP); 931 } 932 933 /* 934 ** Handle entry logic here. 935 */ 936 if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { 937 938 /* 939 ** If not already in radio contact (presumed with the transport), then 940 ** either try to establish contact if allowed, or just move close and 941 ** wait until radio contact can be established. 942 */ 943 if (!In_Radio_Contact()) { 944 TechnoClass * techno = As_Techno(target); 945 946 if (techno) { 947 948 /* 949 ** Determine if the transport is already in radio contact. If so, then just move 950 ** toward the transport and try to establish contact at a later time. 951 */ 952 if (techno->In_Radio_Contact()) { 953 954 // TCTCTC -- call for an update from the transport to get a good rondezvous position. 955 956 ArchiveTarget = target; 957 } else { 958 if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { 959 if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { 960 Transmit_Message(RADIO_OVER_OUT); 961 Assign_Mission(MISSION_MOVE); 962 } 963 } else { 964 Assign_Mission(MISSION_MOVE); 965 } 966 } 967 } 968 } else { 969 Path[0] = FACING_NONE; 970 } 971 } else { 972 Path[0] = FACING_NONE; 973 } 974 FootClass::Assign_Destination(target); 975 } 976 977 978 /*********************************************************************************************** 979 * InfantryClass::Assign_Target -- Gives the infantry a combat target. * 980 * * 981 * This routine will update the infantry's targeting computer so that it will try to * 982 * attack the target specified. This might result in it moving to be within range and thus * 983 * also cause adjustment of the navigation computer. * 984 * * 985 * INPUT: target -- The target that this infantry should attack. * 986 * * 987 * OUTPUT: none * 988 * * 989 * WARNINGS: none * 990 * * 991 * HISTORY: * 992 * 09/08/1994 JLB : Created. * 993 * 06/30/1995 JLB : Tries to capture target if possible. * 994 *=============================================================================================*/ 995 void InfantryClass::Assign_Target(TARGET target) 996 { 997 Validate(); 998 Path[0] = FACING_NONE; 999 FootClass::Assign_Target(target); 1000 1001 /* 1002 ** If this is an infantry that can only capture, then also assign its destination to the 1003 ** target specified. 1004 */ 1005 if (!Target_Legal(NavCom) && Class->IsCapture && Class->Primary == WEAPON_NONE) { 1006 BuildingClass const * building = As_Building(target); 1007 if (building && building->Can_Capture()) { 1008 Assign_Destination(target); 1009 } 1010 } 1011 } 1012 1013 1014 /*********************************************************************************************** 1015 * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * 1016 * * 1017 * This routine is used to handle the non-graphic AI processing the infantry requires. * 1018 * Call this routine ONCE per game frame. * 1019 * * 1020 * INPUT: none * 1021 * * 1022 * OUTPUT: none * 1023 * * 1024 * WARNINGS: none * 1025 * * 1026 * HISTORY: * 1027 * 09/08/1994 JLB : Created. * 1028 *=============================================================================================*/ 1029 void InfantryClass::AI(void) 1030 { 1031 Validate(); 1032 FootClass::AI(); 1033 1034 if (IsUnloading) Mark(MARK_CHANGE); 1035 1036 /* 1037 ** Special hack to make sure that if this infantry is in firing animation, but the 1038 ** stage class isn't set, then abort the firing flag. 1039 */ 1040 if (IsFiring && !Fetch_Rate()) { 1041 IsFiring = false; 1042 } 1043 1044 /* 1045 ** Delete this unit if it finds itself off the edge of the map and it is in 1046 ** guard or other static mission mode. 1047 */ 1048 if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { 1049 Stun(); 1050 Delete_This(); 1051 return; 1052 } 1053 1054 /* 1055 ** Act on new orders if the unit is at a good position to do so. 1056 */ 1057 if (!IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { 1058 Commence(); 1059 } 1060 1061 /* 1062 ** After a time, the infantry will gain courage. 1063 */ 1064 if (Fear) { 1065 1066 /* 1067 ** Nikumba is really a coward at heart. He never becomes un-afraid. 1068 */ 1069 if (*this != INFANTRY_C10) { 1070 Fear--; 1071 1072 /* 1073 ** When an armed civilian becomes unafraid, he will then reload 1074 ** another clip into his pistol. 1075 */ 1076 if (Fear == 0 && Ammo == 0 && Class->Primary != WEAPON_NONE) { 1077 Ammo = Class->MaxAmmo; 1078 } 1079 } 1080 1081 /* 1082 ** Stand up if brave and lie down if afraid. 1083 */ 1084 if (IsProne) { 1085 if (Fear < FEAR_ANXIOUS) { 1086 Do_Action(DO_GET_UP); 1087 } 1088 } else { 1089 1090 /* 1091 ** Drop to the ground if anxious. Don't drop to the ground while moving 1092 ** and the special elite flag is active. 1093 */ 1094 if (Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving) || !Special.IsDefenderAdvantage)) { 1095 Do_Action(DO_LIE_DOWN); 1096 } 1097 } 1098 } 1099 1100 /* 1101 ** When is darkness or in doubt, 1102 ** run in circles, scream, and shout. 1103 */ 1104 if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsDriving && !Target_Legal(NavCom)) { 1105 Scatter(0); 1106 } 1107 1108 /* 1109 ** Scatter infantry off buildings in guard modes. 1110 */ 1111 if (!IsTethered && !IsFiring && !IsDriving && !IsRotating && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord_Cell(Coord)].Cell_Building() != NULL) { 1112 Scatter(0, true, true); 1113 } 1114 1115 /* 1116 ** Special victory dance action. 1117 */ 1118 if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment.Expired()) { 1119 IsStoked = false; 1120 Do_Action((Random_Pick(0, 1) == 0) ? DO_GESTURE1 : DO_GESTURE2); 1121 if (*this == INFANTRY_RAMBO) { 1122 VocType _response[] = { 1123 VOC_RAMBO_LEFTY, 1124 VOC_RAMBO_LAUGH, 1125 VOC_RAMBO_COMIN, 1126 VOC_RAMBO_TUFF 1127 }; 1128 Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); 1129 } 1130 } 1131 1132 /* 1133 ** Determine if this infantry unit should fire off an 1134 ** attack or not. 1135 */ 1136 switch (Can_Fire(TarCom, 0)) { 1137 case FIRE_CLOAKED: 1138 Do_Uncloak(); 1139 break; 1140 1141 case FIRE_OK: 1142 #ifdef BOXING 1143 ObjectClass *object = As_Object(TarCom); 1144 1145 if (object) { 1146 /* If we're engaged in hand-to-hand combat, keep boxing */ 1147 if (IsBoxing) { 1148 IsFiring = true; 1149 if (((InfantryClass *)object)->Doing == DO_FIGHT_READY) { 1150 Do_Action((DoType) ((int)DO_PUNCH + (DoType)(Random_Pick(0, 1) == 1)),true); 1151 } 1152 } else { 1153 1154 if (Is_Target_Infantry(TarCom) && (Distance(TarCom)<=0x80) && (Coord_Y(Coord) == Coord_Y(object->Coord))) { 1155 1156 // Too close to shoot, so start hand-to-hand combat 1157 if (Establish_Contact((TechnoClass *)object)) { 1158 if (Transmit_Message(RADIO_PREPARE_TO_BOX) == RADIO_ROGER) { 1159 IsBoxing = true; 1160 Do_Action(DO_ON_GUARD,true); 1161 } 1162 } 1163 } else { 1164 #endif 1165 1166 /* 1167 ** Start firing animation. 1168 */ 1169 if (IsProne) { 1170 Do_Action(DO_FIRE_PRONE); 1171 IsFiring = true; 1172 } else { 1173 Do_Action(DO_FIRE_WEAPON); 1174 IsFiring = true; 1175 } 1176 #ifdef BOXING 1177 } 1178 #endif 1179 1180 PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); 1181 1182 /* 1183 ** If the target is in range, and the NavCom is the same, then just 1184 ** stop and keep firing. 1185 */ 1186 if (TarCom == NavCom) { 1187 NavCom = TARGET_NONE; 1188 Path[0] = FACING_NONE; 1189 } 1190 #ifdef BOXING 1191 } 1192 } 1193 #endif 1194 break; 1195 } 1196 1197 /* 1198 ** If in the middle of firing animation, then only 1199 ** process that. Infantry cannot fire and move simultaneously. 1200 ** At some point in the firing animation process, a projectile 1201 ** will be launched. When the required animation frames have 1202 ** been completed, the firing animation stops. 1203 */ 1204 int firestage = Class->FireLaunch; 1205 if (IsProne) firestage = Class->ProneLaunch; 1206 1207 #ifdef BOXING 1208 if (IsBoxing) { 1209 firestage = 1; 1210 if (Doing == DO_KICK) firestage = 2; 1211 } 1212 #endif 1213 1214 if (IsFiring && Fetch_Stage() == firestage) { 1215 Fire_At(TarCom, 0); 1216 1217 if (Class->Primary == WEAPON_GRENADE) { 1218 Map[::As_Cell(TarCom)].Incoming(Coord, true); 1219 } 1220 } 1221 1222 /* 1223 ** Handle the completion of the animation sequence. 1224 */ 1225 1226 if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { 1227 switch (Doing) { 1228 default: 1229 if (IsDriving) { 1230 if (IsProne) { 1231 Do_Action(DO_CRAWL, true); 1232 } else { 1233 Do_Action(DO_WALK, true); 1234 } 1235 } else { 1236 if (IsProne) { 1237 Do_Action(DO_PRONE, true); 1238 } else { 1239 Do_Action(DO_STAND_READY, true); 1240 } 1241 } 1242 break; 1243 1244 #ifdef BOXING 1245 case DO_FIGHT_READY: 1246 case DO_ON_GUARD: 1247 case DO_PUNCH: 1248 case DO_KICK: 1249 case DO_PUNCH_HIT1: 1250 case DO_PUNCH_HIT2: 1251 case DO_KICK_HIT1: 1252 case DO_KICK_HIT2: 1253 if (In_Radio_Contact()) { 1254 Do_Action(DO_FIGHT_READY, true); 1255 } else { 1256 IsBoxing = false; 1257 Do_Action(DO_READY_WEAPON); 1258 } 1259 break; 1260 #endif 1261 1262 /* 1263 ** When death is due to hand-to-hand combat, use the gunfire death 1264 ** decay animation since there is no custom animation available - yet. 1265 */ 1266 case DO_PUNCH_DEATH: 1267 case DO_KICK_DEATH: 1268 // Fall into next case. 1269 1270 case DO_GUN_DEATH: 1271 case DO_EXPLOSION_DEATH: 1272 case DO_EXPLOSION2_DEATH: 1273 case DO_GRENADE_DEATH: 1274 case DO_FIRE_DEATH: 1275 Delete_This(); 1276 return; 1277 } 1278 } 1279 1280 /* 1281 ** Perform movement operations at this time. 1282 */ 1283 if (!IsFiring /*&& !IsBoxing*/) { 1284 if (!IsDriving) { 1285 1286 /* 1287 ** When in guard mode, never allow a valid navcom. 1288 */ 1289 if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { 1290 Assign_Destination(TARGET_NONE); 1291 // if (IsTethered) Scatter(0, true); 1292 } 1293 1294 /* 1295 ** A head to coordinate is needed. If there is no path 1296 ** available, then create one. 1297 */ 1298 if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { 1299 1300 /* 1301 ** Determine if the next cell in the list is available 1302 ** to be entered. If not, then abort the path and try 1303 ** again. 1304 */ 1305 if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { 1306 Path[0] = FACING_NONE; 1307 } 1308 1309 /* 1310 ** Check to see if the target is closer than expected. This occurs 1311 ** when heading toward a moving object and that object is heading 1312 ** toward the unit. Shorten the precalculated path to be no longer 1313 ** than the distance to the target. 1314 */ 1315 int d = Lepton_To_Cell(Distance(NavCom)); 1316 if (d < CONQUER_PATH_MAX) { 1317 Path[d] = FACING_NONE; 1318 } 1319 1320 /* 1321 ** Find a path to follow if one isn't already calculated. 1322 */ 1323 if (Path[0] == FACING_NONE) { 1324 1325 /* 1326 ** Calculate the path from the current location to the 1327 ** destination indicated by the navigation computer. If there 1328 ** was a fundamental error with finding a path, then this 1329 ** indicates that basic path & movement logic needs to be 1330 ** aborted. 1331 */ 1332 if (!PathDelay.Expired()) { 1333 return; 1334 } 1335 if (!Basic_Path()) { 1336 //Mono_Printf("Infantry Basic_Path is failing.\n");Get_Key(); 1337 if (Distance(NavCom) < 0x0280 && !IsTethered) { 1338 Assign_Destination(TARGET_NONE); 1339 } else { 1340 if (TryTryAgain) { 1341 TryTryAgain--; 1342 } else { 1343 if (IsNewNavCom) Sound_Effect(VOC_SCOLD); 1344 IsNewNavCom = false; 1345 1346 //If we're trying to enter a transport we need to fail so others can try to enter. - LLL 4/17/2020 1347 if (Mission == MISSION_ENTER) { 1348 Mission = MISSION_NONE; 1349 Assign_Mission(MISSION_GUARD); 1350 Commence(); 1351 1352 Transmit_Message(RADIO_OVER_OUT); 1353 } 1354 } 1355 } 1356 Stop_Driver(); 1357 return; 1358 } 1359 TryTryAgain = PATH_RETRY; 1360 } 1361 1362 /* 1363 ** Determine the coordinate to head to based on the infantry's 1364 ** current location and the next location in the path. 1365 */ 1366 COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); 1367 CELL acell = Coord_Cell(acoord); 1368 1369 if (Can_Enter_Cell(acell) != MOVE_OK) { 1370 1371 if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered && House->IsHuman && Distance(NavCom) < 0x0200) { 1372 Assign_Destination(TARGET_NONE); 1373 } 1374 1375 /* 1376 ** If blocked by a moving block then just exit start of move and 1377 ** try again next tick. 1378 */ 1379 if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { 1380 if (Map[acell].Cell_Object()) { 1381 if (!House->Is_Ally(Map[acell].Cell_Object())) { 1382 Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); 1383 } 1384 } else { 1385 if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { 1386 Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); 1387 } 1388 } 1389 } 1390 1391 Path[0] = FACING_NONE; 1392 Stop_Driver(); 1393 if (IsNewNavCom) Sound_Effect(VOC_SCOLD); 1394 IsNewNavCom = false; 1395 1396 } else { 1397 if (Start_Driver(acoord)) { 1398 PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); 1399 Set_Speed(0xFF); 1400 if (IsProne) { 1401 Do_Action(DO_CRAWL); 1402 } else { 1403 Do_Action(DO_WALK); 1404 } 1405 } 1406 } 1407 } 1408 1409 } else { 1410 1411 /* 1412 ** The infantry knows where it should be headed, so head there. Check 1413 ** to see if the infantry is "close enough" to the desired location that 1414 ** it should just consider itself to have arrived. In this case, force 1415 ** the infantry to the destination location and mark this path step 1416 ** as complete. 1417 */ 1418 Mark(MARK_UP); 1419 if (Distance(Head_To_Coord()) < 0x0010) { 1420 1421 memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); 1422 Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; 1423 Coord = Head_To_Coord(); 1424 Stop_Driver(); 1425 Per_Cell_Process(true); 1426 1427 if (!IsActive || IsInLimbo) return; 1428 1429 if (Coord_Cell(Coord) == As_Cell(NavCom)) { 1430 NavCom = TARGET_NONE; 1431 if (Mission == MISSION_MOVE) { 1432 Enter_Idle_Mode(); 1433 } 1434 //Stop_Driver(); 1435 Path[0] = FACING_NONE; 1436 } 1437 } else { 1438 int movespeed = Speed; 1439 1440 /* 1441 ** When prone, the infantry moves at half speed or double 1442 ** speed. This depends on whether the infantry actually has 1443 ** prone animation stages. Civilians don't, and so they 1444 ** run instead. 1445 */ 1446 if (IsProne) { 1447 if (Class->IsFraidyCat && !Class->IsCrawling) { 1448 movespeed = Speed*2; 1449 } else { 1450 movespeed = Speed/2; 1451 } 1452 } 1453 1454 if (IsTethered) { 1455 Transmit_Message(RADIO_REDRAW); 1456 } 1457 1458 /* 1459 ** Advance the infantry as far as it should go. 1460 */ 1461 MPHType maxspeed = MPHType(min((unsigned)(Class->MaxSpeed * House->GroundspeedBias), MPH_LIGHT_SPEED)); 1462 Coord = Coord_Move(Coord, Direction(Head_To_Coord()), Fixed_To_Cardinal(maxspeed, movespeed)); 1463 } 1464 Mark(MARK_DOWN); 1465 } 1466 IsNewNavCom = false; 1467 } 1468 } 1469 1470 1471 #ifdef NEVER 1472 /*************************************************************************** 1473 * InfantryClass::Blocking_Object -- Determines how a object blocks an inf * 1474 * * 1475 * INPUT: TechnoClass * pointer to object that is blocking unit * 1476 * CELL the cell the unit is being blocked in * 1477 * * 1478 * OUTPUT: MoveBitType the way that the object is blocking the unit * 1479 * * 1480 * HISTORY: * 1481 * 06/08/1995 PWG : Created. * 1482 *=========================================================================*/ 1483 MoveBitType InfantryClass::Blocking_Object(TechnoClass const *techno, CELL cell) const 1484 { 1485 Validate(); 1486 bool inf = (techno->What_Am_I() == RTTI_INFANTRY); 1487 bool unit = (techno->What_Am_I() == RTTI_UNIT) || inf; 1488 1489 CellClass const * cellptr = &Map[cell]; 1490 1491 if (House->Is_Ally(techno)) { 1492 1493 /* 1494 ** Logic to handle a trasport type object. 1495 */ 1496 if (NavCom == techno->As_Target() && Contact_With_Whom() == techno) { 1497 return(MOVEF_OK); 1498 } 1499 1500 /* 1501 ** If the object is of type infantry, then the square is blocked only 1502 ** if the cell is completely full of infantry. 1503 */ 1504 if (inf && ((cellptr->Flag.Composite & 0x1f) != 0x1f)) { 1505 return(MOVEF_OK); 1506 } 1507 1508 if (unit) { 1509 1510 /* 1511 ** If the unit in question has a destination than we should 1512 ** be prepared to wait for the unit to get out of our way. 1513 */ 1514 if (((FootClass *)techno)->NavCom != TARGET_NONE) { 1515 return(MOVEF_MOVING_BLOCK); 1516 } 1517 return(MOVEF_TEMP); 1518 } 1519 } else { 1520 1521 /* 1522 ** If its an enemy unit, things are dealt with a little differently 1523 */ 1524 if (unit) { 1525 1526 1527 /* 1528 ** If the object is cloaked, then consider it passable for findpath purposes, 1529 ** but not so for all other cases. 1530 */ 1531 if (techno->Cloak == CLOAKED) { 1532 if (IsFindPath) return(MOVEF_OK); 1533 return(MOVEF_CLOAK); 1534 } 1535 1536 /* 1537 ** If our vehicle is weapon equipped, then report that the cell occupier 1538 ** needs only to be destroyed in order to make the cell passable. 1539 */ 1540 if (Class->Primary != WEAPON_NONE) { 1541 return(MOVEF_DESTROYABLE); 1542 } 1543 } 1544 } 1545 return(MOVEF_NO); 1546 } 1547 #endif 1548 1549 1550 1551 /*********************************************************************************************** 1552 * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * 1553 * * 1554 * This routine is used to examine the cell specified and determine if the infantry is * 1555 * allowed to enter it. It is used by the path finding algorithm. * 1556 * * 1557 * INPUT: cell -- The cell to examine. * 1558 * * 1559 * OUTPUT: Returns the type of blockage in the cell. * 1560 * * 1561 * WARNINGS: none * 1562 * * 1563 * HISTORY: * 1564 * 09/01/1994 JLB : Created. * 1565 *=============================================================================================*/ 1566 MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const 1567 { 1568 Validate(); 1569 /* 1570 ** If we are moving into an illegal cell, then we can't do that. 1571 */ 1572 if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); 1573 1574 /* 1575 ** If moving off the edge of the map, then consider that an illegal move. 1576 */ 1577 if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) { 1578 return(MOVE_NO); 1579 } 1580 CellClass * cellptr = &Map[cell]; 1581 1582 /* 1583 ** Walls are considered impassable for infantry UNLESS the wall has a hole 1584 ** in it. 1585 */ 1586 if (cellptr->Overlay != OVERLAY_NONE) { 1587 OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); 1588 1589 if (otype.IsCrate && !House->IsHuman) { 1590 return(MOVE_NO); 1591 } 1592 1593 if (otype.IsWall) { 1594 if ((cellptr->OverlayData / 16) != otype.DamageLevels) { 1595 return(MOVE_NO); 1596 } 1597 } 1598 } 1599 1600 /* 1601 ** Loop through all of the objects in the square setting a bit 1602 ** for how they affect movement. 1603 */ 1604 MoveType retval = MOVE_OK; 1605 ObjectClass *obj = cellptr->Cell_Occupier(); 1606 while (obj) { 1607 1608 if (obj != this) { 1609 1610 /* 1611 ** Special case check so that a landed aircraft that is in radio contact, will not block 1612 ** a capture attempt. It is presumed that this case happens when a helicopter is landed 1613 ** at a helipad. 1614 */ 1615 if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { 1616 1617 /* 1618 ** Special check to always allow entry into the building that this infantry 1619 ** is trying to capture. 1620 */ 1621 if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT) { 1622 if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { 1623 return(MOVE_OK); 1624 } 1625 } 1626 1627 /* 1628 ** Special check to always allow entry into the building that this infantry 1629 ** is trying to capture. 1630 */ 1631 if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { 1632 return(MOVE_OK); 1633 } 1634 1635 /* 1636 ** Allied objects block movement using different rules than for enemy 1637 ** objects. 1638 */ 1639 if (House->Is_Ally(obj)) { 1640 switch (obj->What_Am_I()) { 1641 1642 /* 1643 ** A unit blocks as either a moving blockage or a stationary temp blockage. 1644 ** This depends on whether the unit is currently moving or not. 1645 */ 1646 case RTTI_UNIT: 1647 if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { 1648 if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; 1649 } else { 1650 if (retval < MOVE_TEMP) retval = MOVE_TEMP; 1651 } 1652 break; 1653 1654 /* 1655 ** Aircraft and buildings always block movement. If for some reason there is an 1656 ** allied terrain object, that blocks movement as well. 1657 */ 1658 case RTTI_TERRAIN: 1659 case RTTI_AIRCRAFT: 1660 case RTTI_BUILDING: 1661 return(MOVE_NO); 1662 1663 default: 1664 break; 1665 } 1666 1667 } else { 1668 1669 /* 1670 ** Cloaked enemy objects are not considered if this is a Find_Path() 1671 ** call. 1672 */ 1673 if (!obj->Is_Techno() || !((TechnoClass *)obj)->Is_Cloaked(this)) { 1674 1675 /* 1676 ** Any non-allied blockage is considered impassible if the infantry 1677 ** is not equipped with a weapon. 1678 */ 1679 if (Class->Primary == WEAPON_NONE) return(MOVE_NO); 1680 1681 /* 1682 ** Some kinds of terrain are considered destroyable if the infantry is equipped 1683 ** with the weapon that can destroy it. Otherwise, the terrain is considered 1684 ** impassable. 1685 */ 1686 switch (obj->What_Am_I()) { 1687 case RTTI_TERRAIN: 1688 if (((TerrainClass *)obj)->Class->IsFlammable && 1689 BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) { 1690 1691 if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; 1692 } else { 1693 return(MOVE_NO); 1694 } 1695 break; 1696 1697 default: 1698 if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; 1699 break; 1700 } 1701 } else { 1702 if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; 1703 } 1704 } 1705 } 1706 } 1707 1708 /* 1709 ** Move to next object in chain. 1710 */ 1711 obj = obj->Next; 1712 } 1713 1714 /* 1715 ** If foot soldiers cannot travel on the cell -- consider it impassible. 1716 */ 1717 if (retval == MOVE_OK && !IsTethered && !Ground[cellptr->Land_Type()].Cost[SPEED_FOOT]) { 1718 return(MOVE_NO); 1719 } 1720 1721 /* 1722 ** if a unit has the cell reserved then we just can't go in there. 1723 */ 1724 if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { 1725 return(MOVE_NO); 1726 } 1727 1728 /* 1729 ** if a block of infantry has the cell reserved then there are two 1730 ** possibilities... 1731 */ 1732 if (cellptr->InfType != HOUSE_NONE) { 1733 if (House->Is_Ally(cellptr->InfType)) { 1734 if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { 1735 if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; 1736 } 1737 } else { 1738 if (Class->Primary != WEAPON_NONE) { 1739 if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; 1740 } else { 1741 return(MOVE_NO); 1742 } 1743 } 1744 } 1745 1746 /* 1747 ** If it is still ok to move the infantry, then perform the last check 1748 ** to see if the cell is already full of infantry. 1749 */ 1750 if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { 1751 return(MOVE_NO); 1752 } 1753 1754 /* 1755 ** Return with the most severe reason why this cell would be impassable. 1756 */ 1757 return(retval); 1758 } 1759 1760 1761 /*********************************************************************************************** 1762 * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* 1763 * * 1764 * This is a rendering support routine that will return a pointer to a list of cell offsets * 1765 * that specify the cells the infantry unit is currently overlapping (graphic wise) but * 1766 * is not considered to occupy. This list is used to update the map display. * 1767 * * 1768 * INPUT: none * 1769 * * 1770 * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * 1771 * occupy. * 1772 * * 1773 * WARNINGS: none * 1774 * * 1775 * HISTORY: * 1776 * 09/01/1994 JLB : Created. * 1777 *=============================================================================================*/ 1778 short const * InfantryClass::Overlap_List(void) const 1779 { 1780 Validate(); 1781 //return(Coord_Spillage_List(Coord, 24 + ((IsSelected || Doing > DO_WALK)?12:0))); 1782 return(Coord_Spillage_List(Coord, 24 + ((Doing > DO_WALK || Is_Selected_By_Player())?12:0))); 1783 // return(Coord_Spillage_List(Coord, (IsSelected ? 24 : 14))+1); 1784 } 1785 1786 1787 /*********************************************************************************************** 1788 * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * 1789 * * 1790 * Determines if the infantry unit can fire on the target. If it can't fire, then the * 1791 * reason why is returned. * 1792 * * 1793 * INPUT: target -- The target to determine if the infantry can fire upon. * 1794 * * 1795 * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * 1796 * can't, why not. * 1797 * * 1798 * WARNINGS: none * 1799 * * 1800 * HISTORY: * 1801 * 09/01/1994 JLB : Created. * 1802 * 06/27/1995 JLB : Flame thrower can fire while prone now. * 1803 *=============================================================================================*/ 1804 FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const 1805 { 1806 Validate(); 1807 WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; 1808 1809 #ifdef BOXING 1810 /* 1811 ** If in hand-to-hand, and we're currently playing a got-hit animation, 1812 ** then we can't punch back yet. 1813 */ 1814 if (IsBoxing) { 1815 if ( (Doing>=DO_PUNCH_HIT1 && Doing<=DO_KICK_DEATH) || (Doing == DO_ON_GUARD) ) return FIRE_BUSY; 1816 if (Arm) return(FIRE_BUSY); // don't let fire if still re-arming 1817 } 1818 #endif 1819 1820 /* 1821 ** Don't allow firing if the turret is not ready. 1822 */ 1823 if (IsFiring) return(FIRE_REARM); 1824 1825 #ifdef OBSOLETE 1826 if (weapon->Fires == BULLET_FLAME && IsProne) return(FIRE_ILLEGAL); 1827 #endif 1828 1829 /* 1830 ** The target must still be legal. 1831 */ 1832 if (!Target_Legal(target)) { 1833 return(FIRE_ILLEGAL); 1834 } 1835 1836 /* 1837 ** If this unit cannot fire while moving, then bail. 1838 */ 1839 if ((IsDriving && Special.IsDefenderAdvantage) || (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { 1840 return(FIRE_MOVING); 1841 } 1842 1843 /* 1844 ** If we're moving, but not facing the right direction, then exit. 1845 */ 1846 if (!Special.IsDefenderAdvantage && IsDriving) { 1847 int diff = PrimaryFacing.Difference(Direction(TarCom)); 1848 if (ABS(diff) >= 32) { 1849 return(FIRE_MOVING); 1850 } 1851 } 1852 1853 return(FootClass::Can_Fire(target, which)); 1854 } 1855 1856 1857 /*********************************************************************************************** 1858 * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * 1859 * * 1860 * Use this routine when the infantry unit as accomplished its task and needs to find * 1861 * something to do. The default behavior is to enter some idle state such as guarding. * 1862 * * 1863 * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * 1864 * or is initially placed on the map? * 1865 * * 1866 * OUTPUT: none * 1867 * * 1868 * WARNINGS: none * 1869 * * 1870 * HISTORY: * 1871 * 09/01/1994 JLB : Created. * 1872 *=============================================================================================*/ 1873 void InfantryClass::Enter_Idle_Mode(bool ) 1874 { 1875 Validate(); 1876 MissionType order; 1877 1878 if (Target_Legal(TarCom)) { 1879 order = MISSION_ATTACK; 1880 } else { 1881 if (Target_Legal(NavCom)) { 1882 order = MISSION_MOVE; 1883 } else { 1884 if (GameToPlay == GAME_NORMAL || House->IsHuman) { 1885 order = MISSION_GUARD; 1886 } else { 1887 #ifndef USE_RA_AI 1888 order = MISSION_HUNT; 1889 1890 #else 1891 // 1892 // Added for RA AI in TD. ST - 7/26/2019 9:12AM 1893 // 1894 // This applies only to non-human houses in a non-normal game type 1895 // 1896 1897 order = MISSION_GUARD; 1898 1899 if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) { 1900 return; 1901 } 1902 1903 if (!Team) { 1904 if (House->IQ >= Rule.IQGuardArea) { 1905 if (Is_Weapon_Equipped()) { 1906 order = MISSION_GUARD_AREA; 1907 } 1908 } 1909 } 1910 #endif 1911 1912 } 1913 } 1914 } 1915 Assign_Mission(order); 1916 } 1917 1918 1919 /*********************************************************************************************** 1920 * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * 1921 * * 1922 * This routine is the random animator initiator for infantry units. This routine should * 1923 * be called regularly. On occasion, it will cause the infantry to go into an idle * 1924 * animation. * 1925 * * 1926 * INPUT: none * 1927 * * 1928 * OUTPUT: none * 1929 * * 1930 * WARNINGS: none * 1931 * * 1932 * HISTORY: * 1933 * 09/01/1994 JLB : Created. * 1934 * 12/13/1994 JLB : Does random facing change. * 1935 * 07/02/1995 JLB : Nikoomba special effects. * 1936 *=============================================================================================*/ 1937 void InfantryClass::Random_Animate(void) 1938 { 1939 Validate(); 1940 if (!IsDriving && !IsProne && (Doing == DO_STAND_GUARD || Doing == DO_STAND_READY) && !IsFiring) { 1941 1942 /* 1943 ** Scared infantry will always follow the golden rule of civilians; 1944 ** "When in darkness or in doubt, run in circles, scream, and shout!" 1945 */ 1946 if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { 1947 Scatter(NULL, true); 1948 return; 1949 } 1950 1951 /* 1952 ** If Nikoomba is not scared, then he will be doing his thing with random animations. 1953 */ 1954 if (*this == INFANTRY_C10) { 1955 switch (Random_Pick(0, 3)) { 1956 case 0: 1957 Do_Action(DO_IDLE2); 1958 break; 1959 1960 default: 1961 break; 1962 } 1963 } 1964 1965 switch (Random_Picky((int)0, (int)55, (char*)NULL, (int)0)) { 1966 case 10: 1967 Do_Action(DO_SALUTE1); 1968 break; 1969 1970 case 11: 1971 Do_Action(DO_SALUTE2); 1972 break; 1973 1974 case 12: 1975 Do_Action(DO_GESTURE1); 1976 break; 1977 1978 case 13: 1979 Do_Action(DO_GESTURE2); 1980 break; 1981 1982 case 4: 1983 case 3: 1984 case 0: 1985 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1986 Mark(MARK_CHANGE); 1987 break; 1988 1989 case 1: 1990 Do_Action(DO_IDLE1); 1991 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1992 if (Sim_Random_Pick(1,20) == 1 && !Is_Selected_By_Player() && *this == INFANTRY_MOEBIUS && IsDiscoveredByPlayer) { 1993 static VocType _response[] = { 1994 // VOC_EXCELLENT1, 1995 // VOC_EXCELLENT2, 1996 VOC_EXCELLENT3, 1997 // VOC_EXCELLENT4, 1998 // VOC_EXCELLENT5, 1999 VOC_QUIP1, 2000 // VOC_QUIP2 2001 }; 2002 Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); 2003 } 2004 break; 2005 2006 case 2: 2007 Do_Action(DO_IDLE2); 2008 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 2009 if (!Is_Selected_By_Player() && IsOwnedByPlayer && *this == INFANTRY_RAMBO && Sim_Random_Pick(0, 2) == 0) { 2010 Sound_Effect(VOC_RAMBO_CMON, Coord); 2011 } 2012 break; 2013 2014 /* 2015 ** On occasion, civilian types will wander about. 2016 */ 2017 case 5: 2018 case 6: 2019 case 7: 2020 case 8: 2021 case 9: 2022 if (!House->IsHuman && Class->IsFraidyCat) { 2023 Scatter(NULL, true); 2024 } 2025 break; 2026 } 2027 } 2028 } 2029 2030 2031 /*********************************************************************************************** 2032 * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * 2033 * * 2034 * This routine is used when the infantry should scatter to a nearby cell. Scattering * 2035 * occurs as an occasional consequence of being fired upon. It is one of the features * 2036 * that makes infantry so "charming". * 2037 * * 2038 * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * 2039 * scatter. If the threat isn't from a particular direction, then this * 2040 * parameter will be NULL. * 2041 * * 2042 * OUTPUT: none * 2043 * * 2044 * WARNINGS: none * 2045 * * 2046 * HISTORY: * 2047 * 09/24/1994 JLB : Created. * 2048 * 12/12/1994 JLB : Flame thrower infantry always scatter. * 2049 *=============================================================================================*/ 2050 void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) 2051 { 2052 Validate(); 2053 2054 /* 2055 ** A unit that is in the process of going somewhere will never scatter. 2056 */ 2057 if (IsDriving || Target_Legal(NavCom)) forced = false; 2058 2059 /* 2060 ** If the infantry is currently engaged in legitimate combat, then don't 2061 ** scatter unless forced to. 2062 */ 2063 if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; 2064 2065 /* 2066 ** Don't scatter if performing an action that can't be interrupted. 2067 */ 2068 if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; 2069 2070 /* 2071 ** For human players, don't scatter the infantry, if the special 2072 ** flag has not been enabled that allows infantry scatter. 2073 */ 2074 if (!Special.IsScatter && !nokidding && House->IsHuman && !forced && !Team) return; 2075 2076 if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { 2077 FacingType toface; 2078 FacingType newface; 2079 CELL newcell; 2080 2081 if (threat) { 2082 toface = Dir_Facing(Direction8(threat, Coord)); 2083 toface = toface + (Random_Pick(0, 4)-2); 2084 } else { 2085 COORDINATE coord = Coord & 0x00FF00FFL; 2086 2087 if (coord != 0x00800080L) { 2088 toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); 2089 } else { 2090 toface = Dir_Facing(PrimaryFacing.Current()); 2091 } 2092 toface = toface + (Random_Pick(0, 4)-2); 2093 } 2094 2095 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 2096 newface = toface + face; 2097 newcell = Adjacent_Cell(Coord_Cell(Coord), newface); 2098 2099 if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { 2100 if (!Class->IsAvoidingTiberium || 2101 Map[newcell].Overlay == OVERLAY_NONE || 2102 OverlayTypeClass::As_Reference(Map[newcell].Overlay).Land != LAND_TIBERIUM) { 2103 Assign_Mission(MISSION_MOVE); 2104 Assign_Destination(::As_Target(newcell)); 2105 } 2106 } 2107 } 2108 } 2109 } 2110 2111 2112 /*********************************************************************************************** 2113 * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * 2114 * * 2115 * This starts the infantry into a choreographed animation sequence. These sequences can * 2116 * be as simple as standing up or lying down, but can also be complex, such as dying or * 2117 * performing some idle animation. * 2118 * * 2119 * INPUT: todo -- The choreographed sequence to start. * 2120 * * 2121 * force -- Force starting this animation even if the current animation is flagged * 2122 * as uninterruptible. This is necessary for death animations. * 2123 * * 2124 * OUTPUT: bool; Was the animation started? * 2125 * * 2126 * WARNINGS: none * 2127 * * 2128 * HISTORY: * 2129 * 09/24/1994 JLB : Created. * 2130 *=============================================================================================*/ 2131 bool InfantryClass::Do_Action(DoType todo, bool force) 2132 { 2133 Validate(); 2134 if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { 2135 Mark(MARK_CHANGE); 2136 //Mark(MARK_OVERLAP_UP); 2137 Doing = todo; 2138 //Mark(MARK_OVERLAP_DOWN); 2139 if (todo == DO_IDLE1 || todo == DO_IDLE2) { 2140 Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); 2141 } else { 2142 Set_Rate(MasterDoControls[Doing].Rate); 2143 } 2144 Set_Stage(0); 2145 2146 /* 2147 ** Kludge to make sure that if infantry is in the dying animation, it isn't still 2148 ** moving as well. 2149 */ 2150 if (!Strength) { 2151 Stop_Driver(); 2152 } 2153 2154 /* 2155 ** Since the animation sequence might be interrupted. Set any flags 2156 ** necessary so that if interrupted, the affect on the infantry is 2157 ** still accomplished. 2158 */ 2159 switch (todo) { 2160 case DO_LIE_DOWN: 2161 IsProne = true; 2162 break; 2163 2164 case DO_GET_UP: 2165 IsProne = false; 2166 break; 2167 2168 case DO_READY_WEAPON: 2169 IsBoxing = false; 2170 break; 2171 2172 case DO_ON_GUARD: 2173 IsBoxing = true; 2174 PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); 2175 Path[0] = FACING_NONE; 2176 break; 2177 2178 default: 2179 break; 2180 } 2181 2182 return(true); 2183 } 2184 2185 return(false); 2186 } 2187 2188 2189 /*********************************************************************************************** 2190 * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * 2191 * * 2192 * This is used to stop the infantry from animating in movement. This function will stop * 2193 * the infantry moving and revert it to either a prone or standing. * 2194 * * 2195 * INPUT: none * 2196 * * 2197 * OUTPUT: bool; Was the driving stopped? * 2198 * * 2199 * WARNINGS: none * 2200 * * 2201 * HISTORY: * 2202 * 09/24/1994 JLB : Created. * 2203 *=============================================================================================*/ 2204 bool InfantryClass::Stop_Driver(void) 2205 { 2206 Validate(); 2207 if (Head_To_Coord()) { 2208 2209 /* 2210 ** Remove the "reservation" bit in the destination location. 2211 */ 2212 Clear_Occupy_Bit(Head_To_Coord()); 2213 } 2214 2215 /* 2216 ** Set the occupation bit at the current location. 2217 */ 2218 Set_Occupy_Bit(Coord); 2219 2220 if (Doing != DO_STAND_READY) { 2221 StopDriverFrame = Frame; 2222 } 2223 2224 if (IsProne) { 2225 Do_Action(DO_PRONE); 2226 } else { 2227 Do_Action(DO_STAND_READY); 2228 } 2229 2230 return(FootClass::Stop_Driver()); 2231 } 2232 2233 2234 /*********************************************************************************************** 2235 * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * 2236 * * 2237 * Use this routine to being the infantry moving toward the destination specified. The * 2238 * destination is first checked to see if there is a free spot available. Then the infantry * 2239 * reserves that spot and begins movement toward it. * 2240 * * 2241 * INPUT: headto -- The coordinate location desired for the infantry to head to. * 2242 * * 2243 * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * 2244 * the specified destination could not contain the infantry unit. * 2245 * * 2246 * WARNINGS: none * 2247 * * 2248 * HISTORY: * 2249 * 12/21/1994 JLB : Created. * 2250 * 05/14/1995 JLB : Tries to move to closest spot possible. * 2251 * 05/15/1995 JLB : Uses closest spot if moving onto transport. * 2252 *=============================================================================================*/ 2253 bool InfantryClass::Start_Driver(COORDINATE & headto) 2254 { 2255 Validate(); 2256 COORDINATE old = headto; 2257 2258 /* 2259 ** Convert the head to coordinate to a legal sub-position location. 2260 */ 2261 headto = Map[Coord_Cell(headto)].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); 2262 if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { 2263 headto = Map[Coord_Cell(old)].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); 2264 } 2265 2266 /* 2267 ** If the infantry started moving, then fixup the occupation bits. 2268 */ 2269 if (headto && FootClass::Start_Driver(headto)) { 2270 /* 2271 ** Remove the occupation bit from the infantry's current location. 2272 */ 2273 Clear_Occupy_Bit(Coord); 2274 2275 /* 2276 ** Set the occupation bit for the new headto location. 2277 */ 2278 Set_Occupy_Bit(headto); 2279 return(true); 2280 } 2281 2282 return(false); 2283 } 2284 2285 2286 #ifdef NEVER 2287 /*********************************************************************************************** 2288 * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantly * 2289 * * 2290 * This routine ensures that when the infantry primary facing is changes, it is changed * 2291 * instantly and always. There is no provision for infantry facing changing slowly over * 2292 * time as the other vehicles usually do. * 2293 * * 2294 * INPUT: facing -- The desired facing for the infantry unit. * 2295 * * 2296 * OUTPUT: none * 2297 * * 2298 * WARNINGS: none * 2299 * * 2300 * HISTORY: * 2301 * 12/11/1994 JLB : Created. * 2302 *=============================================================================================*/ 2303 void InfantryClass::Set_Primary_Facing(DirType facing, bool ) 2304 { 2305 Validate(); 2306 FootClass::Set_Primary_Facing(facing, true); 2307 } 2308 #endif 2309 2310 2311 /*********************************************************************************************** 2312 * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * 2313 * * 2314 * This routine will clean up the infantry occupation bits (as necessary) as well as stop * 2315 * the infantry movement process when it gets limboed. * 2316 * * 2317 * INPUT: none * 2318 * * 2319 * OUTPUT: bool; Was the infantry unit limboed? * 2320 * * 2321 * WARNINGS: none * 2322 * * 2323 * HISTORY: * 2324 * 12/22/1994 JLB : Created. * 2325 *=============================================================================================*/ 2326 bool InfantryClass::Limbo(void) 2327 { 2328 Validate(); 2329 if (!IsInLimbo) { 2330 Stop_Driver(); 2331 2332 Clear_Occupy_Bit(Coord); 2333 } 2334 return(FootClass::Limbo()); 2335 } 2336 2337 2338 /*********************************************************************************************** 2339 * InfantryClass::Fire_At -- Fires projectile from infantry unit. * 2340 * * 2341 * Use this routine when the infantry unit wishes to fire a projectile. This routine * 2342 * will launch the projectile and perform any other necessary infantry specific operations. * 2343 * * 2344 * INPUT: target -- The target of the attack. * 2345 * * 2346 * which -- Which weapon to use for firing. 0=primary, 1=secondary. * 2347 * * 2348 * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * 2349 * NULL is returned. If there is already the maximum bullet objects in play, then * 2350 * this could happen. * 2351 * * 2352 * WARNINGS: none * 2353 * * 2354 * HISTORY: * 2355 * 12/26/1994 JLB : Created. * 2356 *=============================================================================================*/ 2357 BulletClass * InfantryClass::Fire_At(TARGET target, int which) 2358 { 2359 Validate(); 2360 BulletClass * bullet = NULL; 2361 WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; 2362 2363 IsFiring = false; 2364 2365 #ifdef BOXING 2366 if (IsBoxing) { 2367 RadioMessageType hitaction = (Doing == DO_KICK) ? RADIO_KICK : RADIO_PUNCH; 2368 2369 /* 2370 ** When fighting, verify that the target is legal to proceed. If there is some 2371 ** error, then abort fightning mode. Otherwise, tell the target that it has 2372 ** just been hit. 2373 */ 2374 if (In_Radio_Contact() && Target_Legal(target) && Transmit_Message(hitaction) == RADIO_ROGER) { 2375 Arm = Rearm_Delay(true); 2376 } else { 2377 2378 /* 2379 ** Fighting done for some reason, so pick up gun 2380 */ 2381 IsBoxing = false; 2382 Do_Action(DO_READY_WEAPON,true); 2383 } 2384 } else { 2385 #endif 2386 2387 bullet = FootClass::Fire_At(target, which); 2388 if (bullet) { 2389 2390 /* 2391 ** For fraidycat infantry that run out of ammo, always go into 2392 ** a maximum fear state at that time. 2393 */ 2394 if (Class->IsFraidyCat && !Ammo) { 2395 Fear = FEAR_MAXIMUM; 2396 if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { 2397 Assign_Mission(MISSION_GUARD); 2398 } 2399 } 2400 2401 /* 2402 ** Create the projectile. Then process any special operations that 2403 ** need to be performed according to the style of projectile 2404 ** created. 2405 */ 2406 Sound_Effect(weapon->Sound, Coord); 2407 } 2408 2409 #ifdef BOXING 2410 } 2411 #endif 2412 return(bullet); 2413 } 2414 2415 2416 /*********************************************************************************************** 2417 * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * 2418 * * 2419 * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * 2420 * ensure that the coordinate is a legal subposition. * 2421 * * 2422 * INPUT: coord -- The coordinate to unimbo the infantry at. * 2423 * * 2424 * facing -- The desired initial facing for the infantry unit. * 2425 * * 2426 * strength -- The desired initial strength for the infantry unit. * 2427 * * 2428 * mission -- The desired initial mission for the infantry unit. * 2429 * * 2430 * OUTPUT: bool; Was the infantry unlimboed successfully? * 2431 * * 2432 * WARNINGS: none * 2433 * * 2434 * HISTORY: * 2435 * 12/26/1994 JLB : Created. * 2436 *=============================================================================================*/ 2437 bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) 2438 { 2439 Validate(); 2440 /* 2441 ** Make sure that the infantry start in a legal position on the map. 2442 */ 2443 coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, ScenarioInit); 2444 if (coord == NULL) { 2445 return(false); 2446 } 2447 2448 if (FootClass::Unlimbo(coord, facing)) { 2449 2450 /* 2451 ** Ensure that the owning house knows about the 2452 ** new object. 2453 */ 2454 House->IScan |= (1L << Class->Type); 2455 House->ActiveIScan |= (1L << Class->Type); 2456 2457 /* 2458 ** If there is no sight range, then this object isn't discovered by the player unless 2459 ** it actually appears in a cell mapped by the player. 2460 */ 2461 if (Class->SightRange == 0 && GameToPlay == GAME_NORMAL && !House->IsHuman && !Map[Coord_Cell(coord)].Is_Visible(PlayerPtr)) { 2462 IsDiscoveredByPlayerMask &= ~(1 << (int)PlayerPtr->Class->House); 2463 IsDiscoveredByPlayer = false; 2464 } 2465 2466 Set_Occupy_Bit(coord); 2467 return(true); 2468 } 2469 return(false); 2470 } 2471 2472 2473 /*********************************************************************************************** 2474 * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * 2475 * * 2476 * This routine intercepts the Greatest_Threat request and adds the appropriate target * 2477 * types to search for. For regular infantry, this consists of all the ground types. For * 2478 * rocket launching infantry, this also includes aircraft. * 2479 * * 2480 * INPUT: threat -- The basic threat control value. * 2481 * * 2482 * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * 2483 * target could be found, then TARGET_NONE is returned. * 2484 * * 2485 * WARNINGS: none * 2486 * * 2487 * HISTORY: * 2488 * 01/01/1995 JLB : Created. * 2489 *=============================================================================================*/ 2490 TARGET InfantryClass::Greatest_Threat(ThreatType threat) const 2491 { 2492 Validate(); 2493 /* 2494 ** Engineers consider only buildings that can be captures as being a threat. All others 2495 ** are ignored. 2496 */ 2497 if (Class->IsCapture && Class->Primary == WEAPON_NONE) { 2498 threat = threat | THREAT_CAPTURE; 2499 } 2500 2501 switch (Class->Primary) { 2502 case WEAPON_NONE: 2503 if (*this != INFANTRY_E7) { 2504 return(TARGET_NONE); 2505 } 2506 // fall into next case. 2507 2508 // 2509 // ^ 2510 // | Original comment above. 2511 // 2512 // Crash here because INFANTRY_E7 has no primary weapon and WEAPON_NONE == -1, so we will reference a -ve index into the Weapons array. 2513 // Assume original intent was to fall out of the switch and call the base class Greatest_Threat, so adding a break here. 2514 // ST - 4/24/2019 11:01AM 2515 // 2516 break; // Added. ST - 4/24/2019 11:02AM 2517 2518 /* 2519 ** Dragon missile equiped soldiers are also assumed to carry a Stinger missile. As such, 2520 ** they will consider aircraft a legal target. 2521 */ 2522 default: 2523 if (BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { 2524 threat = threat | THREAT_AIR; 2525 } 2526 break; 2527 2528 /* 2529 ** The sniper rifle equipped soldier doesn't go hunting for targets 2530 ** unless specifically in hunt mode. 2531 */ 2532 case WEAPON_RIFLE: 2533 if (House->IsHuman && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { 2534 return(TARGET_NONE); 2535 } 2536 return(TechnoClass::Greatest_Threat(threat | THREAT_INFANTRY|THREAT_BUILDINGS)); 2537 } 2538 return(FootClass::Greatest_Threat(threat)); 2539 } 2540 2541 2542 /*********************************************************************************************** 2543 * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * 2544 * * 2545 * This routine handles playing an audio response as a result of the player selecting the * 2546 * infantry unit. This occurs prior to giving it an order and may not be followed by any * 2547 * order at all. * 2548 * * 2549 * INPUT: none * 2550 * * 2551 * OUTPUT: none * 2552 * * 2553 * WARNINGS: none * 2554 * * 2555 * HISTORY: * 2556 * 01/01/1995 JLB : Created. * 2557 * 05/05/1995 JLB : Rambo response types added. * 2558 *=============================================================================================*/ 2559 void InfantryClass::Response_Select(void) 2560 { 2561 Validate(); 2562 VocType response; 2563 if (*this == INFANTRY_RAMBO) { 2564 static VocType _response[] = { 2565 VOC_RAMBO_YEA, 2566 VOC_RAMBO_YES, 2567 VOC_RAMBO_YO 2568 }; 2569 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2570 } else { 2571 if (Class->IsCivilian) { 2572 if (*this == INFANTRY_MOEBIUS) { 2573 static VocType _response[] = { 2574 VOC_YES, 2575 VOC_COMMANDER, 2576 VOC_HELLO, 2577 VOC_HMMM 2578 }; 2579 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2580 } else { 2581 if (Class->IsFemale) { 2582 response = VOC_GIRL_YEAH; 2583 } else { 2584 response = VOC_GUY_YEAH; 2585 } 2586 } 2587 } else { 2588 static VocType _response[] = { 2589 VOC_ACKNOWL, 2590 VOC_REPORT, 2591 VOC_REPORT, 2592 VOC_YESSIR, 2593 VOC_YESSIR, 2594 VOC_READY, 2595 VOC_AWAIT 2596 }; 2597 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2598 } 2599 } 2600 if (AllowVoice) { 2601 Sound_Effect(response, 0, Infantry.ID(this)+1); 2602 } 2603 } 2604 2605 2606 /*********************************************************************************************** 2607 * InfantryClass::Response_Move -- Plays infantry response to movement order. * 2608 * * 2609 * When the infantry is given the order to move, this routine handles the audio repsonse * 2610 * generated by the infantry unit. * 2611 * * 2612 * INPUT: none * 2613 * * 2614 * OUTPUT: none * 2615 * * 2616 * WARNINGS: none * 2617 * * 2618 * HISTORY: * 2619 * 01/01/1995 JLB : Created. * 2620 * 05/05/1995 JLB : Rambo response types added. * 2621 *=============================================================================================*/ 2622 void InfantryClass::Response_Move(void) 2623 { 2624 Validate(); 2625 VocType response; 2626 if (*this == INFANTRY_RAMBO) { 2627 static VocType _response[] = { 2628 VOC_RAMBO_UGOTIT, 2629 VOC_RAMBO_ONIT, 2630 VOC_RAMBO_NOPROB 2631 }; 2632 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2633 } else { 2634 if (Class->IsCivilian) { 2635 if (*this == INFANTRY_MOEBIUS) { 2636 static VocType _response[] = { 2637 VOC_OF_COURSE, 2638 VOC_YESYES 2639 }; 2640 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2641 } else { 2642 if (Class->IsFemale) { 2643 response = VOC_GIRL_OKAY; 2644 } else { 2645 response = VOC_GUY_OKAY; 2646 } 2647 } 2648 } else { 2649 static VocType _response[] = { 2650 VOC_MOVEOUT, 2651 VOC_MOVEOUT, 2652 VOC_MOVEOUT, 2653 VOC_ROGER, 2654 VOC_RIGHT_AWAY, 2655 VOC_UGOTIT, 2656 VOC_AFFIRM, 2657 VOC_AFFIRM 2658 }; 2659 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2660 } 2661 } 2662 if (AllowVoice) { 2663 Sound_Effect(response, 0, Infantry.ID(this)+1); 2664 } 2665 } 2666 2667 2668 /*********************************************************************************************** 2669 * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * 2670 * * 2671 * When the player gives an infantry unit the order to attack, this routine handles * 2672 * the audio response by that unit. * 2673 * * 2674 * INPUT: none * 2675 * * 2676 * OUTPUT: none * 2677 * * 2678 * WARNINGS: none * 2679 * * 2680 * HISTORY: * 2681 * 01/01/1995 JLB : Created. * 2682 * 05/05/1995 JLB : Rambo response types added. * 2683 *=============================================================================================*/ 2684 void InfantryClass::Response_Attack(void) 2685 { 2686 Validate(); 2687 VocType response; 2688 if (*this == INFANTRY_RAMBO) { 2689 static VocType _response[] = { 2690 VOC_RAMBO_NOPROB, 2691 VOC_RAMBO_UGOTIT, 2692 VOC_RAMBO_NOPROB, 2693 VOC_RAMBO_ONIT 2694 }; 2695 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2696 } else { 2697 if (Class->IsCivilian) { 2698 if (Class->IsFemale) { 2699 response = VOC_GIRL_OKAY; 2700 } else { 2701 response = VOC_GUY_OKAY; 2702 } 2703 } else { 2704 static VocType _response[] = { 2705 VOC_RIGHT_AWAY, 2706 VOC_AFFIRM, 2707 VOC_AFFIRM, 2708 VOC_UGOTIT, 2709 VOC_NO_PROB, 2710 VOC_YESSIR, 2711 VOC_YESSIR, 2712 VOC_YESSIR 2713 }; 2714 response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; 2715 } 2716 } 2717 2718 if (AllowVoice) { 2719 Sound_Effect(response, 0, Infantry.ID(this)+1); 2720 } 2721 } 2722 2723 2724 /*********************************************************************************************** 2725 * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * 2726 * * 2727 * This routine will return with the point of origin for any firing projectiles. Typically, * 2728 * this only includes the rocket launcher and the grenade thrower. The other infantry * 2729 * have either invisible projectiles or special animations. * 2730 * * 2731 * INPUT: none * 2732 * * 2733 * OUTPUT: Returns with the coordinate where the projectile will appear as it is fired. * 2734 * * 2735 * WARNINGS: none * 2736 * * 2737 * HISTORY: * 2738 * 01/04/1995 JLB : Created. * 2739 *=============================================================================================*/ 2740 COORDINATE InfantryClass::Fire_Coord(int) const 2741 { 2742 Validate(); 2743 if (Class->Type == INFANTRY_E4) { 2744 return(Coord); // special case for flame thrower guy 2745 } else { 2746 return(Coord_Add(Coord, XYP_COORD(0, -5))); 2747 } 2748 } 2749 2750 2751 /*************************************************************************** 2752 * InfantryClass::Receive_Message -- Process radio messages * 2753 * * 2754 * If the infantry's boxing, it needs to return to a normal state when * 2755 * his opponent moves away. Otherwise fall thru to FootClass processing* 2756 * * 2757 * INPUT: from - Pointer to the originator of this message. * 2758 * * 2759 * message - the message to process * 2760 * * 2761 * param -- Reference to an optional parameter that can be * 2762 * used to transfer more information than is * 2763 * possible with the simple radio message values. * 2764 * * 2765 * OUTPUT: an appropriate response message * 2766 * * 2767 * WARNINGS: * 2768 * * 2769 * HISTORY: * 2770 * 01/19/1995 BWG : Created. * 2771 * 05/14/1995 JLB : Handles loading maneuver messages. * 2772 *=========================================================================*/ 2773 RadioMessageType InfantryClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) 2774 { 2775 Validate(); 2776 int damage; 2777 2778 switch (message) { 2779 2780 case RADIO_OVER_OUT: 2781 #ifdef BOXING 2782 if (IsBoxing) Do_Action(DO_READY_WEAPON); 2783 #endif 2784 break; 2785 2786 /* 2787 ** Request a fisticuff fight. If this infantry is already involved in a fight, 2788 ** then refuse the offer. 2789 */ 2790 case RADIO_PREPARE_TO_BOX: 2791 #ifdef BOXING 2792 if (IsBoxing) break; 2793 #endif 2794 if (Contact_With_Whom() == from) { 2795 Do_Action(DO_ON_GUARD, true); 2796 Assign_Target(Contact_With_Whom()->As_Target()); 2797 return(RADIO_ROGER); 2798 } 2799 return(RADIO_NEGATIVE); 2800 2801 /* 2802 ** Just received a kick! Take some damage. 2803 */ 2804 case RADIO_KICK: 2805 damage = Infantry_Kick_Damage[Random_Pick(0, (int)(sizeof(Infantry_Kick_Damage) / sizeof(Infantry_Kick_Damage[0])))]; 2806 if (Take_Damage(damage, 0, WARHEAD_FOOT, this) == RESULT_DESTROYED) return(RADIO_STATIC); 2807 return(RADIO_ROGER); 2808 2809 /* 2810 ** Just recieved a punch! Take some damage. 2811 */ 2812 case RADIO_PUNCH: 2813 damage = Infantry_Punch_Damage[Random_Pick(0, (int)(sizeof(Infantry_Punch_Damage) / sizeof(Infantry_Punch_Damage[0])))]; 2814 if (Take_Damage(damage, 0, WARHEAD_FIST, this) == RESULT_DESTROYED) return(RADIO_STATIC); 2815 return(RADIO_ROGER); 2816 2817 } 2818 return(FootClass::Receive_Message(from, message, param)); 2819 } 2820 2821 2822 /*************************************************************************** 2823 * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing* 2824 * * 2825 * If the infantry's in a boxing mode, return an appropriate re-arming * 2826 * delay. Otherwise return the default return val. * 2827 * * 2828 * INPUT: second -- bool; see TechnoClass... * 2829 * * 2830 * OUTPUT: Returns with the # of game frames to delay before shooting * 2831 * * 2832 * WARNINGS: none * 2833 * * 2834 * HISTORY: * 2835 * 01/16/1995 BWG : Created. * 2836 *=========================================================================*/ 2837 int InfantryClass::Rearm_Delay(bool second) const 2838 { 2839 Validate(); 2840 #ifdef BOXING 2841 if (IsBoxing) { 2842 return(Random_Pick(5, 50)); 2843 } 2844 #endif 2845 return(FootClass::Rearm_Delay(second)); 2846 } 2847 2848 2849 /*********************************************************************************************** 2850 * InfantryClass::Assign_Mission -- Assign mission to infantry object. * 2851 * * 2852 * When a new mission is assigned, make sure he gets out of boxing mode. * 2853 * * 2854 * INPUT: order -- The new mission to assign to the unit. * 2855 * * 2856 * OUTPUT: none * 2857 * * 2858 * WARNINGS: none * 2859 * * 2860 * HISTORY: * 2861 * 12/29/1994 JLB : Created. * 2862 *=============================================================================================*/ 2863 void InfantryClass::Assign_Mission(MissionType order) 2864 { 2865 Validate(); 2866 if (order == MISSION_SABOTAGE) { 2867 Sound_Effect(VOC_RAMBO_PRESENT, Coord); 2868 } 2869 2870 IsBoxing = false; 2871 FootClass::Assign_Mission(order); 2872 } 2873 2874 2875 /*********************************************************************************************** 2876 * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * 2877 * * 2878 * This routine checks to see if the infantry unit can capture the specified object rather * 2879 * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * 2880 * * 2881 * INPUT: object -- The object that the mouse is currently over. * 2882 * * 2883 * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * 2884 * object specified. * 2885 * * 2886 * WARNINGS: none * 2887 * * 2888 * HISTORY: * 2889 * 03/01/1995 JLB : Created. * 2890 *=============================================================================================*/ 2891 ActionType InfantryClass::What_Action(ObjectClass * object) const 2892 { 2893 Validate(); 2894 ActionType action = FootClass::What_Action(object); 2895 2896 /* 2897 ** First see if it's a commando, and if he's attacking a building, have him return ACTION_SABOTAGE instead 2898 */ 2899 if (*this == INFANTRY_RAMBO && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { 2900 return(ACTION_SABOTAGE); 2901 } 2902 2903 /* 2904 ** There is no self-select action available for infantry types. 2905 */ 2906 if (action == ACTION_SELF) { 2907 action = ACTION_NONE; 2908 } 2909 2910 /* 2911 ** Check to see if it can enter a transporter. 2912 */ 2913 if ( 2914 House->Is_Ally(object) && 2915 Is_Owned_By_Player() && // Changed for multiplayer. ST - 3/13/2019 5:37PM 2916 //IsOwnedByPlayer && 2917 object->Is_Techno() && 2918 //IsOwnedByPlayer && 2919 ((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { 2920 // if (object->Owner() == Owner() && object->What_Am_I() == RTTI_UNIT && ((UnitClass *)object)->Class->IsTransporter && ((UnitClass *)object)->How_Many() < 5) { 2921 action = ACTION_ENTER; 2922 } 2923 2924 if (Class->IsCapture && action == ACTION_ATTACK) { 2925 if (object->Owner() != Owner() && 2926 ((object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || 2927 (object->What_Am_I() == RTTI_BUILDING && 2928 ((BuildingClass *)object)->Can_Capture())) 2929 ) { 2930 2931 action = ACTION_CAPTURE; 2932 } else { 2933 if (Class->Primary == WEAPON_NONE) { 2934 action = ACTION_NONE; 2935 } 2936 } 2937 } 2938 return(action); 2939 } 2940 2941 2942 /*********************************************************************************************** 2943 * InfantryClass::Read_INI -- Reads units from scenario INI file. * 2944 * * 2945 * This routine is used to read all the starting units from the * 2946 * scenario control INI file. The units are created and placed on the * 2947 * map by this routine. * 2948 * * 2949 * INI entry format: * 2950 * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * 2951 * Facingnum, Triggername * 2952 * * 2953 * INPUT: buffer -- Pointer to the loaded scenario INI file. * 2954 * * 2955 * OUTPUT: none * 2956 * * 2957 * WARNINGS: none * 2958 * * 2959 * HISTORY: * 2960 * 05/24/1994 JLB : Created. * 2961 *=============================================================================================*/ 2962 void InfantryClass::Read_INI(char *buffer) 2963 { 2964 InfantryClass *infantry; // Working infantry pointer. 2965 char *tbuffer; // Accumulation buffer of infantry IDs. 2966 HousesType inhouse; // Infantry house. 2967 InfantryType classid; // Infantry class. 2968 int len; // Length of data in buffer. 2969 char buf[128]; 2970 2971 len = strlen(buffer) + 2; 2972 tbuffer = buffer + len; 2973 2974 /*------------------------------------------------------------------------ 2975 Read the entire INFANTRY INI section into HIDBUF 2976 ------------------------------------------------------------------------*/ 2977 WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); 2978 2979 while (*tbuffer != '\0') { 2980 2981 /* 2982 ** Get an infantry entry 2983 */ 2984 WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); 2985 2986 /* 2987 ** 1st token: house name. 2988 */ 2989 inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); 2990 if (inhouse != HOUSE_NONE) { 2991 2992 /* 2993 ** 2nd token: infantry type name. 2994 */ 2995 classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); 2996 2997 /* 2998 ** Special case: replace C7 with C5 on scg08eb 2999 */ 3000 if (GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_GOOD && Scenario == 8 && ScenVar == SCEN_VAR_B && classid == INFANTRY_C7) { 3001 classid = INFANTRY_C5; 3002 } 3003 3004 if (classid != INFANTRY_NONE) { 3005 3006 if (HouseClass::As_Pointer(inhouse) != NULL) { 3007 infantry = new InfantryClass(classid, inhouse); 3008 if (infantry) { 3009 3010 /* 3011 ** 3rd token: strength. 3012 */ 3013 int strength = atoi(strtok(NULL, ",\n\r")); 3014 3015 /* 3016 ** 4th token: cell #. 3017 */ 3018 COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\n\r"))); 3019 3020 /* 3021 ** 5th token: cell sub-location. 3022 */ 3023 coord = Coord_Add(coord & 0xFF00FF00L, StoppingCoordAbs[atoi(strtok(NULL, ","))]); 3024 3025 /* 3026 ** Fetch the mission and facing. 3027 */ 3028 MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); 3029 DirType dir = (DirType)atoi(strtok(NULL,",\n\r")); 3030 infantry->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\n\r")); 3031 if (infantry->Trigger) { 3032 infantry->Trigger->AttachCount++; 3033 } 3034 3035 /* 3036 ** Special case: delete pre-placed Chan on scb10ea; he will spawn from the Tech Center. 3037 */ 3038 bool is_scb10ea_chan = GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10 && ScenVar == SCEN_VAR_A && *infantry == INFANTRY_CHAN; 3039 3040 if (!is_scb10ea_chan && infantry->Unlimbo(coord, dir)) { 3041 infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); 3042 if (GameToPlay == GAME_NORMAL || infantry->House->IsHuman) { 3043 infantry->Assign_Mission(mission); 3044 infantry->Commence(); 3045 } else { 3046 infantry->Enter_Idle_Mode(); 3047 } 3048 } else { 3049 3050 /* 3051 ** If the infantry could not be unlimboed, then this is a big error. 3052 ** Delete the infantry. 3053 */ 3054 delete infantry; 3055 } 3056 } 3057 } 3058 } 3059 } 3060 tbuffer += strlen(tbuffer)+1; 3061 } 3062 } 3063 3064 3065 /*********************************************************************************************** 3066 * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * 3067 * * 3068 * This routine writes all of the infantry in the game out to an INI file. This is used * 3069 * in the scenario editor when the game needs to be saved. * 3070 * * 3071 * INI entry format: * 3072 * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * 3073 * Facingnum, Triggername * 3074 * * 3075 * INPUT: buffer -- A pointer to the loaded INI file staging area. * 3076 * * 3077 * OUTPUT: none * 3078 * * 3079 * WARNINGS: none * 3080 * * 3081 * HISTORY: * 3082 * 05/28/1994 JLB : Created. * 3083 *=============================================================================================*/ 3084 void InfantryClass::Write_INI(char *buffer) 3085 { 3086 int index; 3087 char uname[10]; 3088 char buf[128]; 3089 char *tbuffer; // Accumulation buffer of infantry IDs. 3090 3091 /* 3092 ** First, clear out all existing infantry data from the ini file. 3093 */ 3094 tbuffer = buffer + strlen(buffer) + 2; 3095 WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); 3096 while (*tbuffer != '\0') { 3097 WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); 3098 tbuffer += strlen(tbuffer)+1; 3099 } 3100 3101 /* 3102 ** Write the infantry data out. 3103 */ 3104 for (index = 0; index < Infantry.Count(); index++) { 3105 InfantryClass * infantry; 3106 3107 infantry = Infantry.Ptr(index); 3108 if (!infantry->IsInLimbo) { 3109 3110 sprintf(uname, "%03d", index); 3111 sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", 3112 infantry->House->Class->IniName, 3113 infantry->Class->IniName, 3114 infantry->Health_Ratio(), 3115 Coord_Cell(infantry->Coord), 3116 CellClass::Spot_Index(infantry->Coord), 3117 MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? 3118 infantry->MissionQueue : infantry->Mission), 3119 infantry->PrimaryFacing.Current(), 3120 infantry->Trigger ? infantry->Trigger->Get_Name() : "None" 3121 ); 3122 WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); 3123 } 3124 } 3125 } 3126 3127 3128 /*********************************************************************************************** 3129 * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * 3130 * * 3131 * This routine is called when the player clicks over an object while this infantry soldier * 3132 * is selected. Capture attempts are prohibited if the infantry cannot capture. The * 3133 * command might respond if told to sabotage something. * 3134 * * 3135 * INPUT: action -- The action that is nominally to be performed. * 3136 * * 3137 * object -- The object over which the mouse was clicked. * 3138 * * 3139 * OUTPUT: none * 3140 * * 3141 * WARNINGS: none * 3142 * * 3143 * HISTORY: * 3144 * 05/08/1995 JLB : Created. * 3145 *=============================================================================================*/ 3146 void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) 3147 { 3148 Validate(); 3149 if (What_Action(object) != action) { 3150 switch (action) { 3151 case ACTION_SABOTAGE: 3152 case ACTION_CAPTURE: 3153 action = ACTION_ATTACK; 3154 break; 3155 3156 case ACTION_ENTER: 3157 action = ACTION_MOVE; 3158 break; 3159 3160 default: 3161 action = ACTION_NONE; 3162 break; 3163 } 3164 } 3165 3166 FootClass::Active_Click_With(action, object); 3167 } 3168 3169 3170 /*********************************************************************************************** 3171 * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * 3172 * * 3173 * When the infantry soldier is responsible for a kill, this routine is called. It checks * 3174 * to see if the soldier should make some comment or perform some action. The commando * 3175 * infantry is most likely to respond. * 3176 * * 3177 * INPUT: none * 3178 * * 3179 * OUTPUT: Returns the number of kills this infantry soldier has made. * 3180 * * 3181 * WARNINGS: none * 3182 * * 3183 * HISTORY: * 3184 * 05/08/1995 JLB : Created. * 3185 *=============================================================================================*/ 3186 int InfantryClass::Made_A_Kill(void) 3187 { 3188 Validate(); 3189 if (*this == INFANTRY_RAMBO || Random_Pick(0, 5) < Kills) { 3190 IsStoked = true; 3191 Comment = TICKS_PER_SECOND*2; 3192 } 3193 return(FootClass::Made_A_Kill()); 3194 } 3195 3196 3197 /*********************************************************************************************** 3198 * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * 3199 * * 3200 * INPUT: CELL - the cell we are setting the bit in * 3201 * * 3202 * int - the spot index we are setting the bit for * 3203 * * 3204 * OUTPUT: none * 3205 * * 3206 * HISTORY: * 3207 * 06/08/1995 PWG : Created. * 3208 *=============================================================================================*/ 3209 void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) 3210 { 3211 Validate(); 3212 3213 if ((unsigned)cell >= MAP_CELL_TOTAL) { 3214 return; 3215 } 3216 3217 /* 3218 ** Set the occupy postion for the spot that we passed in 3219 */ 3220 Map[cell].Flag.Composite |= (1 << spot_index); 3221 3222 /* 3223 ** Record the type of infantry that now owns the cell 3224 */ 3225 Map[cell].InfType = Owner(); 3226 } 3227 3228 3229 /*************************************************************************** 3230 * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * 3231 * * 3232 * INPUT: * 3233 * * 3234 * OUTPUT: * 3235 * * 3236 * WARNINGS: * 3237 * * 3238 * HISTORY: * 3239 * 06/08/1995 PWG : Created. * 3240 *=========================================================================*/ 3241 void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) 3242 { 3243 Validate(); 3244 3245 if ((unsigned)cell >= MAP_CELL_TOTAL) { 3246 return; 3247 } 3248 3249 /* 3250 ** Clear the occupy bit for the infantry in that cell 3251 */ 3252 Map[cell].Flag.Composite &= ~(1 << spot_index); 3253 3254 /* 3255 ** If he was the last infantry recorded in the cell then 3256 ** remove the infantry ownership flag. 3257 */ 3258 if (!(Map[cell].Flag.Composite & 0x1F)) { 3259 Map[cell].InfType = HOUSE_NONE; 3260 } 3261 } 3262 3263 3264 /*********************************************************************************************** 3265 * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * 3266 * * 3267 * This routine will return with the full name (as a text number) for this infantry * 3268 * unit. Typically, this is the normal name, but in cases of civilian type survivors from * 3269 * a building explosion, it might be a technician instead. In such a case, the special * 3270 * technician name number is returned instead. * 3271 * * 3272 * INPUT: none * 3273 * * 3274 * OUTPUT: Returns with the full name to use for this infantry unit. * 3275 * * 3276 * WARNINGS: none * 3277 * * 3278 * HISTORY: * 3279 * 06/30/1995 JLB : Created. * 3280 *=============================================================================================*/ 3281 int InfantryClass::Full_Name(void) const 3282 { 3283 Validate(); 3284 if (IsTechnician) { 3285 return(TXT_TECHNICIAN); 3286 } 3287 return(Class->Full_Name()); 3288 } 3289 3290 3291 /*********************************************************************************************** 3292 * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * 3293 * * 3294 * This routine intercepts the normal attack mission and if an engineer is detected and the * 3295 * target is a building, then the engineer will be automatically assigned the capture * 3296 * mission. In other cases, the normal attack logic will proceed. * 3297 * * 3298 * INPUT: none * 3299 * * 3300 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 3301 * * 3302 * WARNINGS: none * 3303 * * 3304 * HISTORY: * 3305 * 08/07/1995 JLB : Created. * 3306 *=============================================================================================*/ 3307 int InfantryClass::Mission_Attack(void) 3308 { 3309 Validate(); 3310 if (*this == INFANTRY_E7 && As_Building(TarCom)) { 3311 Assign_Destination(TarCom); 3312 Assign_Mission(MISSION_CAPTURE); 3313 return(1); 3314 } 3315 return(FootClass::Mission_Attack()); 3316 } 3317 3318 3319 RTTIType InfantryClass::What_Am_I(void) const 3320 { 3321 Validate(); 3322 return(RTTI_INFANTRY); 3323 } 3324 3325 ActionType InfantryClass::What_Action(CELL cell) const 3326 { 3327 Validate(); 3328 return FootClass::What_Action(cell); 3329 } 3330 3331 ObjectTypeClass const & InfantryClass::Class_Of(void) const 3332 { 3333 Validate(); 3334 return(*Class); 3335 } 3336 3337 bool InfantryClass::Is_Infantry(void) const 3338 { 3339 Validate(); 3340 return(true); 3341 }