INFANTRY.CPP (180034B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: /CounterStrike/INFANTRY.CPP 2 3/03/97 10:35p 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 : October 28, 1996 [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::Assign_Destination -- Gives the infantry a movement destination. * 36 * InfantryClass::Assign_Target -- Gives the infantry a combat target. * 37 * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * 38 * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * 39 * InfantryClass::Class_Of -- Returns the class reference for this object. * 40 * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * 41 * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * 42 * InfantryClass::Detach -- Removes the specified target from targeting computer. * 43 * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * 44 * InfantryClass::Doing_AI -- Handles the animation AI processing. * 45 * InfantryClass::Draw_It -- Draws a unit object. * 46 * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * 47 * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * 48 * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * 49 * InfantryClass::Fire_At -- Fires projectile from infantry unit. * 50 * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * 51 * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * 52 * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * 53 * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * 54 * InfantryClass::InfantryClass -- The constructor for infantry objects. * 55 * InfantryClass::Init -- Initialize the infantry object system. * 56 * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle* 57 * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * 58 * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * 59 * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * 60 * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't o* 61 * InfantryClass::Paradrop -- Handles paradropping infantry. * 62 * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * 63 * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * 64 * InfantryClass::Read_INI -- Reads units from scenario INI file. * 65 * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * 66 * InfantryClass::Response_Move -- Plays infantry response to movement order. * 67 * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * 68 * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * 69 * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * 70 * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* 71 * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * 72 * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * 73 * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * 74 * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * 75 * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * 76 * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * 77 * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * 78 * InfantryClass::Write_INI -- Store the infantry to the INI database. * 79 * InfantryClass::operator delete -- Returns the infantry object back to the free pool * 80 * InfantryClass::operator new -- Allocates an infantry object from the free pool. * 81 * InfantryClass::~InfantryClass -- Default destructor for infantry units. * 82 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 83 84 #include "function.h" 85 86 /* 87 ** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM 88 */ 89 #include "SidebarGlyphx.h" 90 91 92 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}; 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 // interruptible, 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_GUN_DEATH 113 {false, false, false, 2}, // DO_EXPLOSION_DEATH 114 {false, false, false, 2}, // DO_EXPLOSION2_DEATH 115 {false, false, false, 2}, // DO_GRENADE_DEATH 116 {false, false, false, 2}, // DO_FIRE_DEATH 117 {false, false, false, 2}, // DO_GESTURE1 118 {false, false, false, 2}, // DO_SALUTE1 119 {false, false, false, 2}, // DO_GESTURE2 120 {false, false, false, 2}, // DO_SALUTE2 121 {false, false, false, 2}, // DO_DOG_MAUL 122 }; 123 124 125 #ifdef CHEAT_KEYS 126 /*********************************************************************************************** 127 * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * 128 * * 129 * This routine is used by the debug version to display pertinent information about the * 130 * infantry unit. * 131 * * 132 * INPUT: mono -- The monochrome screen to display the debug information to. * 133 * * 134 * OUTPUT: none * 135 * * 136 * WARNINGS: none * 137 * * 138 * HISTORY: * 139 * 09/01/1994 JLB : Created. * 140 *=============================================================================================*/ 141 void InfantryClass::Debug_Dump(MonoClass * mono) const 142 { 143 assert(Infantry.ID(this) == ID); 144 assert(IsActive); 145 146 mono->Set_Cursor(0, 0); 147 148 mono->Print(Text_String(TXT_DEBUG_INFANTRY)); 149 mono->Set_Cursor(1, 11);mono->Printf("%3d", Doing); 150 mono->Set_Cursor(8, 11);mono->Printf("%3d", Fear); 151 152 mono->Fill_Attrib(66, 13, 12, 1, IsTechnician ? MonoClass::INVERSE : MonoClass::NORMAL); 153 mono->Fill_Attrib(66, 14, 12, 1, IsStoked ? MonoClass::INVERSE : MonoClass::NORMAL); 154 mono->Fill_Attrib(66, 15, 12, 1, IsProne ? MonoClass::INVERSE : MonoClass::NORMAL); 155 156 FootClass::Debug_Dump(mono); 157 } 158 #endif 159 160 161 /*********************************************************************************************** 162 * InfantryClass::InfantryClass -- The constructor for infantry objects. * 163 * * 164 * This is the constructor used when creating an infantry unit. All values are required * 165 * except for facing and position. If these are absent, then the infantry is created in * 166 * a state of limbo -- not placed upon the map. * 167 * * 168 * INPUT: see below... * 169 * * 170 * OUTPUT: none * 171 * * 172 * WARNINGS: none * 173 * * 174 * HISTORY: * 175 * 09/01/1994 JLB : Created. * 176 *=============================================================================================*/ 177 InfantryClass::InfantryClass(InfantryType classid, HousesType house) : 178 FootClass(RTTI_INFANTRY, Infantry.ID(this), house), 179 Class(InfantryTypes.Ptr((int)classid)), 180 Doing(DO_NOTHING), 181 Comment(0), 182 IsTechnician(false), 183 IsStoked(false), 184 IsProne(false), 185 IsZoneCheat(false), 186 WasSelected(false), 187 Fear(FEAR_NONE), 188 StopDriverFrame(-1), 189 LookCell(0) 190 { 191 House->Tracking_Add(this); 192 #ifdef FIXIT_CSII // checked - ajw 9/28/98 193 IsCloakable = Class->IsCloakable; 194 #endif 195 /* 196 ** For two shooters, clear out the second shot flag -- it will be set the first time 197 ** the object fires. For non two shooters, set the flag since it will never be cleared 198 ** and the second shot flag tells the system that normal rearm times apply -- this is 199 ** what is desired for non two shooters. 200 */ 201 if (Class->Is_Two_Shooter()) { 202 IsSecondShot = false; 203 } else { 204 IsSecondShot = true; 205 } 206 Strength = Class->MaxStrength; 207 208 /* 209 ** Civilians carry much less ammo than soldiers do. 210 */ 211 Ammo = Class->MaxAmmo; 212 } 213 214 215 /*********************************************************************************************** 216 * InfantryClass::~InfantryClass -- Default destructor for infantry units. * 217 * * 218 * This is the default destructor for infantry type units. It will put the infantry into * 219 * a limbo state if it isn't already in that state and the game is still active. * 220 * * 221 * INPUT: none * 222 * * 223 * OUTPUT: none * 224 * * 225 * WARNINGS: none * 226 * * 227 * HISTORY: * 228 * 01/10/1995 JLB : Created. * 229 *=============================================================================================*/ 230 InfantryClass::~InfantryClass(void) 231 { 232 if (GameActive && Class.Is_Valid()) { 233 234 /* 235 ** Remove this member from any team it may be associated with. This must occur at the 236 ** top most level of the inheritance hierarchy because it may call virtual functions. 237 */ 238 if (Team.Is_Valid()) { 239 Team->Remove(this); 240 Team = NULL; 241 } 242 243 House->Tracking_Remove(this); 244 Limbo(); 245 } 246 ID = -1; 247 } 248 249 250 /*********************************************************************************************** 251 * InfantryClass::operator new -- Allocates an infantry object from the free pool. * 252 * * 253 * This will allocate an infantry object from the infantry object free pool. If there is * 254 * no available slot, then NULL is returned. * 255 * * 256 * INPUT: none * 257 * * 258 * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * 259 * allocated. * 260 * * 261 * WARNINGS: none * 262 * * 263 * HISTORY: * 264 * 09/01/1994 JLB : Created. * 265 *=============================================================================================*/ 266 void * InfantryClass::operator new(size_t) 267 { 268 void * ptr = Infantry.Allocate(); 269 if (ptr != NULL) { 270 ((InfantryClass *)ptr)->Set_Active(); 271 } 272 return(ptr); 273 } 274 275 276 /*********************************************************************************************** 277 * InfantryClass::operator delete -- Returns the infantry object back to the free pool * 278 * * 279 * This routine is used return an infantry object back to the system. * 280 * * 281 * INPUT: ptr -- Pointer to the infantry object to delete. * 282 * * 283 * OUTPUT: none * 284 * * 285 * WARNINGS: none * 286 * * 287 * HISTORY: * 288 * 09/08/1994 JLB : Created. * 289 *=============================================================================================*/ 290 void InfantryClass::operator delete(void * ptr) 291 { 292 if (ptr != NULL) { 293 ((InfantryClass *)ptr)->IsActive = false; 294 } 295 Infantry.Free((InfantryClass *)ptr); 296 } 297 298 299 /*********************************************************************************************** 300 * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * 301 * * 302 * This routine applies the damage specified to the infantry object. It is possible that * 303 * this routine will DESTROY the infantry unit in the process. * 304 * * 305 * INPUT: damage -- The damage points to inflict. * 306 * * 307 * distance -- The distance from the damage center point to the object's center point.* 308 * * 309 * warhead -- The warhead type that is inflicting the damage. * 310 * * 311 * source -- Who is responsible for inflicting the damage. * 312 * * 313 * OUTPUT: bool; Was the infantry unit destroyed by this damage? * 314 * * 315 * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * 316 * for this in the code that follows the call to Take_Damage(). * 317 * * 318 * HISTORY: * 319 * 09/08/1994 JLB : Created. * 320 * 11/22/1994 JLB : Shares base damage handler for techno objects. * 321 * 03/31/1995 JLB : Revenge factor. * 322 *=============================================================================================*/ 323 ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) 324 { 325 assert(Infantry.ID(this) == ID); 326 assert(IsActive); 327 328 ResultType res = RESULT_NONE; 329 330 /* 331 ** Prone infantry take only half damage, but never below one damage point. 332 */ 333 if (IsProne && damage > 0) { 334 damage = damage * Rule.ProneDamageBias; 335 } 336 337 /* 338 ** If we're taking damage from a dog, we have to decide if we're the 339 ** target of the dog. Dogs don't spill collateral damage onto anyone 340 ** else, so if we're the target of a valid dog, take full damage, but if 341 ** we're not the target, or the dog doesn't exist, then take no damage. 342 */ 343 if (source != NULL && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { 344 if (source->TarCom == As_Target()) { 345 damage = Strength; 346 } else { 347 damage = 0; 348 } 349 } 350 res = FootClass::Take_Damage(damage, distance, warhead, source, forced); 351 352 /* 353 ** hack for dog: if you're hit by a dog, and you're the target, your 354 ** damage gets upped to max. 355 */ 356 357 if (res == RESULT_NONE) return(res); 358 359 if (res == RESULT_DESTROYED) { 360 if (*this == INFANTRY_TANYA) { 361 IsTanyaDead = true; 362 } 363 Death_Announcement(source); 364 Stop_Driver(); 365 Stun(); 366 Mission = MISSION_NONE; 367 Assign_Mission(MISSION_GUARD); 368 Commence(); 369 370 VocType sound; 371 VocType altsound; 372 sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM11); 373 altsound = VOC_YELL1; 374 if (*this == INFANTRY_TANYA) { 375 sound = altsound = VOC_TANYA_DIE; 376 } 377 if (Class->IsDog) { 378 sound = altsound = VOC_DOG_HURT; 379 } 380 381 /* 382 ** The type of warhead determines the animation the infantry 383 ** will perform when killed. 384 */ 385 bool delthis = false; 386 TARGET us = As_Target(); 387 switch (WarheadTypeClass::As_Pointer(warhead)->InfantryDeath) { 388 default: 389 case 0: 390 delthis = true; 391 break; 392 393 case 1: 394 Sound_Effect(sound, Coord); 395 Do_Action(DO_GUN_DEATH, true); 396 break; 397 398 case 2: 399 Sound_Effect(sound, Coord); 400 Do_Action(DO_EXPLOSION_DEATH, true); 401 break; 402 403 case 3: 404 Sound_Effect(sound, Coord); 405 Do_Action(DO_GRENADE_DEATH, true); 406 break; 407 408 case 4: 409 Sound_Effect(altsound, Coord); 410 Do_Action(DO_FIRE_DEATH, true); 411 break; 412 413 case 5: 414 Sound_Effect(sound, Coord); 415 AnimType anim = ANIM_ELECT_DIE; 416 if (Class->IsDog) anim = ANIM_DOG_ELECT_DIE; 417 new AnimClass(anim, Coord); 418 delthis = true; 419 break; 420 } 421 422 if (delthis) { 423 delete this; 424 } 425 return(res); 426 } 427 428 /* 429 ** When infantry gets hit, it gets scared. 430 */ 431 if (res != RESULT_DESTROYED) { 432 COORDINATE source_coord = (source) ? source->Coord : NULL; 433 434 /* 435 ** If an engineer is damaged and it is just sitting there, then tell it 436 ** to go do something since it will definitely die if it doesn't. 437 */ 438 if (!House->IsHuman && *this == INFANTRY_RENOVATOR && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { 439 Assign_Mission(MISSION_HUNT); 440 } 441 442 if (source != NULL) { 443 Scatter(source_coord); 444 } 445 446 if (source != NULL && Fear < FEAR_SCARED) { 447 if (Class->IsFraidyCat) { 448 Fear = FEAR_PANIC; 449 } else { 450 Fear = FEAR_SCARED; 451 } 452 } else { 453 454 /* 455 ** Increase the fear of the infantry by a bit. The fear increases more 456 ** quickly if the infantry is damaged. 457 */ 458 int morefear = FEAR_ANXIOUS; 459 if (Health_Ratio() > Rule.ConditionRed) morefear /= 2; 460 if (Health_Ratio() > Rule.ConditionYellow) morefear /= 2; 461 Fear = FearType(min((int)Fear + morefear, FEAR_MAXIMUM)); 462 } 463 } 464 return(res); 465 } 466 467 468 /*********************************************************************************************** 469 * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * 470 * * 471 * This will determine the shape number to use for this infantry soldier. The shape number * 472 * is relative to the shape file associated with this infantry unit. * 473 * * 474 * INPUT: Window we will be drawing into * 475 * * 476 * OUTPUT: Returns with the shape number for this infantry object to be used when drawing. * 477 * * 478 * WARNINGS: none * 479 * * 480 * HISTORY: * 481 * 07/29/1996 JLB : Created. * 482 * 9/4/2019 1:45PM ST : Added window parameter * 483 *=============================================================================================*/ 484 int InfantryClass::Shape_Number(WindowNumberType window) const 485 { 486 /* 487 ** Fetch the shape pointer to use for the infantry. This is controlled by what 488 ** choreograph sequence the infantry is performing, it's facing, and whether it 489 ** is prone. 490 */ 491 DoType doit = Doing; 492 if (doit == DO_NOTHING) doit = DO_STAND_READY; 493 494 /* 495 ** 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 496 ** goes into the stand pose for a single frame when pausing in the assigned cell destination. ST - 9/4/2019 1:39PM 497 */ 498 if (doit == DO_STAND_READY) { 499 if (window == WINDOW_VIRTUAL) { 500 if (StopDriverFrame != -1) { 501 if (Frame - StopDriverFrame <= 2) { 502 if (Path[0] != FACING_NONE) { 503 doit = DO_WALK; 504 } 505 } 506 } 507 } 508 } 509 510 /* 511 ** The animation frame numbers may be different when rendering in legacy mode vs. exporting for render in GlyphX. ST - 9/5/2019 12:34PM 512 */ 513 const DoInfoStruct *do_controls = (window == WINDOW_VIRTUAL) ? Class->DoControlsVirtual : Class->DoControls; 514 if (window != WINDOW_VIRTUAL && !IsOwnedByPlayer && *this == INFANTRY_SPY) { 515 do_controls = InfantryTypeClass::As_Reference(INFANTRY_E1).DoControls; 516 } 517 518 /* 519 ** The infantry shape is always modulo the number of animation frames 520 ** of the action stage that the infantry is doing. 521 */ 522 int shapenum = Fetch_Stage() % max(do_controls[doit].Count, 1); 523 524 /* 525 ** If facing makes a difference, then the shape number will be incremented 526 ** by the facing accordingly. 527 */ 528 if (do_controls[doit].Jump) { 529 shapenum += HumanShape[Dir_To_32(PrimaryFacing.Current())] * do_controls[doit].Jump; 530 } 531 532 /* 533 ** Finally, the shape number is biased according to the starting frame number for 534 ** that action in the infantry shape file. 535 */ 536 shapenum += do_controls[doit].Frame; 537 538 /* 539 ** Return with the final infantry shape number. 540 */ 541 return(shapenum); 542 } 543 544 545 /*********************************************************************************************** 546 * InfantryClass::Draw_It -- Draws a unit object. * 547 * * 548 * This routine is the one that actually draws a unit object. It displays the unit * 549 * according to its current state flags and centered at the location specified. * 550 * * 551 * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * 552 * * 553 * window -- The clipping window to use. * 554 * * 555 * OUTPUT: none * 556 * * 557 * WARNINGS: none * 558 * * 559 * HISTORY: * 560 * 06/20/1994 JLB : Created. * 561 * 06/27/1994 JLB : Takes a window parameter. * 562 * 08/15/1994 JLB : Converted to infantry support. * 563 * 08/14/1996 JLB : Simplified. * 564 *=============================================================================================*/ 565 void InfantryClass::Draw_It(int x, int y, WindowNumberType window) const 566 { 567 assert(Infantry.ID(this) == ID); 568 assert(IsActive); 569 570 /* 571 ** Verify the legality of the unit class by seeing if there is shape imagery for it. If 572 ** there is no shape image, then it certainly can't be drawn -- bail. 573 */ 574 void const * shapefile = Get_Image_Data(); 575 576 if (shapefile == NULL) return; 577 578 y += 4; 579 x -= 2; 580 581 /* 582 ** Actually draw the root body of the unit. 583 */ 584 Techno_Draw_Object(shapefile, Shape_Number(window), x, y, window); 585 586 FootClass::Draw_It(x, y, window); 587 } 588 589 extern bool MPSuperWeaponDisable; 590 591 /*********************************************************************************************** 592 * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * 593 * * 594 * This routine will handle any special operations that need to be performed once each * 595 * cell travelled. This includes radioing a transport that it is now clear and the * 596 * transport is free to leave. * 597 * * 598 * INPUT: why -- Specifies the circumstances under which this routine was called. * 599 * * 600 * OUTPUT: none * 601 * * 602 * WARNINGS: none * 603 * * 604 * HISTORY: * 605 * 09/08/1994 JLB : Created. * 606 * 03/01/1995 JLB : Capture building options. * 607 * 05/31/1995 JLB : Capture is always successful now. * 608 *=============================================================================================*/ 609 void InfantryClass::Per_Cell_Process(PCPType why) 610 { 611 assert(Infantry.ID(this) == ID); 612 assert(IsActive); 613 614 BStart(BENCH_PCP); 615 CellClass * cellptr = &Map[Coord]; 616 617 if (why == PCP_END) { 618 619 /* 620 ** If the infantry unit is entering a cell that contains the building it is trying to 621 ** capture, then capture it. 622 */ 623 if (Mission == MISSION_CAPTURE) { 624 TechnoClass * tech = cellptr->Cell_Building(); 625 if (tech != NULL) { 626 if ((tech->As_Target() == NavCom || tech->As_Target() == TarCom) && !tech->Can_Capture()) { 627 tech = NULL; 628 Assign_Destination(TARGET_NONE); 629 } 630 } else { 631 tech = cellptr->Cell_Techno(); 632 } 633 if (tech != NULL && (tech->As_Target() == NavCom || tech->As_Target() == TarCom)) { 634 if (*this == INFANTRY_RENOVATOR) { 635 636 /* 637 ** An engineer will either mega-repair a friendly or allied 638 ** building or it will damage/capture an enemy building. Whether 639 ** it damages or captures depends on how badly damaged the 640 ** enemy building is. 641 */ 642 #ifdef FIXIT_ENGINEER_CAPTURE 643 if (House->Is_Ally(tech)) { 644 #else 645 if (tech->House->Is_Ally(House)) { 646 #endif 647 if (tech->Trigger.Is_Valid()) { 648 tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); 649 } 650 tech->Renovate(); 651 } else { 652 bool iscapturable = false; 653 if (tech->What_Am_I() == RTTI_BUILDING) { 654 iscapturable = tech->Can_Capture(); 655 } 656 #ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 657 if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) { 658 #else 659 if (tech->Health_Ratio() <= Rule.ConditionRed && iscapturable) { 660 #endif 661 if (tech->Trigger.Is_Valid()) { 662 tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); 663 } 664 tech->House->IsThieved = true; 665 tech->Captured(House); 666 } else { 667 #ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 668 int damage = min( (short)((int)(tech->Techno_Type_Class()->MaxStrength) * EngineerDamage), tech->Strength-1); 669 #else 670 int damage = min( (tech->Techno_Type_Class()->MaxStrength) / 3, tech->Strength-1); 671 #endif 672 tech->Take_Damage(damage, 0, WARHEAD_HE, this, true); 673 } 674 BEnd(BENCH_PCP); 675 delete this; 676 return; 677 } 678 679 } else { 680 if (*this != INFANTRY_SPY && tech->Trigger.Is_Valid()) { 681 tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); 682 } 683 684 if (*this == INFANTRY_SPY) { 685 int housespy = (1 << (House->Class->House)); 686 // tech->House->IsSpied = true; 687 688 if (tech->Trigger.Is_Valid()) { 689 tech->Trigger->Spring(TEVENT_SPIED, this); 690 } 691 692 if (IsOwnedByPlayer) Speak(VOX_BUILDING_INFILTRATED); 693 694 tech->Mark(MARK_OVERLAP_UP); 695 tech->SpiedBy |= housespy; 696 tech->Mark(MARK_OVERLAP_DOWN); 697 if (tech->What_Am_I() == RTTI_BUILDING) { 698 StructType build = *(BuildingClass *)tech; 699 if (build == STRUCT_RADAR /* || build == STRUCT_EYE */ ) { 700 tech->House->RadarSpied |= housespy; 701 } 702 703 if (Session.Type == GAME_NORMAL || !MPSuperWeaponDisable) { 704 705 // If they're spying on a sub pen, give 'em a sonar pulse 706 if (build == STRUCT_SUB_PEN) { 707 House->SuperWeapon[SPC_SONAR_PULSE].Enable(false, true, false); 708 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 709 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 710 if (House->IsHuman) { 711 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, House); 712 } 713 } else { 714 if (IsOwnedByPlayer) { 715 Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); 716 Map.Column[1].Flag_To_Redraw(); 717 } 718 } 719 } 720 // If they're spying on an airfield, they get Parabombs 721 if (build == STRUCT_AIRSTRIP) { 722 House->SuperWeapon[SPC_PARA_BOMB].Enable(true, true, false); 723 // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM 724 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 725 if (House->IsHuman) { 726 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, House); 727 } 728 } 729 else { 730 if (IsOwnedByPlayer) { 731 Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); 732 Map.Column[1].Flag_To_Redraw(); 733 } 734 } 735 } 736 } 737 } 738 739 } else { 740 741 if (*this == INFANTRY_THIEF) { // Thief just raided a storage facility 742 tech->House->IsThieved = true; 743 744 if (tech->What_Am_I() == RTTI_BUILDING) { 745 BuildingClass * bldg = (BuildingClass *)tech; 746 if (bldg->Class->Capacity) { 747 748 /* 749 ** If we just raided a storage facility (refinery or silo) 750 ** then give the thief up to half the capacity of the 751 ** storage facility. 752 */ 753 if (IsOwnedByPlayer || bldg->IsOwnedByPlayer) Speak(VOX_MONEY_STOLEN); 754 #ifdef OBSOLETE 755 long capacity = bldg->Class->Capacity * 256; 756 capacity /= (bldg->House->Tiberium+1); 757 int bldgcap = bldg->Class->Capacity; 758 759 long cash = (bldgcap * 256) / (capacity+1); 760 if (cash > (bldgcap / 2)) cash = bldgcap / 2; 761 #else 762 long cash = bldg->House->Available_Money() / 2; 763 #endif 764 bldg->House->Spend_Money(cash); 765 House->Refund_Money(cash); 766 } 767 } 768 } 769 } 770 } 771 BEnd(BENCH_PCP); 772 delete this; 773 return; 774 775 } else { 776 777 #ifdef OBSOLETE 778 // are we trying to repair a bridge? 779 if (Is_Target_Cell(TarCom) ) { 780 CELL cell = Coord_Cell(Coord); 781 if (cell == ::As_Cell(NavCom)) { 782 TemplateType tt = cellptr->TType; 783 int icon = cellptr->TIcon; 784 int w = TemplateTypeClass::As_Reference(cellptr->TType).Width; 785 int h = TemplateTypeClass::As_Reference(cellptr->TType).Height; 786 787 cell -= icon % w; 788 cell -= MAP_CELL_W * (icon / w); 789 if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D) { 790 new TemplateClass(TemplateType(cellptr->TType-1), cell); 791 Map.Zone_Reset(MZONEF_ALL); 792 delete this; 793 return; 794 } else { 795 796 // Trying to repair multi-segment bridge. Look for the 797 // start tile, then fix it, and determine the direction to 798 // go in and repair it all that way. 799 TemplateType newtt = TEMPLATE_BRIDGE_1A; 800 int xmov = -1; // coords to move to for next template 801 int ymov = 2; 802 bool valid = false; 803 switch (tt) { 804 case TEMPLATE_BRIDGE_1B: 805 case TEMPLATE_BRIDGE_1C: 806 valid = true; 807 break; 808 case TEMPLATE_BRIDGE_2B: 809 case TEMPLATE_BRIDGE_2C: 810 newtt = TEMPLATE_BRIDGE_2A; 811 xmov = 2; 812 ymov = -1; 813 valid = true; 814 break; 815 case TEMPLATE_BRIDGE_3C: 816 case TEMPLATE_BRIDGE_3D: 817 newtt = TEMPLATE_BRIDGE_3A; 818 valid = true; 819 break; 820 case TEMPLATE_BRIDGE_3E: 821 newtt = TEMPLATE_BRIDGE_3A; 822 xmov = 2; 823 ymov = -1; 824 valid = true; 825 break; 826 } 827 828 // Did we find a valid repairable bridge piece? 829 if (valid) { 830 bool doing = true; 831 while (doing) { 832 new TemplateClass(TemplateType(newtt), cell); 833 cell += (MAP_CELL_W * ymov) + xmov; 834 if (xmov < 0) { 835 xmov = -1; 836 ymov = 1; 837 } else { 838 xmov = 1; 839 ymov = -1; 840 } 841 cellptr = &Map[cell]; 842 tt = cellptr->TType; 843 if ((tt >= TEMPLATE_BRIDGE_3B && tt <= TEMPLATE_BRIDGE_3F) || 844 tt == TEMPLATE_BRIDGE_1B || tt == TEMPLATE_BRIDGE_1C || 845 tt == TEMPLATE_BRIDGE_2B || tt == TEMPLATE_BRIDGE_2C ) { 846 847 if (tt >= TEMPLATE_BRIDGE_3B) { 848 newtt = TEMPLATE_BRIDGE_3A; 849 } else { 850 if (tt < TEMPLATE_BRIDGE_2A) { 851 newtt = TEMPLATE_BRIDGE_1A; 852 } else { 853 newtt = TEMPLATE_BRIDGE_2A; 854 } 855 } 856 icon = cellptr->TIcon; 857 w = TemplateTypeClass::As_Reference(cellptr->TType).Width; 858 h = TemplateTypeClass::As_Reference(cellptr->TType).Height; 859 860 cell -= icon % w; 861 cell -= MAP_CELL_W * (icon / w); 862 } else { 863 doing = false; 864 } 865 } 866 Map.Zone_Reset(MZONEF_ALL); 867 delete this; 868 return; 869 } 870 } 871 } 872 } else { 873 #endif 874 if (!Target_Legal(NavCom)) { 875 Enter_Idle_Mode(); 876 if (Map[Coord].Cell_Building()) { 877 Scatter(0, true); 878 } 879 } 880 #ifdef OBSOLETE 881 } 882 #endif 883 } 884 } 885 886 /* 887 ** Infantry entering a transport vehicle will break radio contact 888 ** at attach itself to the transporter. 889 */ 890 TechnoClass * techno = Contact_With_Whom(); 891 if (Mission == MISSION_ENTER && techno != NULL && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { 892 if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { 893 Limbo(); 894 techno->Attach(this); 895 } 896 BEnd(BENCH_PCP); 897 return; 898 } 899 900 /* 901 ** If the infantry unit is entering a cell that contains the building it is trying to 902 ** sabotage, then sabotage it. 903 */ 904 if (Mission == MISSION_SABOTAGE) { 905 BuildingClass * building = cellptr->Cell_Building(); 906 if (building != NULL && building->As_Target() == NavCom) { 907 if (!building->IronCurtainCountDown && building->Mission != MISSION_DECONSTRUCTION) { 908 building->IsGoingToBlow = true; 909 building->Clicked_As_Target(PlayerPtr->Class->House, (Rule.C4Delay * TICKS_PER_MINUTE) / 2); // 2019/09/20 JAS - Added record of who clicked on the object 910 building->Clicked_As_Target(building->Owner(), (Rule.C4Delay * TICKS_PER_MINUTE) / 2); 911 building->CountDown = Rule.C4Delay * TICKS_PER_MINUTE; 912 building->WhomToRepay = As_Target(); 913 } 914 NavCom = TARGET_NONE; 915 Do_Uncloak(); 916 Arm = Rearm_Delay(true); 917 Scatter(building->Center_Coord(), true, true); // RUN AWAY! 918 BEnd(BENCH_PCP); 919 return; 920 } else { 921 if (::As_Target(Coord_Cell(Center_Coord())) == NavCom) { 922 Explosion_Damage(Coord, Rule.BridgeStrength, this, WARHEAD_HE); 923 924 Stop_Driver(); 925 Scatter(Adjacent_Cell(Coord, PrimaryFacing), true, true); 926 Assign_Mission(MISSION_MOVE); 927 928 CELL cell = Coord_Cell(Center_Coord()); 929 CellClass * cellptr = &Map[cell]; 930 if (!Target_Legal(NavCom) || Map[As_Cell(NavCom)].Land_Type() == LAND_WATER) { 931 Mark(MARK_DOWN); // Needed only so that Tanya will get destroyed by the explosion. 932 } 933 Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); 934 Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); 935 if (!IsActive) { 936 BEnd(BENCH_PCP); 937 return; 938 } 939 940 Mark(MARK_DOWN); 941 } 942 } 943 } 944 945 /* 946 ** If this unit is on a teather, then cut it at this time so that 947 ** the "parent" unit is free to proceed. Note that the parent 948 ** unit might actually be a building. 949 */ 950 if (IsTethered) { 951 Transmit_Message(RADIO_UNLOADED); 952 if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE) { 953 Do_Action(DO_GESTURE1); 954 } else { 955 Do_Action(DO_GESTURE2); 956 } 957 958 /* 959 ** Special voice play. 960 */ 961 if (*this == INFANTRY_TANYA) { 962 Sound_Effect(VOC_TANYA_LAUGH, Coord); 963 } 964 965 /* 966 ** If the cell is now full of infantry, tell them all to scatter 967 ** in order to make room for more. 968 */ 969 if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { 970 cellptr->Incoming(0, true, true); 971 // cellptr->Incoming(0, true); 972 } 973 } 974 975 /* 976 ** When the infantry reaches the center of the cell, it may begin a new mission. 977 */ 978 if (MissionQueue == MISSION_NONE && !Target_Legal(NavCom) && !Target_Legal(TarCom) && !In_Radio_Contact()) { 979 Enter_Idle_Mode(); 980 } 981 Commence(); 982 983 /* 984 ** If entering a cell with a land mine in it, blow up the mine. 985 */ 986 BuildingClass * bldng = cellptr->Cell_Building(); 987 if (bldng != NULL && *bldng == STRUCT_APMINE) { 988 /* 989 ** Show the animation and get rid of the land mine 990 */ 991 COORDINATE blcoord = bldng->Center_Coord(); 992 new AnimClass(Combat_Anim(Rule.APMineDamage, WARHEAD_HE, cellptr->Land_Type()), blcoord); 993 delete bldng; 994 int damage; 995 for (int index = 0; index < Infantry.Count(); index++) { 996 InfantryClass * obj = Infantry.Ptr(index); 997 if (obj != NULL && !obj->IsInLimbo) { 998 int dist = ::Distance(obj->Coord, blcoord); 999 if (dist <= 0xC0) { 1000 damage = Rule.APMineDamage; 1001 obj->Take_Damage(damage, 0, WARHEAD_HE); 1002 } 1003 } 1004 } 1005 if (!IsActive) { 1006 BEnd(BENCH_PCP); 1007 return; 1008 } 1009 } 1010 1011 /* 1012 ** If the last cell we looked from isn't adjacent to our current cell, 1013 ** perform a full look. 1014 */ 1015 CELL cell = Coord_Cell(Coord); 1016 if (::Distance(Cell_X(cell), Cell_Y(cell), Cell_X(LookCell), Cell_Y(LookCell)) > 1) { 1017 Look(false); 1018 } else { 1019 Look(true); 1020 } 1021 1022 #if 1 1023 /* 1024 ** If after all is said and done, the unit finishes its move on an impassable cell, then 1025 ** it must presume that it is in the case of a unit driving onto a bridge that blows up 1026 ** before the unit completes it's move. In such a case the unit should have been destroyed 1027 ** anyway, so blow it up now. 1028 */ 1029 LandType land = Map[Coord].Land_Type(); 1030 if (!IsDriving && !Class->IsBomber && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { 1031 int damage = Strength; 1032 Take_Damage(damage, 0, WARHEAD_AP, NULL, true); 1033 return; 1034 } 1035 #endif 1036 1037 } 1038 1039 if (IsActive) { 1040 FootClass::Per_Cell_Process(why); 1041 } 1042 BEnd(BENCH_PCP); 1043 } 1044 1045 1046 /*********************************************************************************************** 1047 * InfantryClass::Detach -- Removes the specified target from targeting computer. * 1048 * * 1049 * This is a support routine that removes the target specified from any targeting or * 1050 * navigation computers. When a target is destroyed or removed from the game system, * 1051 * the target must be removed from any tracking systems of the other units. This routine * 1052 * handles removal for infantry units. * 1053 * * 1054 * INPUT: target -- The target to remove from the infantry unit's tracking systems. * 1055 * * 1056 * all -- Is the target going away for good as opposed to just cloaking/hiding? * 1057 * * 1058 * OUTPUT: none * 1059 * * 1060 * WARNINGS: none * 1061 * * 1062 * HISTORY: * 1063 * 09/08/1994 JLB : Created. * 1064 *=============================================================================================*/ 1065 void InfantryClass::Detach(TARGET target, bool all) 1066 { 1067 assert(Infantry.ID(this) == ID); 1068 assert(IsActive); 1069 1070 if (TarCom == target) { 1071 Mark(MARK_OVERLAP_UP); 1072 IsFiring = false; 1073 Mark(MARK_OVERLAP_DOWN); 1074 } 1075 FootClass::Detach(target, all); 1076 } 1077 1078 1079 /*********************************************************************************************** 1080 * InfantryClass::Init -- Initialize the infantry object system. * 1081 * * 1082 * This routine will force the infantry object system into its empty initial state. It * 1083 * is called when the scenario needs to be cleared in preparation for a scenario load. * 1084 * * 1085 * INPUT: none * 1086 * * 1087 * OUTPUT: none * 1088 * * 1089 * WARNINGS: none * 1090 * * 1091 * HISTORY: * 1092 * 09/08/1994 JLB : Created. * 1093 *=============================================================================================*/ 1094 void InfantryClass::Init(void) 1095 { 1096 Infantry.Free_All(); 1097 } 1098 1099 1100 /*********************************************************************************************** 1101 * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * 1102 * * 1103 * This routine updates the infantry's navigation computer so that the infantry will * 1104 * travel to the destination target specified. * 1105 * * 1106 * INPUT: target -- The target to have the infantry unit move to. * 1107 * * 1108 * OUTPUT: none * 1109 * * 1110 * WARNINGS: none * 1111 * * 1112 * HISTORY: * 1113 * 09/08/1994 JLB : Created. * 1114 *=============================================================================================*/ 1115 void InfantryClass::Assign_Destination(TARGET target) 1116 { 1117 assert(Infantry.ID(this) == ID); 1118 assert(IsActive); 1119 1120 /* 1121 ** Special flag so that infantry will start heading in the right direction immediately. 1122 */ 1123 if (IsDriving && !IsFormationMove && Target_Legal(target) && Map[Center_Coord()].Is_Clear_To_Move(Class->Speed, true, false)) { 1124 Stop_Driver(); 1125 } 1126 1127 /* 1128 ** When telling an infantry soldier to move to a location twice, then this 1129 ** means that movement is more important than safety. Get up and run! 1130 */ 1131 if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat && !Class->IsDog) { 1132 Do_Action(DO_GET_UP); 1133 } 1134 1135 /* 1136 ** If telling a dog to attack a human, start the dog running 1137 */ 1138 TechnoClass * tech = As_Techno(target); 1139 1140 /* 1141 ** Handle entry logic here. 1142 */ 1143 if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { 1144 1145 /* 1146 ** If not already in radio contact (presumed with the transport), then 1147 ** either try to establish contact if allowed, or just move close and 1148 ** wait until radio contact can be established. 1149 */ 1150 if (!In_Radio_Contact()) { 1151 TechnoClass * techno = As_Techno(target); 1152 if (techno != NULL) { 1153 1154 /* 1155 ** Determine if the transport is already in radio contact. If so, then just move 1156 ** toward the transport and try to establish contact at a later time. 1157 */ 1158 if (techno->In_Radio_Contact()) { 1159 // TCTCTC -- call for an update from the transport to get a good rendezvous position. 1160 1161 ArchiveTarget = target; 1162 } else { 1163 if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { 1164 if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { 1165 Transmit_Message(RADIO_OVER_OUT); 1166 } else { 1167 //BG: keep retransmitted navcom from radio-move-here. 1168 return; 1169 } 1170 } 1171 } 1172 } 1173 } else { 1174 Path[0] = FACING_NONE; 1175 } 1176 } else { 1177 Path[0] = FACING_NONE; 1178 } 1179 FootClass::Assign_Destination(target); 1180 } 1181 1182 1183 /*********************************************************************************************** 1184 * InfantryClass::Assign_Target -- Gives the infantry a combat target. * 1185 * * 1186 * This routine will update the infantry's targeting computer so that it will try to * 1187 * attack the target specified. This might result in it moving to be within range and thus * 1188 * also cause adjustment of the navigation computer. * 1189 * * 1190 * INPUT: target -- The target that this infantry should attack. * 1191 * * 1192 * OUTPUT: none * 1193 * * 1194 * WARNINGS: none * 1195 * * 1196 * HISTORY: * 1197 * 09/08/1994 JLB : Created. * 1198 * 06/30/1995 JLB : Tries to capture target if possible. * 1199 *=============================================================================================*/ 1200 void InfantryClass::Assign_Target(TARGET target) 1201 { 1202 assert(Infantry.ID(this) == ID); 1203 assert(IsActive); 1204 1205 Path[0] = FACING_NONE; 1206 if (Class->IsDog) { 1207 if (::As_Object(target) && ::As_Object(target)->What_Am_I() != RTTI_INFANTRY) { 1208 target = TARGET_NONE; 1209 } 1210 } 1211 FootClass::Assign_Target(target); 1212 1213 /* 1214 ** If this is an infantry that can only capture, then also assign its destination to the 1215 ** target specified. 1216 */ 1217 if (!Target_Legal(NavCom) && Class->IsCapture && !Is_Weapon_Equipped()) { 1218 BuildingClass const * building = As_Building(target); 1219 if (building != NULL && building->Can_Capture()) { 1220 Assign_Destination(target); 1221 } 1222 } 1223 } 1224 1225 1226 /*********************************************************************************************** 1227 * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * 1228 * * 1229 * This routine is used to handle the non-graphic AI processing the infantry requires. * 1230 * Call this routine ONCE per game frame. * 1231 * * 1232 * INPUT: none * 1233 * * 1234 * OUTPUT: none * 1235 * * 1236 * WARNINGS: none * 1237 * * 1238 * HISTORY: * 1239 * 09/08/1994 JLB : Created. * 1240 * 08/14/1996 JLB : Simplified. * 1241 *=============================================================================================*/ 1242 void InfantryClass::AI(void) 1243 { 1244 assert(Infantry.ID(this) == ID); 1245 assert(IsActive); 1246 1247 FootClass::AI(); 1248 1249 if (!IsActive) { 1250 return; 1251 } 1252 1253 if (IsUnloading) Mark(MARK_CHANGE_REDRAW); 1254 1255 /* 1256 ** Infantry that are not on the ground should always be redrawn. Such is 1257 ** the case when they are parachuting to the ground. 1258 */ 1259 if (In_Which_Layer() != LAYER_GROUND) { 1260 Mark(MARK_CHANGE); 1261 } 1262 1263 /* 1264 ** Special hack to make sure that if this infantry is in firing animation, but the 1265 ** stage class isn't set, then abort the firing flag. 1266 */ 1267 if (IsFiring && Fetch_Rate() == 0) { 1268 Mark(MARK_OVERLAP_UP); 1269 IsFiring = false; 1270 Do_Action(DO_STAND_READY); 1271 Mark(MARK_OVERLAP_DOWN); 1272 } 1273 1274 /* 1275 ** Delete this unit if it finds itself off the edge of the map and it is in 1276 ** guard or other static mission mode. 1277 */ 1278 if (Edge_Of_World_AI()) { 1279 return; 1280 } 1281 1282 /* 1283 ** Act on new orders if the unit is at a good position to do so. 1284 */ 1285 if (!IsFiring && !IsFalling && !IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { 1286 if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) Enter_Idle_Mode(); 1287 Commence(); 1288 } 1289 1290 /* 1291 ** Special hack to make sure the dog never attacks a cell. 1292 */ 1293 if (Class->IsDog && Target_Legal(TarCom) && Is_Target_Cell(TarCom)) { 1294 Assign_Target(TARGET_NONE); 1295 } 1296 1297 /* 1298 ** Handle any infantry fear logic or related actions. 1299 */ 1300 Fear_AI(); 1301 1302 /* 1303 ** Special victory dance action. 1304 */ 1305 if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment == 0) { 1306 IsStoked = false; 1307 Do_Action(Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2); 1308 } 1309 1310 /* 1311 ** Determine if this infantry unit should fire off an 1312 ** attack or not. 1313 */ 1314 Firing_AI(); 1315 1316 /* 1317 ** Handle the completion of the animation sequence. 1318 */ 1319 Doing_AI(); 1320 1321 /* 1322 ** Perform movement operations at this time. 1323 */ 1324 Movement_AI(); 1325 } 1326 1327 1328 /*********************************************************************************************** 1329 * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * 1330 * * 1331 * This routine is used to examine the cell specified and determine if the infantry is * 1332 * allowed to enter it. It is used by the path finding algorithm. * 1333 * * 1334 * INPUT: cell -- The cell to examine. * 1335 * * 1336 * OUTPUT: Returns the type of blockage in the cell. * 1337 * * 1338 * WARNINGS: none * 1339 * * 1340 * HISTORY: * 1341 * 09/01/1994 JLB : Created. * 1342 *=============================================================================================*/ 1343 MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const 1344 { 1345 assert(Infantry.ID(this) == ID); 1346 assert(IsActive); 1347 1348 /* 1349 ** If we are moving into an illegal cell, then we can't do that. 1350 */ 1351 if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); 1352 1353 /* 1354 ** If moving off the edge of the map, then consider that an illegal move. 1355 */ 1356 if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { 1357 return(MOVE_NO); 1358 } 1359 1360 CellClass * cellptr = &Map[cell]; 1361 1362 /* 1363 ** Walls are considered impassable for infantry UNLESS the wall has a hole 1364 ** in it. 1365 */ 1366 if (cellptr->Overlay != OVERLAY_NONE) { 1367 OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); 1368 1369 if (otype.IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { 1370 return(MOVE_NO); 1371 } 1372 1373 if (otype.IsWall) { 1374 if ((cellptr->OverlayData / 16) != otype.DamageLevels) { 1375 1376 /* 1377 ** If the wall can be destroyed, then return this fact instead of 1378 ** a complete failure to enter. 1379 */ 1380 if (Is_Weapon_Equipped() && Class->PrimaryWeapon->Is_Wall_Destroyer()) { 1381 return(MOVE_DESTROYABLE); 1382 } 1383 return(MOVE_NO); 1384 } 1385 } 1386 } 1387 1388 /* 1389 ** Loop through all of the objects in the square setting a bit 1390 ** for how they affect movement. 1391 */ 1392 MoveType retval = MOVE_OK; 1393 ObjectClass * obj = cellptr->Cell_Occupier(); 1394 while (obj != NULL) { 1395 1396 if (obj != this) { 1397 1398 /* 1399 ** Always allow movement if the cell is the object to be captured or sabotaged. 1400 */ 1401 if (((Mission == MISSION_ENTER && In_Radio_Contact()) || Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && 1402 (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { 1403 1404 return(MOVE_OK); 1405 } 1406 1407 /* 1408 ** Guard area should not allow the guarding unit to enter the cell with the 1409 ** guarded unit. 1410 */ 1411 if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target() && Is_Target_Unit(ArchiveTarget)) { 1412 return(MOVE_NO); 1413 } 1414 1415 /* 1416 ** If object is a land mine, allow movement 1417 */ 1418 if (obj->What_Am_I() == RTTI_BUILDING) { 1419 if ((*(BuildingClass *)obj) == STRUCT_AVMINE) { 1420 obj = obj->Next; 1421 continue; 1422 } else { 1423 if (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House)) { 1424 if ((*(BuildingClass *)obj) == STRUCT_APMINE) { 1425 obj = obj->Next; 1426 continue; 1427 } 1428 } 1429 } 1430 } 1431 1432 /* 1433 ** Special case check so that a landed aircraft that is in radio contact, will not block 1434 ** a capture attempt. It is presumed that this case happens when a helicopter is landed 1435 ** at a helipad. 1436 */ 1437 // if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { 1438 1439 /* 1440 ** Special check to always allow entry into the building that this infantry 1441 ** is trying to capture. 1442 */ 1443 // if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT || obj->What_Am_I() == RTTI_UNIT) { 1444 // if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { 1445 // return(MOVE_OK); 1446 // } 1447 // } 1448 1449 /* 1450 ** Special check to always allow entry into the building that this infantry 1451 ** is trying to capture. 1452 */ 1453 if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { 1454 return(MOVE_OK); 1455 } 1456 1457 /* 1458 ** Allied objects block movement using different rules than for enemy 1459 ** objects. 1460 */ 1461 if (House->Is_Ally(obj) || ScenarioInit) { 1462 switch (obj->What_Am_I()) { 1463 1464 /* 1465 ** A unit blocks as either a moving blockage or a stationary temp blockage. 1466 ** This depends on whether the unit is currently moving or not. 1467 */ 1468 case RTTI_UNIT: 1469 if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { 1470 if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; 1471 } else { 1472 if (retval < MOVE_TEMP) retval = MOVE_TEMP; 1473 } 1474 break; 1475 1476 /* 1477 ** Aircraft and buildings always block movement. If for some reason there is an 1478 ** allied terrain object, that blocks movement as well. 1479 */ 1480 case RTTI_TERRAIN: 1481 case RTTI_AIRCRAFT: 1482 case RTTI_BUILDING: 1483 return(MOVE_NO); 1484 1485 default: 1486 break; 1487 } 1488 1489 } else { 1490 1491 /* 1492 ** Cloaked enemy objects are not considered if this is a Find_Path() 1493 ** call. 1494 */ 1495 if (!obj->Is_Techno() || !((TechnoClass *)obj)->Is_Cloaked(this)) { 1496 1497 /* 1498 ** Any non-allied blockage is considered impassible if the infantry 1499 ** is not equipped with a weapon. 1500 */ 1501 if (Combat_Damage() <= 0) return(MOVE_NO); 1502 1503 /* 1504 ** Some kinds of terrain are considered destroyable if the infantry is equipped 1505 ** with the weapon that can destroy it. Otherwise, the terrain is considered 1506 ** impassable. 1507 */ 1508 switch (obj->What_Am_I()) { 1509 case RTTI_TERRAIN: 1510 #ifdef OBSOLETE 1511 if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && 1512 Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { 1513 1514 if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; 1515 } else { 1516 return(MOVE_NO); 1517 } 1518 break; 1519 #else 1520 return(MOVE_NO); 1521 #endif 1522 case RTTI_INFANTRY: 1523 if ( *(InfantryClass *)obj == INFANTRY_SPY && !Class->IsDog) { 1524 retval = MOVE_TEMP; 1525 break; 1526 } 1527 // otherwise, fall thru. 1528 default: 1529 if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; 1530 break; 1531 } 1532 } else { 1533 if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; 1534 } 1535 } 1536 // } 1537 } 1538 1539 /* 1540 ** Move to next object in chain. 1541 */ 1542 obj = obj->Next; 1543 } 1544 1545 /* 1546 ** If foot soldiers cannot travel on the cell -- consider it impassable. 1547 */ 1548 if (retval == MOVE_OK && !IsTethered && Ground[cellptr->Land_Type()].Cost[SPEED_FOOT] == 0) { 1549 1550 #ifdef OBSOLETE 1551 /* 1552 ** Special case - if it's an engineer, and the cell under consideration 1553 ** is his NavCom, and his mission is mission_capture, then he's most 1554 ** likely moving to his final destination to repair a bridge, so we 1555 ** should let him. 1556 */ 1557 if (*this == INFANTRY_RENOVATOR && Is_Target_Cell(TarCom) && (cell == ::As_Cell(NavCom)) && (cellptr->TType == TEMPLATE_BRIDGE1D || cellptr->TType == TEMPLATE_BRIDGE2D || (cellptr->TType >= TEMPLATE_BRIDGE_1C && cellptr->TType <= TEMPLATE_BRIDGE_3E) ) ) { 1558 return(MOVE_OK); 1559 } 1560 #endif 1561 return(MOVE_NO); 1562 } 1563 1564 /* 1565 ** if a unit has the cell reserved then we just can't go in there. 1566 */ 1567 if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { 1568 return(MOVE_NO); 1569 } 1570 1571 /* 1572 ** if a block of infantry has the cell reserved then there are two 1573 ** possibilities... 1574 */ 1575 if (cellptr->InfType != HOUSE_NONE) { 1576 if (House->Is_Ally(cellptr->InfType)) { 1577 if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { 1578 if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; 1579 } 1580 } else { 1581 if (Combat_Damage() > 0) { 1582 if (retval < MOVE_DESTROYABLE) { 1583 retval = MOVE_DESTROYABLE; 1584 } 1585 } else { 1586 return(MOVE_NO); 1587 } 1588 } 1589 } 1590 1591 /* 1592 ** If it is still ok to move the infantry, then perform the last check 1593 ** to see if the cell is already full of infantry. 1594 */ 1595 if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { 1596 return(MOVE_NO); 1597 } 1598 1599 /* 1600 ** Return with the most severe reason why this cell would be impassable. 1601 */ 1602 return(retval); 1603 } 1604 1605 1606 /*********************************************************************************************** 1607 * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* 1608 * * 1609 * This is a rendering support routine that will return a pointer to a list of cell offsets * 1610 * that specify the cells the infantry unit is currently overlapping (graphic wise) but * 1611 * is not considered to occupy. This list is used to update the map display. * 1612 * * 1613 * INPUT: none * 1614 * * 1615 * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * 1616 * occupy. * 1617 * * 1618 * WARNINGS: none * 1619 * * 1620 * HISTORY: * 1621 * 09/01/1994 JLB : Created. * 1622 *=============================================================================================*/ 1623 #ifdef PARTIAL 1624 short const * InfantryClass::Overlap_List(bool redraw) const 1625 #else 1626 short const * InfantryClass::Overlap_List(bool ) const 1627 #endif 1628 { 1629 assert(Infantry.ID(this) == ID); 1630 assert(IsActive); 1631 1632 if (Class->IsDog) { 1633 return(Coord_Spillage_List(Coord, 24 + (Doing == DO_DOG_MAUL ? 40 : 0) + (Doing >= DO_GUN_DEATH && Doing <= DO_FIRE_DEATH ? 40 : 0) )); 1634 } else { 1635 1636 /* 1637 ** The default infantry rectangle will be as large as the largest shape the infantry 1638 ** can be. 1639 */ 1640 1641 #ifdef PARTIAL 1642 Rect rect(-16, -24, 32, 36); 1643 1644 /* 1645 ** If this is for a visual change redraw, then the overlap list will be based 1646 ** on the actual dimensions of the shape data. If the dimensions have already 1647 ** been calculated then use them, otherwise, use the default large rectangle 1648 ** previously created. 1649 */ 1650 if (Height == 0 && !Is_Selected_By_Player() && redraw && Class->DimensionData != NULL) { 1651 int shapenum = Shape_Number(); 1652 if (!Class->DimensionData[shapenum].Is_Valid()) { 1653 Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); 1654 } 1655 rect = Class->DimensionData[shapenum]; 1656 rect.Y += 4; 1657 rect.X -= 2; 1658 } 1659 return(Coord_Spillage_List(Coord, rect, true)); 1660 #else 1661 1662 static Rect rect(-16, -24, 32, 36); 1663 return(Coord_Spillage_List(Coord, rect, true)); 1664 #endif 1665 1666 // return(Coord_Spillage_List(Coord, 24 /*+ ((Doing > DO_WALK || IsSelected)?12:0)*/ )); 1667 } 1668 } 1669 1670 1671 /*********************************************************************************************** 1672 * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * 1673 * * 1674 * Determines if the infantry unit can fire on the target. If it can't fire, then the * 1675 * reason why is returned. * 1676 * * 1677 * INPUT: target -- The target to determine if the infantry can fire upon. * 1678 * * 1679 * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * 1680 * can't, why not. * 1681 * * 1682 * WARNINGS: none * 1683 * * 1684 * HISTORY: * 1685 * 09/01/1994 JLB : Created. * 1686 * 06/27/1995 JLB : Flame thrower can fire while prone now. * 1687 *=============================================================================================*/ 1688 FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const 1689 { 1690 assert(Infantry.ID(this) == ID); 1691 assert(IsActive); 1692 1693 /* 1694 ** Don't allow firing if the infantry is still firing on previous target. 1695 */ 1696 // if (IsFiring) return(FIRE_REARM); 1697 1698 /* 1699 ** If a medic is shooting at a healed target, let's declare the target 1700 ** illegal so he won't be constantly healing healed infantrymen. 1701 */ 1702 if (Combat_Damage() < 0) { 1703 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1704 TechnoClass * targ = As_Techno(target); 1705 #else 1706 InfantryClass * targ = As_Infantry(target); 1707 #endif 1708 if (targ == NULL || targ->Health_Ratio() >= Rule.ConditionGreen) { 1709 return(FIRE_ILLEGAL); 1710 } 1711 } 1712 1713 /* 1714 ** If this unit cannot fire while moving, then bail. 1715 */ 1716 if (IsDriving || (Target_Legal(NavCom) && Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { 1717 return(FIRE_MOVING); 1718 } 1719 1720 /* 1721 ** Only one dog can fire on an infantry at a time 1722 */ 1723 if (Class->IsDog) { 1724 for (int index = 0; index < Infantry.Count(); index++) { 1725 InfantryClass *dog = Infantry.Ptr(index); 1726 if (dog != this && dog->Class->IsDog && (dog->IsFiring || dog->IsInLimbo) && dog->TarCom == target) { 1727 return(FIRE_ILLEGAL); 1728 } 1729 } 1730 } 1731 1732 return(FootClass::Can_Fire(target, which)); 1733 } 1734 1735 1736 /*********************************************************************************************** 1737 * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * 1738 * * 1739 * This routine will determine the coordinate to use when this infantry fires. The * 1740 * coordinate is the location where bullets appear (or fire effects appear) when the * 1741 * object fires its weapon. * 1742 * * 1743 * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * 1744 * weapon, 1 means secondary weapon. * 1745 * * 1746 * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * 1747 * should appear. * 1748 * * 1749 * WARNINGS: none * 1750 * * 1751 * HISTORY: * 1752 * 11/12/2019 SKY : Created. * 1753 *=============================================================================================*/ 1754 COORDINATE InfantryClass::Fire_Coord(int which) const 1755 { 1756 COORDINATE coord = FootClass::Fire_Coord(which); 1757 1758 /* 1759 ** Since electric weapons draw a zap between start and end points, prone infantry that fire one 1760 ** need to adjust their fire coordinate so the start point looks correct. 1761 */ 1762 if (IsProne) { 1763 TechnoTypeClass const & tclass = *Techno_Type_Class(); 1764 WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; 1765 if (weapon && weapon->IsElectric) { 1766 coord = Coord_Add(coord, XY_Coord(0, 48)); 1767 } 1768 } 1769 1770 return coord; 1771 } 1772 1773 1774 /*********************************************************************************************** 1775 * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * 1776 * * 1777 * Use this routine when the infantry unit as accomplished its task and needs to find * 1778 * something to do. The default behavior is to enter some idle state such as guarding. * 1779 * * 1780 * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * 1781 * or is initially placed on the map? * 1782 * * 1783 * OUTPUT: none * 1784 * * 1785 * WARNINGS: none * 1786 * * 1787 * HISTORY: * 1788 * 09/01/1994 JLB : Created. * 1789 *=============================================================================================*/ 1790 void InfantryClass::Enter_Idle_Mode(bool ) 1791 { 1792 assert(Infantry.ID(this) == ID); 1793 assert(IsActive); 1794 1795 MissionType order = MISSION_GUARD; 1796 1797 if (Target_Legal(TarCom)) { 1798 order = MISSION_ATTACK; 1799 if (Mission == MISSION_SABOTAGE) { 1800 order = MISSION_SABOTAGE; 1801 } 1802 if (Mission == MISSION_CAPTURE) { 1803 order = MISSION_CAPTURE; 1804 } 1805 } else { 1806 1807 Handle_Navigation_List(); 1808 1809 if (Target_Legal(NavCom)) { 1810 order = MISSION_MOVE; 1811 if (Mission == MISSION_CAPTURE) { 1812 order = MISSION_CAPTURE; 1813 } 1814 if (Mission == MISSION_SABOTAGE) { 1815 order = MISSION_SABOTAGE; 1816 } 1817 } else { 1818 1819 if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsZombie || MissionControl[Mission].IsParalyzed) { 1820 return; 1821 } 1822 1823 if (Class->IsDog) { 1824 if (House->IsHuman || Team.Is_Valid()) { 1825 order = MISSION_GUARD; 1826 } else { 1827 order = MISSION_GUARD_AREA; 1828 ArchiveTarget = ::As_Target(Coord_Cell(Center_Coord())); 1829 } 1830 } else { 1831 if (House->IsHuman || Team.Is_Valid()) { 1832 order = MISSION_GUARD; 1833 } else { 1834 if (House->IQ < Rule.IQGuardArea) { 1835 order = MISSION_GUARD; 1836 } else { 1837 if (Is_Weapon_Equipped()) { 1838 order = MISSION_GUARD_AREA; 1839 } else { 1840 order = MISSION_GUARD; 1841 } 1842 } 1843 } 1844 } 1845 } 1846 } 1847 Assign_Mission(order); 1848 } 1849 1850 1851 /*********************************************************************************************** 1852 * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * 1853 * * 1854 * This routine is the random animator initiator for infantry units. This routine should * 1855 * be called regularly. On occasion, it will cause the infantry to go into an idle * 1856 * animation. * 1857 * * 1858 * INPUT: none * 1859 * * 1860 * OUTPUT: none * 1861 * * 1862 * WARNINGS: none * 1863 * * 1864 * HISTORY: * 1865 * 09/01/1994 JLB : Created. * 1866 * 12/13/1994 JLB : Does random facing change. * 1867 * 07/02/1995 JLB : Nikoomba special effects. * 1868 *=============================================================================================*/ 1869 bool InfantryClass::Random_Animate(void) 1870 { 1871 assert(Infantry.ID(this) == ID); 1872 assert(IsActive); 1873 1874 if (Is_Ready_To_Random_Animate()) { 1875 IdleTimer = Random_Pick(Rule.RandomAnimateTime * (TICKS_PER_MINUTE/2), Rule.RandomAnimateTime * (TICKS_PER_MINUTE*2)); 1876 1877 /* 1878 ** Scared infantry will always follow the golden rule of civilians; 1879 ** "When in darkness or in doubt, run in circles, scream, and shout!" 1880 */ 1881 if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { 1882 Scatter(NULL, true); 1883 return(true); 1884 } 1885 1886 switch (Random_Pick(0, 10)) { 1887 case 0: 1888 if (Class->IsDog) { 1889 Do_Action(DO_IDLE1); 1890 } 1891 break; 1892 1893 case 1: 1894 Do_Action(DO_SALUTE1); 1895 break; 1896 1897 case 2: 1898 Do_Action(DO_SALUTE2); 1899 break; 1900 1901 case 3: 1902 Do_Action(DO_GESTURE1); 1903 break; 1904 1905 case 4: 1906 Do_Action(DO_GESTURE2); 1907 break; 1908 1909 case 5: 1910 Do_Action(DO_IDLE1); 1911 break; 1912 1913 case 6: 1914 Mark(MARK_CHANGE_REDRAW); 1915 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1916 Mark(MARK_CHANGE_REDRAW); 1917 break; 1918 1919 case 7: 1920 Do_Action(DO_IDLE2); 1921 Mark(MARK_CHANGE_REDRAW); 1922 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1923 Mark(MARK_CHANGE_REDRAW); 1924 if (!Is_Selected_By_Player() && IsOwnedByPlayer && *this == INFANTRY_TANYA && Sim_Random_Pick(0, 2) == 0) { 1925 Sound_Effect(VOC_TANYA_SHAKE, Coord); 1926 } 1927 break; 1928 1929 /* 1930 ** On occasion, civilian types will wander about. 1931 */ 1932 case 8: 1933 Mark(MARK_CHANGE_REDRAW); 1934 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1935 Mark(MARK_CHANGE_REDRAW); 1936 if (!House->IsHuman && Class->IsFraidyCat) { 1937 Scatter(NULL, true); 1938 } 1939 break; 1940 1941 case 9: 1942 case 10: 1943 Mark(MARK_CHANGE_REDRAW); 1944 PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); 1945 Mark(MARK_CHANGE_REDRAW); 1946 1947 } 1948 return(true); 1949 } 1950 return(false); 1951 } 1952 1953 1954 /*********************************************************************************************** 1955 * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * 1956 * * 1957 * This routine is used when the infantry should scatter to a nearby cell. Scattering * 1958 * occurs as an occasional consequence of being fired upon. It is one of the features * 1959 * that makes infantry so "charming". * 1960 * * 1961 * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * 1962 * scatter. If the threat isn't from a particular direction, then this * 1963 * parameter will be NULL. * 1964 * * 1965 * forced -- The threat is real and a serious effort to scatter should be made. * 1966 * * 1967 * nokidding-- The scatter should affect the player's infantry even if it otherwise * 1968 * wouldn't have. * 1969 * * 1970 * OUTPUT: none * 1971 * * 1972 * WARNINGS: none * 1973 * * 1974 * HISTORY: * 1975 * 09/24/1994 JLB : Created. * 1976 * 12/12/1994 JLB : Flame thrower infantry always scatter. * 1977 * 08/02/1996 JLB : Added the nokidding parameter * 1978 *=============================================================================================*/ 1979 void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) 1980 { 1981 assert(Infantry.ID(this) == ID); 1982 assert(IsActive); 1983 1984 /* 1985 ** A unit that is in the process of going somewhere will never scatter. 1986 */ 1987 if (IsDriving) forced = false; 1988 1989 /* 1990 ** Certain missions prevent scattering regardless of whether it would be 1991 ** a good idea or not. 1992 */ 1993 if (!MissionControl[Mission].IsScatter && !forced) return; 1994 1995 /* 1996 ** If the infantry is currently engaged in legitimate combat, then don't 1997 ** scatter unless forced to. 1998 */ 1999 if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; 2000 2001 /* 2002 ** Don't scatter if performing an action that can't be interrupted. 2003 */ 2004 if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; 2005 2006 /* 2007 ** For human players, don't scatter the infantry, if the special 2008 ** flag has not been enabled that allows infantry scatter. 2009 */ 2010 if (!Rule.IsScatter && !nokidding && House->IsHuman && !forced && !Team.Is_Valid()) return; 2011 2012 if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { 2013 FacingType toface; 2014 2015 if (threat) { 2016 toface = Dir_Facing(Direction8(threat, Coord)); 2017 toface = toface + FacingType(Random_Pick(0, 4)-2); 2018 } else { 2019 COORDINATE coord = Coord_Fraction(Center_Coord()); 2020 2021 if (coord != 0x00800080L) { 2022 toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); 2023 } else { 2024 toface = Dir_Facing(PrimaryFacing.Current()); 2025 } 2026 toface = toface + FacingType(Random_Pick(0, 4)-2); 2027 } 2028 2029 CELL newcell = 0; 2030 CELL altcell = 0; 2031 FacingType face; 2032 for (face = FACING_N; face < FACING_COUNT; face++) { 2033 FacingType newface = toface + face; 2034 newcell = Adjacent_Cell(Coord_Cell(Coord), newface); 2035 2036 if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { 2037 if (altcell == 0) altcell = newcell; 2038 if (!Map[newcell].Is_Bridge_Here()) break; 2039 // Assign_Mission(MISSION_MOVE); 2040 // Assign_Destination(::As_Target(newcell)); 2041 } 2042 } 2043 if (face == FACING_COUNT) { 2044 newcell = 0; 2045 } 2046 2047 if (newcell == 0) { 2048 newcell = altcell; 2049 } 2050 2051 if (newcell != 0) { 2052 Assign_Mission(MISSION_MOVE); 2053 Assign_Destination(::As_Target(newcell)); 2054 } 2055 } 2056 } 2057 2058 2059 /*********************************************************************************************** 2060 * InfantryClass::Look -- Performs a look around (map reveal) action. * 2061 * * 2062 * This routine will reveal the map around this object. * 2063 * * 2064 * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * 2065 * If it is absolutely known that the object has only moved one * 2066 * cell from its previous location that it performed a Look() at, * 2067 * then set this parameter to TRUE. It will only perform the look * 2068 * check on the perimeter cells. * 2069 * * 2070 * OUTPUT: none * 2071 * * 2072 * WARNINGS: This routine is slow, try to call it only when necessary. * 2073 * * 2074 * HISTORY: * 2075 * 03/14/1996 JLB : Created. * 2076 *=============================================================================================*/ 2077 void InfantryClass::Look(bool incremental) 2078 { 2079 LookCell = Coord_Cell(Coord); 2080 FootClass::Look(incremental); 2081 } 2082 2083 2084 /*********************************************************************************************** 2085 * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * 2086 * * 2087 * This starts the infantry into a choreographed animation sequence. These sequences can * 2088 * be as simple as standing up or lying down, but can also be complex, such as dying or * 2089 * performing some idle animation. * 2090 * * 2091 * INPUT: todo -- The choreographed sequence to start. * 2092 * * 2093 * force -- Force starting this animation even if the current animation is flagged * 2094 * as uninterruptible. This is necessary for death animations. * 2095 * * 2096 * OUTPUT: bool; Was the animation started? * 2097 * * 2098 * WARNINGS: none * 2099 * * 2100 * HISTORY: * 2101 * 09/24/1994 JLB : Created. * 2102 *=============================================================================================*/ 2103 bool InfantryClass::Do_Action(DoType todo, bool force) 2104 { 2105 assert(Infantry.ID(this) == ID); 2106 assert(IsActive); 2107 2108 if (todo == DO_NOTHING || Class->DoControls[todo].Count == 0) { 2109 return(false); 2110 } 2111 2112 if (*this == INFANTRY_SPY && todo >= DO_GESTURE1) { 2113 todo = (DoType)(DO_IDLE1 + Random_Pick(0,1)); 2114 } 2115 2116 if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { 2117 Mark(MARK_OVERLAP_UP); 2118 Doing = todo; 2119 Mark(MARK_OVERLAP_DOWN); 2120 if (todo == DO_IDLE1 || todo == DO_IDLE2) { 2121 Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); 2122 } else { 2123 Set_Rate(MasterDoControls[Doing].Rate); 2124 } 2125 Set_Stage(0); 2126 2127 /* 2128 ** Kludge to make sure that if infantry is in the dying animation, it isn't still 2129 ** moving as well. 2130 */ 2131 if (Strength == 0) { 2132 Stop_Driver(); 2133 } 2134 2135 /* 2136 ** Make sure dogs don't try to go somewhere while they're mauling 2137 */ 2138 if (todo == DO_DOG_MAUL) { 2139 Stop_Driver(); 2140 Assign_Destination(TARGET_NONE); 2141 } 2142 2143 /* 2144 ** Since the animation sequence might be interrupted. Set any flags 2145 ** necessary so that if interrupted, the affect on the infantry is 2146 ** still accomplished. 2147 */ 2148 switch (todo) { 2149 case DO_LIE_DOWN: 2150 IsProne = true; 2151 break; 2152 2153 case DO_GET_UP: 2154 IsProne = false; 2155 break; 2156 2157 default: 2158 break; 2159 } 2160 2161 return(true); 2162 } 2163 2164 return(false); 2165 } 2166 2167 2168 /*********************************************************************************************** 2169 * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * 2170 * * 2171 * This is used to stop the infantry from animating in movement. This function will stop * 2172 * the infantry moving and revert it to either a prone or standing. * 2173 * * 2174 * INPUT: none * 2175 * * 2176 * OUTPUT: bool; Was the driving stopped? * 2177 * * 2178 * WARNINGS: none * 2179 * * 2180 * HISTORY: * 2181 * 09/24/1994 JLB : Created. * 2182 *=============================================================================================*/ 2183 bool InfantryClass::Stop_Driver(void) 2184 { 2185 assert(Infantry.ID(this) == ID); 2186 assert(IsActive); 2187 2188 if (Head_To_Coord()) { 2189 2190 /* 2191 ** Remove the "reservation" bit in the destination location. 2192 */ 2193 Clear_Occupy_Bit(Head_To_Coord()); 2194 } 2195 2196 /* 2197 ** Set the occupation bit at the current location. 2198 */ 2199 Set_Occupy_Bit(Coord); 2200 2201 if (Doing != DO_STAND_READY) { 2202 StopDriverFrame = Frame; 2203 } 2204 2205 if (Class->IsDog) { 2206 Do_Action(DO_STAND_READY); 2207 } else { 2208 if (IsProne) { 2209 Do_Action(DO_PRONE); 2210 } else { 2211 Do_Action(DO_STAND_READY); 2212 } 2213 } 2214 2215 if (Can_Enter_Cell(Coord_Cell(Coord)) == MOVE_OK) { 2216 IsZoneCheat = false; 2217 } else { 2218 IsZoneCheat = true; 2219 } 2220 2221 return(FootClass::Stop_Driver()); 2222 } 2223 2224 2225 /*********************************************************************************************** 2226 * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * 2227 * * 2228 * Use this routine to being the infantry moving toward the destination specified. The * 2229 * destination is first checked to see if there is a free spot available. Then the infantry * 2230 * reserves that spot and begins movement toward it. * 2231 * * 2232 * INPUT: headto -- The coordinate location desired for the infantry to head to. * 2233 * * 2234 * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * 2235 * the specified destination could not contain the infantry unit. * 2236 * * 2237 * WARNINGS: none * 2238 * * 2239 * HISTORY: * 2240 * 12/21/1994 JLB : Created. * 2241 * 05/14/1995 JLB : Tries to move to closest spot possible. * 2242 * 05/15/1995 JLB : Uses closest spot if moving onto transport. * 2243 *=============================================================================================*/ 2244 bool InfantryClass::Start_Driver(COORDINATE & headto) 2245 { 2246 assert(Infantry.ID(this) == ID); 2247 assert(IsActive); 2248 2249 COORDINATE old = headto; 2250 2251 /* 2252 ** Convert the head to coordinate to a legal sub-position location. 2253 */ 2254 headto = Map[headto].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); 2255 if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { 2256 headto = Map[old].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); 2257 } 2258 2259 /* 2260 ** If the infantry started moving, then fixup the occupation bits. 2261 */ 2262 if (headto && FootClass::Start_Driver(headto)) { 2263 if (!IsActive) return(false); 2264 2265 /* 2266 ** Remove the occupation bit from the infantry's current location. 2267 */ 2268 Clear_Occupy_Bit(Coord); 2269 2270 /* 2271 ** Set the occupation bit for the new headto location. 2272 */ 2273 Set_Occupy_Bit(headto); 2274 return(true); 2275 } 2276 2277 return(false); 2278 } 2279 2280 2281 /*********************************************************************************************** 2282 * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * 2283 * * 2284 * This routine will clean up the infantry occupation bits (as necessary) as well as stop * 2285 * the infantry movement process when it gets limboed. * 2286 * * 2287 * INPUT: none * 2288 * * 2289 * OUTPUT: bool; Was the infantry unit limboed? * 2290 * * 2291 * WARNINGS: none * 2292 * * 2293 * HISTORY: * 2294 * 12/22/1994 JLB : Created. * 2295 *=============================================================================================*/ 2296 bool InfantryClass::Limbo(void) 2297 { 2298 assert(Infantry.ID(this) == ID); 2299 assert(IsActive); 2300 2301 if (!IsInLimbo) { 2302 Stop_Driver(); 2303 2304 Clear_Occupy_Bit(Coord); 2305 } 2306 return(FootClass::Limbo()); 2307 } 2308 2309 2310 /*********************************************************************************************** 2311 * InfantryClass::Fire_At -- Fires projectile from infantry unit. * 2312 * * 2313 * Use this routine when the infantry unit wishes to fire a projectile. This routine * 2314 * will launch the projectile and perform any other necessary infantry specific operations. * 2315 * * 2316 * INPUT: target -- The target of the attack. * 2317 * * 2318 * which -- Which weapon to use for firing. 0=primary, 1=secondary. * 2319 * * 2320 * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * 2321 * NULL is returned. If there is already the maximum bullet objects in play, then * 2322 * this could happen. * 2323 * * 2324 * WARNINGS: none * 2325 * * 2326 * HISTORY: * 2327 * 12/26/1994 JLB : Created. * 2328 *=============================================================================================*/ 2329 BulletClass * InfantryClass::Fire_At(TARGET target, int which) 2330 { 2331 assert(Infantry.ID(this) == ID); 2332 assert(IsActive); 2333 2334 Mark(MARK_OVERLAP_UP); 2335 IsFiring = false; 2336 Mark(MARK_OVERLAP_DOWN); 2337 2338 BulletClass * bullet = FootClass::Fire_At(target, which); 2339 if (bullet != NULL && !IsInLimbo) { 2340 2341 /* 2342 ** For fraidycat infantry that run out of ammo, always go into 2343 ** a maximum fear state at that time. 2344 */ 2345 if (Class->IsFraidyCat && !Ammo) { 2346 Fear = FEAR_MAXIMUM; 2347 if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { 2348 Assign_Mission(MISSION_GUARD); 2349 } 2350 } 2351 } 2352 return(bullet); 2353 } 2354 2355 2356 /*********************************************************************************************** 2357 * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * 2358 * * 2359 * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * 2360 * ensure that the coordinate is a legal subposition. * 2361 * * 2362 * INPUT: coord -- The coordinate to unlimbo the infantry at. * 2363 * * 2364 * facing -- The desired initial facing for the infantry unit. * 2365 * * 2366 * strength -- The desired initial strength for the infantry unit. * 2367 * * 2368 * mission -- The desired initial mission for the infantry unit. * 2369 * * 2370 * OUTPUT: bool; Was the infantry unlimboed successfully? * 2371 * * 2372 * WARNINGS: none * 2373 * * 2374 * HISTORY: * 2375 * 12/26/1994 JLB : Created. * 2376 *=============================================================================================*/ 2377 bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) 2378 { 2379 assert(Infantry.ID(this) == ID); 2380 assert(IsActive); 2381 2382 /* 2383 ** Make sure that the infantry start in a legal position on the map. 2384 */ 2385 coord = Map[coord].Closest_Free_Spot(coord, ScenarioInit); 2386 if (coord == NULL) { 2387 return(false); 2388 } 2389 2390 if (FootClass::Unlimbo(coord, facing)) { 2391 2392 /* 2393 ** Ensure that the owning house knows about the 2394 ** new object. 2395 */ 2396 House->IScan |= (1L << Class->Type); 2397 House->ActiveIScan |= (1L << Class->Type); 2398 2399 /* 2400 ** If there is no sight range, then this object isn't discovered by the player unless 2401 ** it actually appears in a cell mapped by the player. 2402 */ 2403 if (Class->SightRange == 0) { 2404 IsDiscoveredByPlayer = false; 2405 } 2406 2407 Set_Occupy_Bit(coord); 2408 return(true); 2409 } 2410 return(false); 2411 } 2412 2413 2414 /*********************************************************************************************** 2415 * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * 2416 * * 2417 * This routine intercepts the Greatest_Threat request and adds the appropriate target * 2418 * types to search for. For regular infantry, this consists of all the ground types. For * 2419 * rocket launching infantry, this also includes aircraft. * 2420 * * 2421 * INPUT: threat -- The basic threat control value. * 2422 * * 2423 * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * 2424 * target could be found, then TARGET_NONE is returned. * 2425 * * 2426 * WARNINGS: none * 2427 * * 2428 * HISTORY: * 2429 * 01/01/1995 JLB : Created. * 2430 * 09/28/1995 JLB : Engineers try to recapture buildings first. * 2431 *=============================================================================================*/ 2432 TARGET InfantryClass::Greatest_Threat(ThreatType threat) const 2433 { 2434 assert(Infantry.ID(this) == ID); 2435 assert(IsActive); 2436 2437 /* 2438 ** Engineers consider only buildings that can be captured as being a threat. All others 2439 ** are ignored. If there is a building that needs to be recaptured and it is nearby 2440 ** then automatically head toward it to recapture it. 2441 */ 2442 if (!House->IsHuman && Class->IsCapture && !Is_Weapon_Equipped()) { 2443 if (House->ToCapture != TARGET_NONE && Distance(House->ToCapture) < 0x0F00) { 2444 return(House->ToCapture); 2445 } 2446 threat = threat | THREAT_CAPTURE; 2447 } 2448 2449 if (!Is_Weapon_Equipped()) { 2450 if (!Class->IsCapture && *this != INFANTRY_RENOVATOR && *this != INFANTRY_SPY && *this != INFANTRY_THIEF) { 2451 return(TARGET_NONE); 2452 } 2453 } 2454 2455 /* 2456 ** Special hack to make Tanya not auto-fire if controlled by a 2457 ** human player. 2458 */ 2459 if (*this == INFANTRY_TANYA && House->IsHuman) { 2460 return(TARGET_NONE); 2461 } 2462 2463 if (Class->PrimaryWeapon != NULL) { 2464 threat = threat | Class->PrimaryWeapon->Allowed_Threats(); 2465 } 2466 if (Class->SecondaryWeapon != NULL) { 2467 threat = threat | Class->SecondaryWeapon->Allowed_Threats(); 2468 } 2469 2470 /* 2471 ** Organic weapon types don't consider anything but infantry to be a threat. Such 2472 ** weapon types would be the dog jaw and the medic first aid kit. 2473 */ 2474 if (Is_Weapon_Equipped() && Class->PrimaryWeapon->WarheadPtr->IsOrganic) { 2475 threat = threat & ~(THREAT_BUILDINGS|THREAT_VEHICLES|THREAT_BOATS|THREAT_AIR); 2476 } 2477 2478 /* 2479 ** Human controlled infantry don't automatically fire upon buildings. 2480 */ 2481 if (Is_Weapon_Equipped() && House->IsHuman) { 2482 threat = threat & ~THREAT_BUILDINGS; 2483 } 2484 2485 /* 2486 ** If this is a bomber type, then allow buildings to be considered a threat. 2487 */ 2488 if (Class->IsBomber && !House->IsHuman) { 2489 threat = threat | THREAT_BUILDINGS; 2490 } 2491 2492 /* 2493 ** Special hack: if it's a thief, then the only possible objects to 2494 ** consider are tiberium-processing objects (silos & refineries). 2495 */ 2496 if (*this == INFANTRY_THIEF) { 2497 threat = threat | THREAT_CAPTURE | THREAT_TIBERIUM; 2498 // threat = (ThreatType)(THREAT_CAPTURE | THREAT_TIBERIUM); 2499 } 2500 return(FootClass::Greatest_Threat(threat)); 2501 } 2502 2503 2504 /*********************************************************************************************** 2505 * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * 2506 * * 2507 * This routine handles playing an audio response as a result of the player selecting the * 2508 * infantry unit. This occurs prior to giving it an order and may not be followed by any * 2509 * order at all. * 2510 * * 2511 * INPUT: none * 2512 * * 2513 * OUTPUT: none * 2514 * * 2515 * WARNINGS: none * 2516 * * 2517 * HISTORY: * 2518 * 01/01/1995 JLB : Created. * 2519 * 05/05/1995 JLB : Rambo response types added. * 2520 *=============================================================================================*/ 2521 void InfantryClass::Response_Select(void) 2522 { 2523 assert(Infantry.ID(this) == ID); 2524 assert(IsActive); 2525 2526 if (!AllowVoice) return; 2527 2528 if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { 2529 VocType response = VOC_NONE; 2530 if (Class->IsFemale) { 2531 response = VOC_GIRL_YEAH; 2532 } else { 2533 response = VOC_GUY_YEAH; 2534 } 2535 Sound_Effect(response, fixed(1), ID+1); 2536 2537 } else { 2538 static VocType _eng_response[] = {VOC_ENG_YES,VOC_ENG_ENG}; 2539 static VocType _ein_response[] = {VOC_E_AH}; 2540 static VocType _dog_response[] = {VOC_DOG_YES}; 2541 static VocType _spy_response[] = {VOC_SPY_COMMANDER,VOC_SPY_YESSIR}; 2542 static VocType _medic_response[] = {VOC_MED_REPORTING,VOC_MED_YESSIR}; 2543 static VocType _tanya_response[] = {VOC_TANYA_YEA,VOC_TANYA_YES,VOC_TANYA_WHATS}; 2544 static VocType _thief_response[] = {VOC_THIEF_YEA,VOC_THIEF_WHAT}; 2545 static VocType _default_response[] = {VOC_ACKNOWL,VOC_REPORT,VOC_REPORT,VOC_YESSIR,VOC_YESSIR,VOC_READY,VOC_AWAIT}; 2546 static VocType _stavros[] = {VOC_STAVCMDR,VOC_STAVYES}; 2547 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2548 static VocType _mechanic_response[] = {VOC_MECHHOWDY1,VOC_MECHHUH1,VOC_MECHLAFF1}; 2549 static VocType _shock_response[] = {VOC_STYES1,VOC_STJUMP1,VOC_STJUICE1}; 2550 #endif 2551 2552 int size = 0; 2553 VocType * response = NULL; 2554 HousesType house = PlayerPtr->ActLike; 2555 switch (Class->Type) { 2556 case INFANTRY_GENERAL: 2557 if (house != HOUSE_USSR && house != HOUSE_BAD) { 2558 response = _stavros; 2559 size = ARRAY_SIZE(_stavros); 2560 } else { 2561 response = _default_response; 2562 size = ARRAY_SIZE(_default_response); 2563 } 2564 house = HOUSE_USSR; 2565 break; 2566 2567 case INFANTRY_DOG: 2568 response = _dog_response; 2569 size = ARRAY_SIZE(_dog_response); 2570 break; 2571 2572 case INFANTRY_EINSTEIN: 2573 response = _ein_response; 2574 size = ARRAY_SIZE(_ein_response); 2575 break; 2576 2577 case INFANTRY_SPY: 2578 response = _spy_response; 2579 size = ARRAY_SIZE(_spy_response); 2580 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2581 if(house == HOUSE_USSR) { 2582 response = _default_response; 2583 size = ARRAY_SIZE(_default_response); 2584 } 2585 #endif 2586 break; 2587 2588 case INFANTRY_MEDIC: 2589 response = _medic_response; 2590 size = ARRAY_SIZE(_medic_response); 2591 break; 2592 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2593 case INFANTRY_MECHANIC: 2594 response = _mechanic_response; 2595 size = ARRAY_SIZE(_mechanic_response); 2596 break; 2597 case INFANTRY_SHOCK: 2598 response = _shock_response; 2599 size = ARRAY_SIZE(_shock_response); 2600 break; 2601 #endif 2602 case INFANTRY_TANYA: 2603 response = _tanya_response; 2604 size = ARRAY_SIZE(_tanya_response); 2605 break; 2606 2607 case INFANTRY_THIEF: 2608 response = _thief_response; 2609 size = ARRAY_SIZE(_thief_response); 2610 break; 2611 2612 case INFANTRY_RENOVATOR: 2613 response = _eng_response; 2614 size = ARRAY_SIZE(_eng_response); 2615 break; 2616 2617 default: 2618 response = _default_response; 2619 size = ARRAY_SIZE(_default_response); 2620 break; 2621 } 2622 if (response != NULL) { 2623 Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); 2624 } 2625 } 2626 } 2627 2628 2629 /*********************************************************************************************** 2630 * InfantryClass::Response_Move -- Plays infantry response to movement order. * 2631 * * 2632 * When the infantry is given the order to move, this routine handles the audio response * 2633 * generated by the infantry unit. * 2634 * * 2635 * INPUT: none * 2636 * * 2637 * OUTPUT: none * 2638 * * 2639 * WARNINGS: none * 2640 * * 2641 * HISTORY: * 2642 * 01/01/1995 JLB : Created. * 2643 * 05/05/1995 JLB : Rambo response types added. * 2644 *=============================================================================================*/ 2645 void InfantryClass::Response_Move(void) 2646 { 2647 assert(Infantry.ID(this) == ID); 2648 assert(IsActive); 2649 2650 if (!AllowVoice) return; 2651 2652 if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { 2653 VocType response; 2654 if (Class->IsFemale) { 2655 response = VOC_GIRL_OKAY; 2656 } else { 2657 response = VOC_GUY_OKAY; 2658 } 2659 Sound_Effect(response, fixed(1), ID+1); 2660 2661 } else { 2662 static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; 2663 static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; 2664 static VocType _dog_response[] = {VOC_DOG_BARK}; 2665 static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; 2666 static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; 2667 #ifdef ENGLISH 2668 static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_ROCK}; 2669 #else 2670 static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_GIVE}; 2671 #endif 2672 static VocType _thief_response[] = {VOC_THIEF_MOVEOUT,VOC_THIEF_OKAY,VOC_THIEF_AFFIRM}; 2673 static VocType _default_response[] = {VOC_ROGER,VOC_RIGHT_AWAY,VOC_UGOTIT,VOC_AFFIRM,VOC_AFFIRM}; 2674 static VocType _stavros[] = {VOC_STAVMOV,VOC_STAVCRSE}; 2675 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2676 static VocType _mechanic[] = {VOC_MECHYES1,VOC_MECHRISE1,VOC_MECHHEAR1,VOC_MECHBOSS1}; 2677 static VocType _shock[] = {VOC_STPOWER1,VOC_STDANCE1,VOC_STCHRGE1}; 2678 #endif 2679 2680 int size = 0; 2681 VocType * response = NULL; 2682 HousesType house = PlayerPtr->ActLike; 2683 switch (Class->Type) { 2684 case INFANTRY_GENERAL: 2685 if (house != HOUSE_USSR && house != HOUSE_BAD) { 2686 response = _stavros; 2687 size = ARRAY_SIZE(_stavros); 2688 } else { 2689 response = _default_response; 2690 size = ARRAY_SIZE(_default_response); 2691 } 2692 house = HOUSE_USSR; 2693 break; 2694 2695 case INFANTRY_DOG: 2696 response = _dog_response; 2697 size = ARRAY_SIZE(_dog_response); 2698 break; 2699 2700 case INFANTRY_EINSTEIN: 2701 response = _ein_response; 2702 size = ARRAY_SIZE(_ein_response); 2703 break; 2704 2705 case INFANTRY_RENOVATOR: 2706 response = _eng_response; 2707 size = ARRAY_SIZE(_eng_response); 2708 break; 2709 2710 case INFANTRY_SPY: 2711 response = _spy_response; 2712 size = ARRAY_SIZE(_spy_response); 2713 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2714 if(house == HOUSE_USSR) { 2715 response = _default_response; 2716 size = ARRAY_SIZE(_default_response); 2717 } 2718 #endif 2719 break; 2720 2721 case INFANTRY_MEDIC: 2722 response = _medic_response; 2723 size = ARRAY_SIZE(_medic_response); 2724 break; 2725 2726 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2727 case INFANTRY_MECHANIC: 2728 response = _mechanic; 2729 size = ARRAY_SIZE(_mechanic); 2730 break; 2731 2732 case INFANTRY_SHOCK: 2733 response = _shock; 2734 size = ARRAY_SIZE(_shock); 2735 break; 2736 2737 #endif 2738 case INFANTRY_TANYA: 2739 response = _tanya_response; 2740 size = ARRAY_SIZE(_tanya_response); 2741 break; 2742 2743 case INFANTRY_THIEF: 2744 response = _thief_response; 2745 size = ARRAY_SIZE(_thief_response); 2746 break; 2747 2748 default: 2749 response = _default_response; 2750 size = ARRAY_SIZE(_default_response); 2751 break; 2752 } 2753 if (response != NULL) { 2754 Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); 2755 } 2756 } 2757 } 2758 2759 2760 /*********************************************************************************************** 2761 * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * 2762 * * 2763 * When the player gives an infantry unit the order to attack, this routine handles * 2764 * the audio response by that unit. * 2765 * * 2766 * INPUT: none * 2767 * * 2768 * OUTPUT: none * 2769 * * 2770 * WARNINGS: none * 2771 * * 2772 * HISTORY: * 2773 * 01/01/1995 JLB : Created. * 2774 * 05/05/1995 JLB : Rambo response types added. * 2775 *=============================================================================================*/ 2776 void InfantryClass::Response_Attack(void) 2777 { 2778 assert(Infantry.ID(this) == ID); 2779 assert(IsActive); 2780 2781 if (!AllowVoice) return; 2782 2783 if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { 2784 VocType response; 2785 if (Class->IsFemale) { 2786 response = VOC_GIRL_OKAY; 2787 } else { 2788 response = VOC_GUY_OKAY; 2789 } 2790 Sound_Effect(response, fixed(1), ID+1); 2791 2792 } else { 2793 static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; 2794 static VocType _dog_response[] = {VOC_DOG_GROWL2}; 2795 static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; 2796 static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; 2797 static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; 2798 #ifdef ENGLISH 2799 static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH}; 2800 #else 2801 static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH,VOC_TANYA_ROCK}; 2802 #endif 2803 static VocType _thief_response[] = {VOC_NONE}; 2804 static VocType _default_response[] = {VOC_RIGHT_AWAY,VOC_AFFIRM,VOC_AFFIRM,VOC_UGOTIT,VOC_NO_PROB,VOC_YESSIR,VOC_YESSIR,VOC_YESSIR}; 2805 static VocType _stavros[] = {VOC_STAVCRSE}; 2806 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2807 static VocType _mechanic[] = {VOC_MECHYEEHAW1,VOC_MECHHOTDIG1,VOC_MECHWRENCH1}; 2808 static VocType _shock[] = {VOC_STLIGHT1,VOC_STBURN1,VOC_STCRISP1,VOC_STSHOCK1}; 2809 #endif 2810 2811 int size = 0; 2812 VocType * response = NULL; 2813 HousesType house = PlayerPtr->ActLike; 2814 switch (Class->Type) { 2815 case INFANTRY_GENERAL: 2816 if (house != HOUSE_USSR && house != HOUSE_BAD) { 2817 response = _stavros; 2818 size = ARRAY_SIZE(_stavros); 2819 } else { 2820 response = _default_response; 2821 size = ARRAY_SIZE(_default_response); 2822 } 2823 house = HOUSE_USSR; 2824 break; 2825 2826 case INFANTRY_DOG: 2827 response = _dog_response; 2828 size = ARRAY_SIZE(_dog_response); 2829 break; 2830 2831 case INFANTRY_SPY: 2832 response = _spy_response; 2833 size = ARRAY_SIZE(_spy_response); 2834 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2835 if(house == HOUSE_USSR) { 2836 response = _default_response; 2837 size = ARRAY_SIZE(_default_response); 2838 } 2839 #endif 2840 break; 2841 2842 case INFANTRY_EINSTEIN: 2843 response = _ein_response; 2844 size = ARRAY_SIZE(_ein_response); 2845 break; 2846 2847 case INFANTRY_RENOVATOR: 2848 response = _eng_response; 2849 size = ARRAY_SIZE(_eng_response); 2850 break; 2851 2852 case INFANTRY_MEDIC: 2853 response = _medic_response; 2854 size = ARRAY_SIZE(_medic_response); 2855 break; 2856 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2857 case INFANTRY_MECHANIC: 2858 response = _mechanic; 2859 size = ARRAY_SIZE(_mechanic); 2860 break; 2861 2862 case INFANTRY_SHOCK: 2863 response = _shock; 2864 size = ARRAY_SIZE(_shock); 2865 break; 2866 #endif 2867 case INFANTRY_TANYA: 2868 response = _tanya_response; 2869 size = ARRAY_SIZE(_tanya_response); 2870 break; 2871 2872 case INFANTRY_THIEF: 2873 response = _thief_response; 2874 size = ARRAY_SIZE(_thief_response); 2875 break; 2876 2877 default: 2878 response = _default_response; 2879 size = ARRAY_SIZE(_default_response); 2880 break; 2881 } 2882 if (response != NULL) { 2883 Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); 2884 } 2885 } 2886 } 2887 2888 2889 /*********************************************************************************************** 2890 * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * 2891 * * 2892 * This routine checks to see if the infantry unit can capture the specified object rather * 2893 * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * 2894 * * 2895 * INPUT: object -- The object that the mouse is currently over. * 2896 * * 2897 * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * 2898 * object specified. * 2899 * * 2900 * WARNINGS: none * 2901 * * 2902 * HISTORY: * 2903 * 03/01/1995 JLB : Created. * 2904 *=============================================================================================*/ 2905 ActionType InfantryClass::What_Action(ObjectClass const * object) const 2906 { 2907 assert(Infantry.ID(this) == ID); 2908 assert(IsActive); 2909 assert(object != NULL); 2910 2911 ActionType action = FootClass::What_Action(object); 2912 2913 /* 2914 ** If this is an engineer/renovator, we have to make some adjustments. 2915 ** If the cursor is over an enemy building, return action-none. If it's 2916 ** over a friendly building, we have to return action-capture so he can 2917 ** renovate it. 2918 ** However, abort the whole thing if the building is a barrel or mine. 2919 */ 2920 if (*this == INFANTRY_RENOVATOR && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { 2921 BuildingClass const * bldg = (BuildingClass *)object; 2922 if (bldg->Class->IsRepairable) { 2923 if (House->Is_Ally(bldg)) { 2924 if (bldg->Health_Ratio() == 1) { 2925 return(ACTION_NO_GREPAIR); 2926 } 2927 return(ACTION_GREPAIR); 2928 } else { 2929 2930 if (bldg->Can_Capture()) { 2931 #ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 2932 if (bldg->Health_Ratio() <= EngineerCaptureLevel) { 2933 #else 2934 if (bldg->Health_Ratio() <= Rule.ConditionRed) { 2935 #endif 2936 return(ACTION_CAPTURE); 2937 } 2938 return(ACTION_DAMAGE); 2939 } 2940 2941 // if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Can_Capture()) { 2942 } 2943 } 2944 } 2945 2946 /* 2947 ** If this is a medic, and the cursor's over a friendly infantryman, 2948 ** execute an action-attack. In CSII, if this is a mechanic and the 2949 ** cursor's over a friendly vehicle, execute an action-attack. 2950 */ 2951 if (Combat_Damage() < 0 && House->IsPlayerControl) { 2952 if (House->Is_Ally(object)) { 2953 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2954 if( (object->What_Am_I() == RTTI_INFANTRY && object != this && *this == INFANTRY_MEDIC) || 2955 (*this == INFANTRY_MECHANIC && (object->What_Am_I() == RTTI_UNIT || object->What_Am_I() == RTTI_AIRCRAFT) ) ) { 2956 2957 if (object->Health_Ratio() < Rule.ConditionGreen) { 2958 // If it's a mechanic force-moving into an APC, don't try to heal it. 2959 if(*this == INFANTRY_MECHANIC && object->What_Am_I() == RTTI_UNIT && *(UnitClass *)object == UNIT_APC && (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)) ) { 2960 } else { 2961 return(ACTION_HEAL); 2962 } 2963 } 2964 } 2965 #else 2966 if(object->What_Am_I() == RTTI_INFANTRY && object != this) { 2967 if (object->Health_Ratio() < Rule.ConditionGreen) { 2968 return(ACTION_HEAL); 2969 } 2970 } 2971 #endif 2972 if(!object->Is_Techno() || !((TechnoClass *)object)->Techno_Type_Class()->Max_Passengers()) { 2973 if (action == ACTION_GUARD_AREA || action == ACTION_MOVE) { 2974 return(action); 2975 } 2976 return ((action == ACTION_TOGGLE_SELECT) ? ACTION_TOGGLE_SELECT : ACTION_SELECT); 2977 } 2978 } else { 2979 return(ACTION_NOMOVE); 2980 } 2981 } 2982 2983 #ifdef OBSOLETE 2984 /* 2985 ** See if it's a thief attacking an enemy vehicle, let him CAPTURE it. 2986 */ 2987 if (*this == INFANTRY_THIEF && object->What_Am_I() == RTTI_UNIT) { 2988 if (((UnitClass *)object)->House != House) { 2989 return(ACTION_CAPTURE); 2990 } 2991 } 2992 #endif 2993 2994 /* 2995 ** Dogs can only attack infantrymen 2996 */ 2997 if (Class->IsDog && action == ACTION_ATTACK && object->What_Am_I() != RTTI_INFANTRY) { 2998 action = ACTION_NONE; 2999 } 3000 3001 /* 3002 ** See if it's a commando, and if he's attacking a building, 3003 ** have him return ACTION_SABOTAGE instead 3004 */ 3005 if (Class->IsBomber && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { 3006 BuildingClass const * obj = (BuildingClass *)object; 3007 /* 3008 ** Hack: Tanya should shoot barrels, bomb other structures. 3009 */ 3010 if (obj->Class->IsRepairable) { 3011 // if (*obj != STRUCT_BARREL && *obj != STRUCT_BARREL3) { 3012 return(ACTION_SABOTAGE); 3013 } else { 3014 return(ACTION_ATTACK); 3015 } 3016 } 3017 3018 /* 3019 ** See if this infantry is trying to move onto where a land mine is. 3020 */ 3021 if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { 3022 StructType blah = *((BuildingClass *)object); 3023 if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); 3024 } 3025 3026 /* 3027 ** There is no self-select action available for infantry types. 3028 */ 3029 if (action == ACTION_SELF) { 3030 action = ACTION_NONE; 3031 } 3032 3033 /* 3034 ** Check to see if it can enter a transporter. 3035 */ 3036 if ( 3037 House->Is_Ally(object) && 3038 House->IsPlayerControl && object->Is_Techno()) { 3039 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 3040 if (object->What_Am_I() != RTTI_VESSEL || *(VesselClass *)object != VESSEL_CARRIER) { 3041 #endif 3042 switch (((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { 3043 case RADIO_ROGER: 3044 action = ACTION_ENTER; 3045 break; 3046 3047 case RADIO_NEGATIVE: 3048 action = ACTION_NO_ENTER; 3049 break; 3050 3051 default: 3052 break; 3053 } 3054 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 3055 } 3056 #endif 3057 } 3058 3059 if (Class->IsCapture && action == ACTION_ATTACK) { 3060 if (!House->Is_Ally(object) && ( 3061 //Disable capturing of helicopters (object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || 3062 (object->What_Am_I() == RTTI_BUILDING && object->Can_Capture()) ) 3063 ) { 3064 3065 if (*this == INFANTRY_THIEF && (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->Capacity == 0)) { 3066 action = ACTION_NONE; 3067 } else { 3068 3069 /* 3070 ** If we're trying to capture a building, make sure we can get 3071 ** to it. Find an adjacent cell that's the same zone as us. 3072 ** The target circumstance is a naval yard that doesn't touch 3073 ** the shore - a total island. In that case, we can't capture 3074 ** it, so we shouldn't show the action-capture cursor. 3075 */ 3076 action = ACTION_CAPTURE; 3077 if (object->What_Am_I() == RTTI_BUILDING) { 3078 CELL cell = ::As_Cell(object->As_Target()); 3079 int targzone = Map[::As_Cell(As_Target())].Zones[Class->MZone]; 3080 short const *list = ((BuildingClass *)object)->Class->Occupy_List(false); 3081 bool found = false; 3082 while (*list != REFRESH_EOL && !found) { 3083 CELL newcell = cell + *list++; 3084 for (FacingType i=FACING_N; i < FACING_COUNT; i++) { 3085 CELL adjcell = Adjacent_Cell(newcell, i); 3086 if ((unsigned)adjcell >= MAP_CELL_TOTAL) continue; 3087 if (Map[adjcell].Zones[Class->MZone] == targzone) { 3088 found = true; 3089 break; 3090 } 3091 } 3092 } 3093 if (!found) { 3094 action = ACTION_NONE; 3095 } 3096 } 3097 } 3098 } else { 3099 if (!Is_Weapon_Equipped()) { 3100 action = ACTION_NONE; 3101 } 3102 } 3103 } 3104 3105 /* 3106 ** If it doesn't know what to do with the object, then just 3107 ** say it can't move there. 3108 */ 3109 if (action == ACTION_NONE) action = ACTION_NOMOVE; 3110 3111 return(action); 3112 } 3113 3114 3115 /*********************************************************************************************** 3116 * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * 3117 * * 3118 * This routine is called when the player clicks over an object while this infantry soldier * 3119 * is selected. Capture attempts are prohibited if the infantry cannot capture. The * 3120 * command might respond if told to sabotage something. * 3121 * * 3122 * INPUT: action -- The action that is nominally to be performed. * 3123 * * 3124 * object -- The object over which the mouse was clicked. * 3125 * * 3126 * OUTPUT: none * 3127 * * 3128 * WARNINGS: none * 3129 * * 3130 * HISTORY: * 3131 * 05/08/1995 JLB : Created. * 3132 *=============================================================================================*/ 3133 void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) 3134 { 3135 assert(Infantry.ID(this) == ID); 3136 assert(IsActive); 3137 3138 action = What_Action(object); 3139 3140 switch (action) { 3141 case ACTION_GREPAIR: 3142 case ACTION_DAMAGE: 3143 case ACTION_CAPTURE: 3144 action = ACTION_CAPTURE; 3145 break; 3146 3147 case ACTION_HEAL: 3148 action = ACTION_ATTACK; 3149 break; 3150 3151 // case ACTION_ENTER: 3152 // action = ACTION_MOVE; 3153 // break; 3154 3155 case ACTION_SABOTAGE: 3156 case ACTION_ATTACK: 3157 case ACTION_GUARD_AREA: 3158 case ACTION_MOVE: 3159 action = action; 3160 break; 3161 3162 default: 3163 // action = ACTION_NONE; 3164 break; 3165 } 3166 3167 FootClass::Active_Click_With(action, object); 3168 } 3169 3170 3171 /*********************************************************************************************** 3172 * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * 3173 * * 3174 * INPUT: CELL - the cell we are setting the bit in * 3175 * * 3176 * int - the spot index we are setting the bit for * 3177 * * 3178 * OUTPUT: none * 3179 * * 3180 * HISTORY: * 3181 * 06/08/1995 PWG : Created. * 3182 *=============================================================================================*/ 3183 void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) 3184 { 3185 assert(Infantry.ID(this) == ID); 3186 assert(IsActive); 3187 3188 /* 3189 ** Set the occupy position for the spot that we passed in 3190 */ 3191 Map[cell].Flag.Composite |= (1 << spot_index); 3192 3193 /* 3194 ** Record the type of infantry that now owns the cell 3195 */ 3196 Map[cell].InfType = Owner(); 3197 } 3198 3199 3200 /*************************************************************************** 3201 * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * 3202 * * 3203 * INPUT: * 3204 * * 3205 * OUTPUT: * 3206 * * 3207 * WARNINGS: * 3208 * * 3209 * HISTORY: * 3210 * 06/08/1995 PWG : Created. * 3211 *=========================================================================*/ 3212 void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) 3213 { 3214 assert(Infantry.ID(this) == ID); 3215 assert(IsActive); 3216 3217 /* 3218 ** Clear the occupy bit for the infantry in that cell 3219 */ 3220 Map[cell].Flag.Composite &= ~(1 << spot_index); 3221 3222 /* 3223 ** If he was the last infantry recorded in the cell then 3224 ** remove the infantry ownership flag. 3225 */ 3226 if (!(Map[cell].Flag.Composite & 0x1F)) { 3227 Map[cell].InfType = HOUSE_NONE; 3228 } 3229 } 3230 3231 3232 /*********************************************************************************************** 3233 * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * 3234 * * 3235 * This routine will return with the full name (as a text number) for this infantry * 3236 * unit. Typically, this is the normal name, but in cases of civilian type survivors from * 3237 * a building explosion, it might be a technician instead. In such a case, the special * 3238 * technician name number is returned instead. * 3239 * * 3240 * INPUT: none * 3241 * * 3242 * OUTPUT: Returns with the full name to use for this infantry unit. * 3243 * * 3244 * WARNINGS: none * 3245 * * 3246 * HISTORY: * 3247 * 06/30/1995 JLB : Created. * 3248 * 10/28/1996 JLB : Spy returns "enemy soldier" text name. * 3249 *=============================================================================================*/ 3250 int InfantryClass::Full_Name(void) const 3251 { 3252 assert(Infantry.ID(this) == ID); 3253 assert(IsActive); 3254 3255 if (IsTechnician) { 3256 return(TXT_TECHNICIAN); 3257 } 3258 3259 if (*this == INFANTRY_SPY && !House->IsPlayerControl) { 3260 return(TXT_E1); 3261 } 3262 3263 return(Class->Full_Name()); 3264 } 3265 3266 3267 /*********************************************************************************************** 3268 * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * 3269 * * 3270 * This routine intercepts the normal attack mission and if an engineer is detected and the * 3271 * target is a building, then the engineer will be automatically assigned the capture * 3272 * mission. In other cases, the normal attack logic will proceed. * 3273 * * 3274 * INPUT: none * 3275 * * 3276 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 3277 * * 3278 * WARNINGS: none * 3279 * * 3280 * HISTORY: * 3281 * 08/07/1995 JLB : Created. * 3282 * 04/15/1996 BWG : Engineers can only attack their own house's buildings now. * 3283 * 05/29/1996 JLB : Engineers can now damage/capture enemy buildings. * 3284 *=============================================================================================*/ 3285 int InfantryClass::Mission_Attack(void) 3286 { 3287 assert(Infantry.ID(this) == ID); 3288 assert(IsActive); 3289 3290 if (Class->IsBomber && As_Building(TarCom)) { 3291 Assign_Destination(TarCom); 3292 Assign_Mission(MISSION_SABOTAGE); 3293 return(1); 3294 } 3295 3296 if (Class->IsCapture && As_Building(TarCom) != NULL && As_Building(TarCom)->Can_Capture()) { 3297 Assign_Destination(TarCom); 3298 Assign_Mission(MISSION_CAPTURE); 3299 return(1); 3300 } 3301 3302 return(FootClass::Mission_Attack()); 3303 } 3304 3305 3306 /*********************************************************************************************** 3307 * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * 3308 * * 3309 * This routine will determine what action to perform if the mouse was clicked on the cell * 3310 * specified. This is just a courier function since the lower level classes actually * 3311 * perform the work. The need for this routine at this level is due to the existence of * 3312 * a similarly named function at this level as well. C++ namespace rules require this * 3313 * function courier to be in place or an error will result. * 3314 * * 3315 * INPUT: cell -- The cell that the mouse might be clicked upon. * 3316 * * 3317 * OUTPUT: Returns with the action that would be given to this infantry unit if the mouse * 3318 * were clicked at the cell specified. * 3319 * * 3320 * WARNINGS: none * 3321 * * 3322 * HISTORY: * 3323 * 09/21/1995 JLB : Created. * 3324 *=============================================================================================*/ 3325 ActionType InfantryClass::What_Action(CELL cell) const 3326 { 3327 assert(Infantry.ID(this) == ID); 3328 assert(IsActive); 3329 3330 ActionType action = FootClass::What_Action(cell); 3331 3332 /* 3333 ** Dogs can only attack infantrymen 3334 */ 3335 if (Class->IsDog && action == ACTION_ATTACK) { 3336 action = ACTION_NONE; 3337 } 3338 3339 /* 3340 ** If this is a medic, and the cursor's over a friendly infantryman, 3341 ** execute an action-attack. 3342 */ 3343 if (Combat_Damage() < 0 && House->IsPlayerControl) { 3344 if (action == ACTION_ATTACK) { 3345 action = ACTION_NOMOVE; 3346 } 3347 } 3348 3349 /* 3350 ** Demolitioners may destroy a bridge 3351 */ 3352 if (Class->IsBomber && action == ACTION_MOVE && !Special.IsCaptureTheFlag) { 3353 switch (Map[cell].TType) { 3354 case TEMPLATE_BRIDGE1: 3355 case TEMPLATE_BRIDGE2: 3356 case TEMPLATE_BRIDGE1H: 3357 case TEMPLATE_BRIDGE2H: 3358 case TEMPLATE_BRIDGE_1A: 3359 case TEMPLATE_BRIDGE_1B: 3360 case TEMPLATE_BRIDGE_2A: 3361 case TEMPLATE_BRIDGE_2B: 3362 // case TEMPLATE_BRIDGE_3A: 3363 // case TEMPLATE_BRIDGE_3B: 3364 return(ACTION_SABOTAGE); 3365 } 3366 } 3367 3368 #ifdef OBSOLETE 3369 /* 3370 ** Engineers may repair a destroyed bridge. 3371 */ 3372 if (*this == INFANTRY_RENOVATOR && action == ACTION_NOMOVE) { 3373 /* 3374 ** If they're pointing on the wrong side of the bridge, ignore it 3375 ** 'cause we can't get there. 3376 */ 3377 TemplateType tt = Map[cell].TType; 3378 if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D || 3379 tt == TEMPLATE_BRIDGE_1C || tt == TEMPLATE_BRIDGE_2C || 3380 (tt >= TEMPLATE_BRIDGE_3C && tt <= TEMPLATE_BRIDGE_3E) ) { 3381 /* 3382 ** We know they're pointing at a destroyed bridge cell. If the cell 3383 ** they're pointing at is surrounded by impassables, return this 3384 ** cell as impassable. But, if any cell surrounding this cell is 3385 ** passable, return that this is a capturable cell. 3386 */ 3387 if (Map[cell].Land_Type() == LAND_ROCK) { 3388 if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); 3389 3390 if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); 3391 int y = Cell_Y(cell); 3392 if (y) { 3393 LandType above = Map[(CELL)(cell-(MAP_CELL_W-1))].Land_Type(); 3394 if (above == LAND_CLEAR || above == LAND_ROAD) { 3395 if (Map[(CELL)(cell-(MAP_CELL_W-1))].Zone == Map[As_Cell(As_Target())].Zone) { 3396 return(ACTION_CAPTURE); 3397 } 3398 return(ACTION_NOMOVE); 3399 } 3400 } 3401 if (y < MAP_CELL_H) { 3402 LandType below = Map[(CELL)(cell + MAP_CELL_W-1)].Land_Type(); 3403 if (below == LAND_CLEAR || below == LAND_ROAD) { 3404 if (Map[(CELL)(cell+MAP_CELL_W-1)].Zone == Map[As_Cell(As_Target())].Zone) { 3405 return(ACTION_CAPTURE); 3406 } 3407 return(ACTION_NOMOVE); 3408 } 3409 } 3410 } 3411 return(ACTION_NOMOVE); 3412 } 3413 } 3414 #endif 3415 return(action); 3416 } 3417 3418 3419 /*********************************************************************************************** 3420 * InfantryClass::Class_Of -- Returns the class reference for this object. * 3421 * * 3422 * This routine will return a reference to the infantry type class object that describes * 3423 * this infantry's characteristics. * 3424 * * 3425 * INPUT: none * 3426 * * 3427 * OUTPUT: Returns with a reference to the InfantryTypeClass object associated with this * 3428 * infantry object. * 3429 * * 3430 * WARNINGS: none * 3431 * * 3432 * HISTORY: * 3433 * 09/21/1995 JLB : Created. * 3434 *=============================================================================================*/ 3435 ObjectTypeClass const & InfantryClass::Class_Of(void) const 3436 { 3437 assert(Infantry.ID(this) == ID); 3438 assert(IsActive); 3439 3440 return(*Class); 3441 } 3442 3443 3444 /*********************************************************************************************** 3445 * InfantryClass::Read_INI -- Reads units from scenario INI file. * 3446 * * 3447 * This routine is used to read all the starting units from the * 3448 * scenario control INI file. The units are created and placed on the * 3449 * map by this routine. * 3450 * * 3451 * INI entry format: * 3452 * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * 3453 * Facingnum, Triggername * 3454 * * 3455 * INPUT: buffer -- Pointer to the loaded scenario INI file. * 3456 * * 3457 * OUTPUT: none * 3458 * * 3459 * WARNINGS: none * 3460 * * 3461 * HISTORY: * 3462 * 05/24/1994 JLB : Created. * 3463 *=============================================================================================*/ 3464 void InfantryClass::Read_INI(CCINIClass & ini) 3465 { 3466 InfantryClass * infantry; // Working infantry pointer. 3467 HousesType inhouse; // Infantry house. 3468 InfantryType classid; // Infantry class. 3469 char buf[128]; 3470 char * validation; 3471 DirType dir; 3472 TriggerTypeClass * tp; 3473 3474 int len = ini.Entry_Count(INI_Name()); 3475 for (int index = 0; index < len; index++) { 3476 char const * entry = ini.Get_Entry(INI_Name(), index); 3477 3478 /* 3479 ** Get an infantry entry 3480 */ 3481 ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); 3482 3483 /* 3484 ** 1st token: house name. 3485 */ 3486 inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); 3487 if (inhouse != HOUSE_NONE) { 3488 3489 /* 3490 ** 2nd token: infantry type name. 3491 */ 3492 classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); 3493 3494 if (classid != INFANTRY_NONE) { 3495 3496 if (HouseClass::As_Pointer(inhouse) != NULL) { 3497 infantry = new InfantryClass(classid, inhouse); 3498 if (infantry != NULL) { 3499 3500 /* 3501 ** 3rd token: strength. 3502 */ 3503 int strength = atoi(strtok(NULL, ",\n\r")); 3504 3505 /* 3506 ** 4th token: cell #. 3507 */ 3508 CELL cell = atoi(strtok(NULL, ",\n\r")); 3509 COORDINATE coord = Cell_Coord(cell); 3510 3511 /* 3512 ** 5th token: cell sub-location. 3513 */ 3514 int sub = atoi(strtok(NULL, ",")); 3515 coord = Coord_Add(Coord_Whole(coord), StoppingCoordAbs[ sub ]); 3516 3517 /* 3518 ** Fetch the mission and facing. 3519 */ 3520 MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); 3521 validation = strtok(NULL, ",\n\r"); 3522 if (validation) { 3523 dir = (DirType)atoi(validation); 3524 validation = strtok(NULL, ",\n\r"); 3525 if (validation) { 3526 tp = TriggerTypeClass::From_Name(validation); 3527 } else { 3528 tp = NULL; 3529 } 3530 } else { 3531 dir = (DirType)0; 3532 tp = NULL; 3533 } 3534 3535 infantry->Trigger = NULL; 3536 if (tp != NULL) { 3537 TriggerClass * tt = Find_Or_Make(tp); 3538 if (tt != NULL) { 3539 tt->AttachCount++; 3540 infantry->Trigger = tt; 3541 } 3542 } 3543 3544 if (infantry->Unlimbo(coord, dir)) { 3545 infantry->Strength = (int)infantry->Class_Of().MaxStrength * fixed(strength, 256); 3546 if (infantry->Strength > infantry->Class->MaxStrength-3) infantry->Strength = infantry->Class->MaxStrength; 3547 // infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); 3548 if (Session.Type == GAME_NORMAL || infantry->House->IsHuman) { 3549 infantry->Assign_Mission(mission); 3550 infantry->Commence(); 3551 } else { 3552 infantry->Enter_Idle_Mode(); 3553 } 3554 } else { 3555 3556 /* 3557 ** If the infantry could not be unlimboed, then this is a big error. 3558 ** Delete the infantry. 3559 */ 3560 delete infantry; 3561 } 3562 } 3563 } 3564 } 3565 } 3566 } 3567 } 3568 3569 3570 /*********************************************************************************************** 3571 * InfantryClass::Write_INI -- Store the infantry to the INI database. * 3572 * * 3573 * This will store all the infantry objects to the INI database specified. * 3574 * * 3575 * INPUT: ini -- Reference to the INI database to store the infantry data to. * 3576 * * 3577 * OUTPUT: none * 3578 * * 3579 * WARNINGS: none * 3580 * * 3581 * HISTORY: * 3582 * 07/03/1996 JLB : Created. * 3583 *=============================================================================================*/ 3584 void InfantryClass::Write_INI(CCINIClass & ini) 3585 { 3586 /* 3587 ** First, clear out all existing infantry data from the ini file. 3588 */ 3589 ini.Clear(INI_Name()); 3590 3591 /* 3592 ** Write the infantry data out. 3593 */ 3594 for (int index = 0; index < Infantry.Count(); index++) { 3595 InfantryClass * infantry = Infantry.Ptr(index); 3596 if (!infantry->IsInLimbo) { 3597 char uname[10]; 3598 char buf[128]; 3599 3600 sprintf(uname, "%d", index); 3601 sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", 3602 infantry->House->Class->IniName, 3603 infantry->Class->IniName, 3604 infantry->Health_Ratio()*256, 3605 Coord_Cell(infantry->Coord), 3606 CellClass::Spot_Index(infantry->Coord), 3607 MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? 3608 infantry->MissionQueue : infantry->Mission), 3609 infantry->PrimaryFacing.Current(), 3610 infantry->Trigger.Is_Valid() ? infantry->Trigger->Class->IniName : "None" 3611 ); 3612 ini.Put_String(INI_Name(), uname, buf); 3613 } 3614 } 3615 } 3616 3617 3618 /*********************************************************************************************** 3619 * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * 3620 * * 3621 * Use this routine to handle the fear logic for this infantry. It will slowly increase * 3622 * the bravery of the infantry as well as cause it to stand up or lie down as appropriate. * 3623 * It will even handle the special fraidy cat logic for civilian infantry. * 3624 * * 3625 * INPUT: none * 3626 * * 3627 * OUTPUT: none * 3628 * * 3629 * WARNINGS: Only call this once per game logic loop per infantry unit. * 3630 * * 3631 * HISTORY: * 3632 * 07/29/1996 JLB : Created. * 3633 *=============================================================================================*/ 3634 void InfantryClass::Fear_AI(void) 3635 { 3636 /* 3637 ** After a time, the infantry will gain courage. 3638 */ 3639 if (Fear > 0) { 3640 3641 Fear--; 3642 3643 /* 3644 ** When an armed civilian becomes unafraid, he will then reload 3645 ** another clip into his pistol. 3646 */ 3647 if (Fear == 0 && Ammo == 0 && Is_Weapon_Equipped()) { 3648 Ammo = Class->MaxAmmo; 3649 } 3650 3651 /* 3652 ** Stand up if brave and lie down if afraid. 3653 */ 3654 if (IsProne) { 3655 if (Fear < FEAR_ANXIOUS) { 3656 Do_Action(DO_GET_UP); 3657 } 3658 } else { 3659 3660 /* 3661 ** Drop to the ground if anxious. Don't drop to the ground while moving 3662 ** and the special elite flag is active. 3663 */ 3664 if (!Class->IsDog && Height == 0 && Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving))) { 3665 Do_Action(DO_LIE_DOWN); 3666 } 3667 } 3668 } 3669 3670 /* 3671 ** When in darkness or in doubt, 3672 ** run in circles, scream, and shout. 3673 */ 3674 if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsFalling && !IsDriving && !Target_Legal(NavCom)) { 3675 Scatter(0, true); 3676 } 3677 } 3678 3679 3680 /*********************************************************************************************** 3681 * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * 3682 * * 3683 * This routine will detect when the infantry has left the edge of the world and will * 3684 * delete it as necessary. * 3685 * * 3686 * INPUT: none * 3687 * * 3688 * OUTPUT: bool; Was the infantry unit deleted by this routine? * 3689 * * 3690 * WARNINGS: Be sure the check the return value and if true, abort any further processing * 3691 * for this infantry unit. * 3692 * * 3693 * HISTORY: * 3694 * 07/29/1996 JLB : Created. * 3695 *=============================================================================================*/ 3696 bool InfantryClass::Edge_Of_World_AI(void) 3697 { 3698 /* 3699 ** Delete this unit if it finds itself off the edge of the map and it is in 3700 ** guard or other static mission mode. 3701 */ 3702 if (Team.Is_Valid() && IsLocked) Team->IsLeaveMap = true; 3703 3704 if (!Team.Is_Valid() && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { 3705 Stun(); 3706 delete this; 3707 return(true); 3708 } 3709 return(false); 3710 } 3711 3712 3713 /*********************************************************************************************** 3714 * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * 3715 * * 3716 * This will examine the infantry and determine what firing action is required. It will * 3717 * search for targets, starting firing animations, and launch bullets as necessary. * 3718 * * 3719 * INPUT: none * 3720 * * 3721 * OUTPUT: none * 3722 * * 3723 * WARNINGS: Only call this routine once per infantry per game logic loop. * 3724 * * 3725 * HISTORY: * 3726 * 07/29/1996 JLB : Created. * 3727 *=============================================================================================*/ 3728 void InfantryClass::Firing_AI(void) 3729 { 3730 if (Target_Legal(TarCom)) { 3731 int primary = What_Weapon_Should_I_Use(TarCom); 3732 3733 if (!IsFiring) { 3734 switch (Can_Fire(TarCom, primary)) { 3735 case FIRE_ILLEGAL: 3736 if (Combat_Damage(primary) < 0) { 3737 ObjectClass * targ= As_Object(TarCom); 3738 #ifdef FIXIT_CSII // checked - ajw 9/28/98 3739 if (targ) { 3740 if( (targ->What_Am_I() == RTTI_INFANTRY && *this == INFANTRY_MEDIC) || 3741 (*this == INFANTRY_MECHANIC && (targ->What_Am_I() == RTTI_AIRCRAFT || targ->What_Am_I() == RTTI_UNIT )) ) { 3742 3743 if (targ->Health_Ratio() >= Rule.ConditionGreen) { 3744 Assign_Target(TARGET_NONE); 3745 } 3746 } 3747 } else { 3748 Assign_Target(TARGET_NONE); 3749 } 3750 #else 3751 if (targ && targ->What_Am_I() == RTTI_INFANTRY) { 3752 if (targ->Health_Ratio() >= Rule.ConditionGreen) { 3753 Assign_Target(TARGET_NONE); 3754 } 3755 } else { 3756 Assign_Target(TARGET_NONE); 3757 } 3758 #endif 3759 } else if (Class->IsDog) { 3760 Assign_Target(TARGET_NONE); 3761 } 3762 break; 3763 3764 case FIRE_CLOAKED: 3765 Do_Uncloak(); 3766 break; 3767 3768 case FIRE_OK: 3769 /* 3770 ** Start firing animation. 3771 */ 3772 if (IsProne) { 3773 Do_Action(DO_FIRE_PRONE); 3774 } else { 3775 Do_Action(DO_FIRE_WEAPON); 3776 } 3777 3778 Mark(MARK_OVERLAP_UP); 3779 IsFiring = true; 3780 Mark(MARK_OVERLAP_DOWN); 3781 3782 PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); 3783 3784 /* 3785 ** If the target is in range, and the NavCom is the same, then just 3786 ** stop and keep firing. 3787 */ 3788 if (TarCom == NavCom) { 3789 NavCom = TARGET_NONE; 3790 Path[0] = FACING_NONE; 3791 } 3792 break; 3793 } 3794 } 3795 3796 /* 3797 ** If in the middle of firing animation, then only 3798 ** process that. Infantry cannot fire and move simultaneously. 3799 ** At some point in the firing animation process, a projectile 3800 ** will be launched. When the required animation frames have 3801 ** been completed, the firing animation stops. 3802 */ 3803 int firestage = Class->FireLaunch; 3804 if (IsProne) firestage = Class->ProneLaunch; 3805 3806 if (IsFiring && Fetch_Stage() == firestage) { 3807 3808 /* 3809 ** Target might have changed during the firing animation 3810 */ 3811 if (Can_Fire(TarCom, primary) == FIRE_OK) { 3812 Fire_At(TarCom, primary); 3813 3814 /* 3815 ** Run away from slowly approaching projectiles. 3816 */ 3817 if (Class->PrimaryWeapon->MaxSpeed < Rule.Incoming) { 3818 Map[::As_Cell(TarCom)].Incoming(Coord, true); 3819 } 3820 3821 /* 3822 ** If it's a dog, get rid of him (he'll be re-created when he hits) 3823 */ 3824 if (Class->IsDog) { 3825 WasSelected = IsSelected; 3826 ScenarioInit++; 3827 Limbo(); 3828 ScenarioInit--; 3829 } 3830 } else { 3831 Mark(MARK_OVERLAP_UP); 3832 IsFiring = false; 3833 Mark(MARK_OVERLAP_DOWN); 3834 } 3835 } 3836 } else { 3837 if (IsFiring) { 3838 Mark(MARK_OVERLAP_UP); 3839 IsFiring = false; 3840 Mark(MARK_OVERLAP_DOWN); 3841 } 3842 } 3843 } 3844 3845 3846 /*********************************************************************************************** 3847 * InfantryClass::Doing_AI -- Handles the animation AI processing. * 3848 * * 3849 * Infantry can be in one of many different animation sequences. At the conclusion of each * 3850 * sequence, the infantry will quite likely transition to a new animation state. This * 3851 * routine handles detecting when that trasition should occur and starting the infantry * 3852 * into its new state. * 3853 * * 3854 * INPUT: none * 3855 * * 3856 * OUTPUT: none * 3857 * * 3858 * WARNINGS: Only call this routine once per infantry unit per game logic loop. * 3859 * * 3860 * HISTORY: * 3861 * 07/29/1996 JLB : Created. * 3862 *=============================================================================================*/ 3863 void InfantryClass::Doing_AI(void) 3864 { 3865 if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { 3866 switch (Doing) { 3867 default: 3868 if (IsDriving) { 3869 if (Class->IsDog) { 3870 3871 /* 3872 ** Dog crawl animation is actually the run animation. 3873 */ 3874 if (Target_Legal(TarCom)) { 3875 Do_Action(DO_CRAWL, true); 3876 } else { 3877 Do_Action(DO_WALK, true); 3878 } 3879 } else { 3880 if (IsProne) { 3881 Do_Action(DO_CRAWL, true); 3882 } else { 3883 Do_Action(DO_WALK, true); 3884 } 3885 } 3886 } else { 3887 if (Class->IsDog) { 3888 Do_Action(DO_STAND_READY, true); 3889 } else { 3890 if (IsProne) { 3891 Do_Action(DO_PRONE, true); 3892 } else { 3893 Do_Action(DO_STAND_READY, true); 3894 } 3895 } 3896 } 3897 break; 3898 3899 case DO_DOG_MAUL: 3900 Do_Action(DO_STAND_READY, true); 3901 break; 3902 3903 case DO_GUN_DEATH: 3904 case DO_EXPLOSION_DEATH: 3905 case DO_EXPLOSION2_DEATH: 3906 case DO_GRENADE_DEATH: 3907 case DO_FIRE_DEATH: 3908 if (Fetch_Stage() >= Class->DoControls[Doing].Count) { 3909 AnimClass* anim = NULL; 3910 LandType land = Map[Center_Coord()].Land_Type(); 3911 if (land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) { 3912 if (Doing == DO_GUN_DEATH && !Class->IsDog && Height==0) { 3913 anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); 3914 } 3915 if (Doing == DO_GRENADE_DEATH && !Class->IsDog && Height==0) { 3916 anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-10, 3))); 3917 } 3918 if (Doing == DO_EXPLOSION_DEATH && !Class->IsDog && Height==0) { 3919 anim = new AnimClass(ANIM_CORPSE3, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); 3920 } 3921 if (Doing == DO_EXPLOSION2_DEATH && !Class->IsDog && Height==0) { 3922 anim = new AnimClass(ANIM_CORPSE2, Center_Coord()); 3923 } 3924 } 3925 if (anim != NULL) { 3926 anim->Set_Owner(House->Class->House); 3927 } 3928 delete this; 3929 return; 3930 } 3931 } 3932 } 3933 } 3934 3935 3936 /*********************************************************************************************** 3937 * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * 3938 * * 3939 * It examines the infantry state and determines what movement action should be initiated * 3940 * or processed. It handles the actual movement of the infantry as well as any path finding * 3941 * or infantry startup logic. * 3942 * * 3943 * INPUT: none * 3944 * * 3945 * OUTPUT: none * 3946 * * 3947 * WARNINGS: Only call this routine once per infantry unit per game logic loop. * 3948 * * 3949 * HISTORY: * 3950 * 07/29/1996 JLB : Created. * 3951 *=============================================================================================*/ 3952 void InfantryClass::Movement_AI(void) 3953 { 3954 /* 3955 ** Special hack check to ensure that infantry will never get stuck in a movement order if 3956 ** there is no place to go. 3957 */ 3958 if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { 3959 Enter_Idle_Mode(); 3960 } 3961 3962 if (!IsFiring && !IsFalling && Doing != DO_DOG_MAUL) { 3963 if (!IsDriving) { 3964 3965 /* 3966 ** When in guard mode, never allow a valid navcom. 3967 */ 3968 if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { 3969 Assign_Destination(TARGET_NONE); 3970 // if (IsTethered) Scatter(0, true); 3971 } 3972 3973 /* 3974 ** Scatter infantry off buildings in guard modes. 3975 */ 3976 if (!IsTethered && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord].Cell_Building() != NULL) { 3977 Scatter(0, true, true); 3978 } 3979 3980 /* 3981 ** Double check to make sure it doesn't have a movement destination into a zone 3982 ** that it can't travel to. In such a case, abort the movement process by clearing 3983 ** the navigation computer. 3984 */ 3985 if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && !IsDriving && !IsTethered && Target_Legal(NavCom) && IsLocked && Map[Coord].Zones[Class->MZone] != Map[As_Cell(NavCom)].Zones[Class->MZone]) { 3986 // hack: if it's tanya, spy, or engineer, let 'em move there anyway. 3987 if (!Class->IsCapture && Mission != MISSION_ENTER) { 3988 // if (*this != INFANTRY_TANYA && *this != INFANTRY_SPY && *this != INFANTRY_RENOVATOR) { 3989 Assign_Destination(TARGET_NONE); 3990 } 3991 } 3992 3993 /* 3994 ** A head to coordinate is needed. If there is no path 3995 ** available, then create one. 3996 */ 3997 if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { 3998 3999 /* 4000 ** Determine if the next cell in the list is available 4001 ** to be entered. If not, then abort the path and try 4002 ** again. 4003 */ 4004 if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { 4005 Path[0] = FACING_NONE; 4006 } 4007 4008 /* 4009 ** Check to see if the target is closer than expected. This occurs 4010 ** when heading toward a moving object and that object is heading 4011 ** toward the unit. Shorten the precalculated path to be no longer 4012 ** than the distance to the target. 4013 */ 4014 int d = Lepton_To_Cell(Distance(NavCom)); 4015 if (d < CONQUER_PATH_MAX) { 4016 Path[d] = FACING_NONE; 4017 } 4018 4019 /* 4020 ** Find a path to follow if one isn't already calculated. 4021 */ 4022 if (Path[0] == FACING_NONE) { 4023 4024 /* 4025 ** Calculate the path from the current location to the 4026 ** destination indicated by the navigation computer. If there 4027 ** was a fundamental error with finding a path, then this 4028 ** indicates that basic path & movement logic needs to be 4029 ** aborted. 4030 */ 4031 if (PathDelay != 0) { 4032 return; 4033 } 4034 if (!Basic_Path()) { 4035 4036 /* 4037 ** Check to ensure that if a computer controlled unit is in 4038 ** hunt mode, but cannot reach the target it would like to, 4039 ** abort the target tracking and let the normal hunt logic 4040 ** assign a new one. 4041 */ 4042 if (!House->IsHuman && Mission == MISSION_HUNT) { 4043 Assign_Destination(TARGET_NONE); 4044 Assign_Target(TARGET_NONE); 4045 } else { 4046 4047 /* 4048 ** If the infantry unit is close enough to the target, then 4049 ** tell it to stop. 4050 */ 4051 if (Distance(NavCom) < Rule.CloseEnoughDistance && !IsTethered) { 4052 Assign_Destination(TARGET_NONE); 4053 } else { 4054 4055 /* 4056 ** Update the try try again counter so that this 4057 ** infantry unit will try again at a later time. 4058 */ 4059 if (TryTryAgain) { 4060 TryTryAgain--; 4061 } else { 4062 if (IsNewNavCom) Sound_Effect(VOC_SCOLD); 4063 IsNewNavCom = false; 4064 4065 //If we're trying to enter a transport we need to fail so others can try to enter. - LLL 4/17/2020 4066 if (Mission == MISSION_ENTER) { 4067 Mission = MISSION_NONE; 4068 Assign_Mission(MISSION_GUARD); 4069 Commence(); 4070 4071 Transmit_Message(RADIO_OVER_OUT); 4072 } 4073 4074 /* 4075 ** Abort the target and destination process since the path 4076 ** could not be found. In such a case, processing should stop 4077 ** or else the game will bog down with repeated path failures. 4078 ** Only perform the abort of the target is in a different zone. 4079 */ 4080 if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && IsLocked && Target_Legal(NavCom) && Map[As_Cell(NavCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { 4081 Assign_Destination(TARGET_NONE); 4082 } 4083 if (IsLocked && Target_Legal(TarCom) && Map[As_Cell(TarCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { 4084 Assign_Target(TARGET_NONE); 4085 } 4086 } 4087 } 4088 } 4089 Stop_Driver(); 4090 return; 4091 } 4092 TryTryAgain = PATH_RETRY; 4093 } 4094 4095 /* 4096 ** Determine the coordinate to head to based on the infantry's 4097 ** current location and the next location in the path. 4098 */ 4099 COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); 4100 CELL acell = Coord_Cell(acoord); 4101 4102 if (Can_Enter_Cell(acell) != MOVE_OK) { 4103 4104 if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered /*&& House->IsHuman*/ && Distance(NavCom) < Rule.CloseEnoughDistance) { 4105 Assign_Destination(TARGET_NONE); 4106 } else { 4107 4108 /* 4109 ** If blocked by a moving block then just exit start of move and 4110 ** try again next tick. 4111 */ 4112 if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { 4113 if (Map[acell].Cell_Object()) { 4114 if (!House->Is_Ally(Map[acell].Cell_Object())) { 4115 Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); 4116 } 4117 } else { 4118 if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { 4119 Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); 4120 } 4121 } 4122 } 4123 } 4124 4125 Path[0] = FACING_NONE; 4126 Stop_Driver(); 4127 if (IsNewNavCom) Sound_Effect(VOC_SCOLD); 4128 IsNewNavCom = false; 4129 4130 } else { 4131 if (Start_Driver(acoord)) { 4132 if (!IsActive) return; 4133 PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); 4134 if (IsFormationMove) { 4135 Set_Speed(Ground[Map[Coord].Land_Type()].Cost[FormationSpeed] * 255); 4136 } else { 4137 Set_Speed(0xFF); 4138 } 4139 4140 if (Class->IsDog) { 4141 4142 /* 4143 ** Dog crawl animation is actually the run animation. 4144 */ 4145 if (Target_Legal(TarCom)) { 4146 Do_Action(DO_CRAWL); 4147 } else { 4148 Do_Action(DO_WALK); 4149 } 4150 } else { 4151 if (IsProne) { 4152 Do_Action(DO_CRAWL); 4153 } else { 4154 Do_Action(DO_WALK); 4155 } 4156 } 4157 } 4158 } 4159 } 4160 4161 } else { 4162 4163 /* 4164 ** The infantry knows where it should be headed, so head there. Check 4165 ** to see if the infantry is "close enough" to the desired location that 4166 ** it should just consider itself to have arrived. In this case, force 4167 ** the infantry to the destination location and mark this path step 4168 ** as complete. 4169 */ 4170 Mark(MARK_UP); 4171 if (Distance(Head_To_Coord()) < 0x0010) { 4172 4173 memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); 4174 Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; 4175 Coord = Head_To_Coord(); 4176 Per_Cell_Process(PCP_END); 4177 if (!IsActive || IsInLimbo) return; 4178 4179 Stop_Driver(); 4180 if (!IsActive || IsInLimbo) return; 4181 4182 if (Coord_Cell(Coord) == As_Cell(NavCom)) { 4183 NavCom = TARGET_NONE; 4184 if (Mission == MISSION_MOVE) { 4185 Enter_Idle_Mode(); 4186 } 4187 //Stop_Driver(); 4188 Path[0] = FACING_NONE; 4189 } 4190 } else { 4191 int movespeed = Speed; 4192 4193 /* 4194 ** When prone, the infantry moves at half speed or double 4195 ** speed. This depends on whether the infantry actually has 4196 ** prone animation stages. Civilians don't, and so they 4197 ** run instead. 4198 */ 4199 if (Class->IsDog && Target_Legal(TarCom)) { 4200 movespeed *= 2; 4201 } 4202 4203 if (IsProne && !Class->IsDog) { 4204 if ((Class->IsFraidyCat && !Class->IsCrawling) ) { 4205 movespeed = Speed*2; 4206 } else { 4207 movespeed = Speed/2; 4208 } 4209 } 4210 4211 if (IsTethered) { 4212 Transmit_Message(RADIO_REDRAW); 4213 } 4214 4215 /* 4216 ** Advance the infantry as far as it should go. 4217 */ 4218 MPHType maxspeed = MPHType(min(Class->MaxSpeed * SpeedBias * House->GroundspeedBias, MPH_LIGHT_SPEED)); 4219 4220 if (IsFormationMove) maxspeed = FormationMaxSpeed; 4221 4222 Coord = Coord_Move(Coord, Direction(Head_To_Coord()), maxspeed * fixed(movespeed, 256)); 4223 } 4224 Mark(MARK_DOWN); 4225 } 4226 IsNewNavCom = false; 4227 } 4228 } 4229 4230 4231 /*********************************************************************************************** 4232 * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * 4233 * * 4234 * The image data for the infantry differs from normal if this is a spy. A spy always * 4235 * appears like a minigunner to the non-owning players. * 4236 * * 4237 * INPUT: none * 4238 * * 4239 * OUTPUT: Returns with a pointer to the image data to use for this infantry soldier. * 4240 * * 4241 * WARNINGS: none * 4242 * * 4243 * HISTORY: * 4244 * 08/06/1996 JLB : Created. * 4245 *=============================================================================================*/ 4246 void const * InfantryClass::Get_Image_Data(void) const 4247 { 4248 if (!IsOwnedByPlayer && *this == INFANTRY_SPY) { 4249 return(MFCD::Retrieve("E1.SHP")); 4250 } 4251 return(TechnoClass::Get_Image_Data()); 4252 } 4253 4254 4255 /*********************************************************************************************** 4256 * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle * 4257 * * 4258 * This routine will examine this infantry and determine if it is allowed and ready to * 4259 * perform an idle animation. The conditions under which idle animations can be performed * 4260 * are restrictive. Hence this routine. * 4261 * * 4262 * INPUT: none * 4263 * * 4264 * OUTPUT: bool; Is this infantry ready to do an idle animation? * 4265 * * 4266 * WARNINGS: none * 4267 * * 4268 * HISTORY: * 4269 * 10/01/1996 JLB : Created. * 4270 *=============================================================================================*/ 4271 bool InfantryClass::Is_Ready_To_Random_Animate(void) const 4272 { 4273 /* 4274 ** See if the base classes (more rudimentary checking) determines that idle animations 4275 ** cannot occur. If they cannot, then return with the failure code. 4276 */ 4277 if (!FootClass::Is_Ready_To_Random_Animate()) { 4278 return(false); 4279 } 4280 4281 /* 4282 ** While the infantry is in the air (such as when paradropping), it won't be allowed 4283 ** to idle animate. 4284 */ 4285 if (Height > 0) { 4286 return(false); 4287 } 4288 4289 /* 4290 ** When the infantry is walking or otherwise engauged in travel, it won't idle animate. 4291 */ 4292 if (IsDriving) { 4293 return(false); 4294 } 4295 4296 /* 4297 ** When prone, idle animations cannot occur. This is primarily because there are no prone 4298 ** idle animations. 4299 */ 4300 if (IsProne) { 4301 return(false); 4302 } 4303 4304 /* 4305 ** When firing, the infantry should not perform any idle animations. 4306 */ 4307 if (IsFiring) { 4308 return(false); 4309 } 4310 4311 /* 4312 ** Only if the infantry is in guard or ready stance is idle animations allowed. This is 4313 ** because the idle animations start and end with these frames. 4314 */ 4315 if (Doing != DO_STAND_GUARD && Doing != DO_STAND_READY) { 4316 return(false); 4317 } 4318 4319 /* 4320 ** Since no reason was found to indicate it is not a good time to idle 4321 ** animate, then it must be a good time to do so. 4322 */ 4323 return(true); 4324 } 4325 4326 4327 /*********************************************************************************************** 4328 * InfantryClass::Paradrop -- Handles paradropping infantry. * 4329 * * 4330 * This routine will paradrop this soldier at the location specified. It will cause the * 4331 * soldier to hunt if controlled by the computer and to guard if controlledy by the * 4332 * human. * 4333 * * 4334 * INPUT: coord -- The coordinate to paradrop the soldier to. * 4335 * * 4336 * OUTPUT: bool; Was the paradrop successful? * 4337 * * 4338 * WARNINGS: none * 4339 * * 4340 * HISTORY: * 4341 * 10/19/1996 JLB : Created. * 4342 *=============================================================================================*/ 4343 bool InfantryClass::Paradrop(COORDINATE coord) 4344 { 4345 if (FootClass::Paradrop(coord)) { 4346 if (House->IsHuman) { 4347 Assign_Mission(MISSION_GUARD); 4348 } else { 4349 Assign_Mission(MISSION_HUNT); 4350 } 4351 return(true); 4352 } 4353 return(false); 4354 }