TEAM.CPP (127793B)
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/TEAM.CPP 1 3/03/97 10:25a 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 : TEAM.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 12/11/94 * 28 * * 29 * Last Update : August 27, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * TeamClass::AI -- Process team logic. * 34 * TeamClass::Add -- Adds specified object to team. * 35 * TeamClass::Assign_Mission_Target -- Sets teams mission target and clears old target * 36 * TeamClass::Calc_Center -- Determines average location of team members. * 37 * TeamClass::Can_Add -- Determines if the specified object can be added to team. * 38 * TeamClass::Control -- Updates control on a member unit. * 39 * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * 40 * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * 41 * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * 42 * TeamClass::Coordinate_Move -- Handles team movement coordination. * 43 * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * 44 * TeamClass::Debug_Dump -- Displays debug information about the team. * 45 * TeamClass::Detach -- Removes specified target from team tracking. * 46 * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * 47 * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * 48 * TeamClass::Init -- Initializes the team objects for scenario preparation. * 49 * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * 50 * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * 51 * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * 52 * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * 53 * TeamClass::Remove -- Removes the specified object from the team. * 54 * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * 55 * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * 56 * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * 57 * TeamClass::TMission_Attack -- Perform the team attack mission command. * 58 * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * 59 * TeamClass::TMission_Formation -- Process team formation change command. * 60 * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of tim* 61 * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * 62 * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * 63 * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * 64 * TeamClass::TMission_Spy -- Perform the team spy mission. * 65 * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * 66 * TeamClass::TeamClass -- Constructor for the team object type. * 67 * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * 68 * TeamClass::operator delete -- Deallocates a team object. * 69 * TeamClass::operator new -- Allocates a team object. * 70 * TeamClass::~TeamClass -- Team object destructor. * 71 * _Is_It_Breathing -- Checks to see if unit is an active team member. * 72 * _Is_It_Playing -- Determines if unit is active and an initiated team member. * 73 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 74 75 #include "function.h" 76 #include "mission.h" 77 78 79 /*********************************************************************************************** 80 * _Is_It_Breathing -- Checks to see if unit is an active team member. * 81 * * 82 * A unit could be a team member, but not be active. Such a case would occur when a * 83 * reinforcement team is inside a transport. It could also occur if a unit is in the * 84 * process of dying. Call this routine to ensure that the specified unit is a will and * 85 * able participant in the team. * 86 * * 87 * INPUT: object -- Pointer to the unit/infantry/aircraft that is to be checked. * 88 * * 89 * OUTPUT: bool; Is the specified unit active and able to be given commands by the team? * 90 * * 91 * WARNINGS: none * 92 * * 93 * HISTORY: * 94 * 03/11/1996 JLB : Created. * 95 *=============================================================================================*/ 96 static inline bool _Is_It_Breathing(FootClass const * object) 97 { 98 /* 99 ** If the object is not present or appears to be dead, then it 100 ** certainly isn't an active member of the team. 101 */ 102 if (object == NULL || !object->IsActive || object->Strength == 0) return(false); 103 104 /* 105 ** If the object is in limbo, then it isn't an active team member either. However, if the 106 ** scenario init flag is on, then it is probably a reinforcement issue or scenario 107 ** creation situation. In such a case, the members are considered active because they need to 108 ** be given special orders and treatment. 109 */ 110 if (!ScenarioInit && object->IsInLimbo) return(false); 111 112 /* 113 ** Nothing eliminated this object from being considered an active member of the team (i.e., 114 ** "breathing"), then return that it is ok. 115 */ 116 return(true); 117 } 118 119 120 /*********************************************************************************************** 121 * _Is_It_Playing -- Determines if unit is active and an initiated team member. * 122 * * 123 * Use this routine to determine if the specified unit is an active participant of the * 124 * team. When a unit is first recruited to the team, it must travel to the team's location * 125 * before it can become an active player. Call this routine to determine if the specified * 126 * unit can be considered an active player. * 127 * * 128 * INPUT: object -- Pointer to the object that is to be checked to see if it is an * 129 * active player. * 130 * * 131 * OUTPUT: bool; Is the specified unit an active, living, initiated member of the team? * 132 * * 133 * WARNINGS: none * 134 * * 135 * HISTORY: * 136 * 03/11/1996 JLB : Created. * 137 *=============================================================================================*/ 138 static inline bool _Is_It_Playing(FootClass const * object) 139 { 140 /* 141 ** If the object is not active, then it certainly can be a participating member of the 142 ** team. 143 */ 144 if (!_Is_It_Breathing(object)) return(false); 145 146 /* 147 ** Only members that have been "Initiated" are considered "playing" participants of the 148 ** team. This results in the team members that are racing to regroup with the team (i.e., 149 ** not initiated), will continue to catch up to the team even while the initiated team members 150 ** carry out their team specific orders. 151 */ 152 if (!object->IsInitiated && object->What_Am_I() != RTTI_AIRCRAFT) return(false); 153 154 /* 155 ** If it reaches this point, then nothing appears to disqualify the specified object from 156 ** being considered an active playing member of the team. In this case, return that 157 ** information. 158 */ 159 return(true); 160 } 161 162 163 #ifdef CHEAT_KEYS 164 /*********************************************************************************************** 165 * TeamClass::Debug_Dump -- Displays debug information about the team. * 166 * * 167 * This routine will display information about the team. This is useful for debugging * 168 * purposes. * 169 * * 170 * INPUT: mono -- Pointer to the monochrome screen that the debugging information will * 171 * be displayed on. * 172 * * 173 * OUTPUT: none * 174 * * 175 * WARNINGS: none * 176 * * 177 * HISTORY: * 178 * 03/11/1996 JLB : Created. * 179 *=============================================================================================*/ 180 void TeamClass::Debug_Dump(MonoClass * mono) const 181 { 182 mono->Set_Cursor(1, 20);mono->Printf("%8.8s", Class->IniName); 183 mono->Set_Cursor(10, 20);mono->Printf("%3d", Total); 184 mono->Set_Cursor(17, 20);mono->Printf("%3d", Quantity[Class->ID]); 185 if (CurrentMission != -1) { 186 mono->Set_Cursor(1, 22); 187 mono->Printf("%-29s", Class->MissionList[CurrentMission].Description(CurrentMission)); 188 } 189 mono->Set_Cursor(40, 20);mono->Printf("%-10s", FormationName[Formation]); 190 mono->Set_Cursor(22, 20);mono->Printf("%08X", Zone); 191 mono->Set_Cursor(31, 20);mono->Printf("%08X", Target); 192 193 mono->Fill_Attrib(53, 20, 12, 1, IsUnderStrength ? MonoClass::INVERSE : MonoClass::NORMAL); 194 mono->Fill_Attrib(53, 21, 12, 1, IsFullStrength ? MonoClass::INVERSE : MonoClass::NORMAL); 195 mono->Fill_Attrib(53, 22, 12, 1, IsHasBeen ? MonoClass::INVERSE : MonoClass::NORMAL); 196 197 mono->Fill_Attrib(66, 20, 12, 1, IsMoving ? MonoClass::INVERSE : MonoClass::NORMAL); 198 mono->Fill_Attrib(66, 21, 12, 1, IsForcedActive ? MonoClass::INVERSE : MonoClass::NORMAL); 199 mono->Fill_Attrib(66, 22, 12, 1, IsReforming ? MonoClass::INVERSE : MonoClass::NORMAL); 200 } 201 #endif 202 203 204 /*********************************************************************************************** 205 * TeamClass::Init -- Initializes the team objects for scenario preparation. * 206 * * 207 * This routine clears out the team object array in preparation for starting a new * 208 * scenario. * 209 * * 210 * INPUT: none * 211 * * 212 * OUTPUT: none * 213 * * 214 * WARNINGS: none * 215 * * 216 * HISTORY: * 217 * 12/29/1994 JLB : Created. * 218 *=============================================================================================*/ 219 void TeamClass::Init(void) 220 { 221 Teams.Free_All(); 222 } 223 224 225 /*********************************************************************************************** 226 * TeamClass::operator new -- Allocates a team object. * 227 * * 228 * This routine will allocate a team object from the team object pool. * 229 * * 230 * INPUT: size -- The size of the requested allocation. * 231 * * 232 * OUTPUT: Returns with a pointer to the freshly allocated team object. If an allocation * 233 * could not be made, then NULL is returned. * 234 * * 235 * WARNINGS: none * 236 * * 237 * HISTORY: * 238 * 09/21/1995 JLB : Created. * 239 *=============================================================================================*/ 240 void * TeamClass::operator new(size_t) 241 { 242 void * ptr = Teams.Allocate(); 243 if (ptr != NULL) { 244 ((TeamClass *)ptr)->Set_Active(); 245 } 246 return(ptr); 247 } 248 249 250 /*********************************************************************************************** 251 * TeamClass::operator delete -- Deallocates a team object. * 252 * * 253 * This routine will return a team object to the team object pool. * 254 * * 255 * INPUT: ptr -- Pointer to the team object to deallocate. * 256 * * 257 * OUTPUT: none * 258 * * 259 * WARNINGS: none * 260 * * 261 * HISTORY: * 262 * 09/21/1995 JLB : Created. * 263 *=============================================================================================*/ 264 void TeamClass::operator delete(void * ptr) 265 { 266 if (ptr != NULL) { 267 ((TeamClass *)ptr)->IsActive = false; 268 } 269 Teams.Free((TeamClass *)ptr); 270 } 271 272 273 /*********************************************************************************************** 274 * TeamClass::~TeamClass -- Team object destructor. * 275 * * 276 * This routine is called when a team object is destroyed. It handles updating the total * 277 * number count for this team object type. * 278 * * 279 * INPUT: none * 280 * * 281 * OUTPUT: none * 282 * * 283 * WARNINGS: none * 284 * * 285 * HISTORY: * 286 * 09/21/1995 JLB : Created. * 287 * 07/04/1996 JLB : Keeps trigger if trigger still attached to objects. * 288 *=============================================================================================*/ 289 TeamClass::~TeamClass(void) 290 { 291 if (GameActive && Class.Is_Valid()) { 292 while (Member != NULL) { 293 Remove(Member); 294 } 295 Class->Number--; 296 297 /* 298 ** When the team dies, any trigger associated with it, dies as well. This will only occur 299 ** if there are no other objects linked to this trigger. Only player reinforcement 300 ** members that broke off of the team earlier will have this occur. 301 */ 302 if (Trigger.Is_Valid()) { 303 if (Trigger->AttachCount == 0) { 304 delete (TriggerClass *)Trigger; 305 } 306 Trigger = NULL; 307 } 308 } 309 } 310 311 312 /*********************************************************************************************** 313 * TeamClass::TeamClass -- Constructor for the team object type. * 314 * * 315 * This routine is called when the team object is created. * 316 * * 317 * INPUT: type -- Pointer to the team type to make this team object from. * 318 * * 319 * owner -- The owner of this team. * 320 * * 321 * OUTPUT: none * 322 * * 323 * WARNINGS: none * 324 * * 325 * HISTORY: * 326 * 09/21/1995 JLB : Created. * 327 *=============================================================================================*/ 328 TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : 329 AbstractClass(RTTI_TEAM, Teams.ID(this)), 330 Class((TeamTypeClass *)type), 331 House(owner), 332 IsForcedActive(false), 333 IsHasBeen(false), 334 IsFullStrength(false), 335 IsUnderStrength(true), 336 IsReforming(false), 337 IsLagging(false), 338 IsAltered(true), 339 JustAltered(false), 340 IsMoving(false), 341 IsNextMission(true), 342 IsLeaveMap(false), 343 Suspended(false), 344 Trigger(NULL), 345 Zone(TARGET_NONE), 346 ClosestMember(TARGET_NONE), 347 MissionTarget(TARGET_NONE), 348 Target(TARGET_NONE), 349 Total(0), 350 Risk(0), 351 Formation(FORMATION_NONE), 352 SuspendTimer(0), 353 CurrentMission(-1), 354 TimeOut(0), 355 Member(0) 356 { 357 assert(Class); 358 assert(Class->IsActive); 359 assert(Class->ClassCount > 0); 360 361 if (owner == NULL) { 362 House = HouseClass::As_Pointer(Class->House); 363 } 364 365 memset(Quantity, 0, sizeof(Quantity)); 366 if (Class->Origin != -1) { 367 Zone = ::As_Target(Scen.Waypoint[Class->Origin]); 368 } 369 Class->Number++; 370 371 /* 372 ** If there is a trigger tightly associated with this team, then 373 ** create an instance of that trigger and attach it to the team. 374 */ 375 if (Class->Trigger.Is_Valid()) { 376 Trigger = new TriggerClass(Class->Trigger); 377 } 378 } 379 380 381 /*************************************************************************** 382 * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * 383 * * 384 * INPUT: * 385 * * 386 * OUTPUT: * 387 * * 388 * WARNINGS: * 389 * * 390 * HISTORY: * 391 * 05/16/1995 PWG : Created. * 392 *=========================================================================*/ 393 void TeamClass::Assign_Mission_Target(TARGET new_target) 394 { 395 assert(IsActive); 396 assert(Teams.ID(this) == ID); 397 398 /* 399 ** First go through and find anyone who is currently targeting 400 ** the old mission target and clear their Tarcom. 401 */ 402 FootClass * unit = Member; 403 if (MissionTarget != TARGET_NONE) { 404 while (unit != NULL) { 405 bool tar = (unit->TarCom == MissionTarget); 406 bool nav = (unit->NavCom == MissionTarget); 407 if (tar || nav) { 408 409 /* 410 ** If the unit was doing something related to the team mission 411 ** then we kick him into guard mode so that he is easy to change 412 ** missions for. 413 */ 414 unit->Assign_Mission(MISSION_GUARD); 415 416 /* 417 ** If the unit's tarcom is set to the old mission target, then 418 ** clear it, so that it will be reset by whatever happens next. 419 */ 420 if (nav) { 421 unit->Assign_Destination(TARGET_NONE); 422 } 423 424 /* 425 ** If the unit's navcom is set to the old mission target, then 426 ** clear it, so that it will be reset by whatever happens next. 427 */ 428 if (tar) { 429 unit->Assign_Target(TARGET_NONE); 430 } 431 } 432 unit = unit->Member; 433 } 434 } 435 436 /* 437 ** If there is not currently an override on the current mission target 438 ** then assign both MissionTarget and Target to the new target. If 439 ** there is an override, allow the team to keep fighting the override but 440 ** make sure they pick up on the new mission when they are ready. 441 */ 442 if (Target == MissionTarget || !Target_Legal(Target)) { 443 MissionTarget = Target = new_target; 444 } else { 445 MissionTarget = new_target; 446 } 447 } 448 449 450 /*********************************************************************************************** 451 * TeamClass::AI -- Process team logic. * 452 * * 453 * General purpose team logic is handled by this routine. It should be called once per * 454 * active team per game tick. This routine handles recruitment and assigning orders to * 455 * member units. * 456 * * 457 * INPUT: none * 458 * * 459 * OUTPUT: none * 460 * * 461 * WARNINGS: none * 462 * * 463 * HISTORY: * 464 * 12/29/1994 JLB : Created. * 465 * 01/06/1995 JLB : Choreographed gesture. * 466 *=============================================================================================*/ 467 void TeamClass::AI(void) 468 { 469 assert(IsActive); 470 assert(Teams.ID(this) == ID); 471 472 int desired = 0; 473 int old_under = IsUnderStrength; 474 int old_full = IsFullStrength; 475 476 /* 477 ** If the team has been suspended then we need to check if it's time for 478 ** us to reactivate the team. If not, no team logic will be processed 479 ** for this team. 480 */ 481 if (Suspended) { 482 if (SuspendTimer != 0) { 483 return; 484 } 485 Suspended = false; 486 } 487 488 /* 489 ** If this team senses that its composition has been altered, then it should 490 ** recalculate the under strength and full strength flags. 491 */ 492 if (IsAltered) { 493 494 /* 495 ** Figure out the total number of objects that this team type requires. 496 */ 497 for (int index = 0; index < Class->ClassCount; index++) { 498 desired += Class->Members[index].Quantity; 499 } 500 assert(desired != 0); 501 502 if (Total) { 503 IsFullStrength = (Total == desired); 504 if (IsFullStrength) { 505 IsHasBeen = true; 506 } 507 508 /* 509 ** Reinforceable teams will revert (or snap out of) the under strength 510 ** mode when the members transition the magic 1/3 strength threshold. 511 */ 512 if (Class->IsReinforcable) { 513 if (desired > 2) { 514 IsUnderStrength = (Total <= desired / 3); 515 } else { 516 IsUnderStrength = (Total < desired); 517 } 518 } else { 519 520 /* 521 ** Teams that are not flagged as reinforceable are never considered under 522 ** strength if the team has already started its main mission. This 523 ** ensures that once the team has started, it won't dally to pick up 524 ** new members. 525 */ 526 IsUnderStrength = !IsHasBeen; 527 } 528 529 IsAltered = JustAltered = false; 530 } else { 531 IsUnderStrength = true; 532 IsFullStrength = false; 533 Zone = TARGET_NONE; 534 535 /* 536 ** A team that exists on the player's side is automatically destroyed 537 ** when there are no team members left. This team was created as a 538 ** result of reinforcement logic and no longer needs to exist when there 539 ** are no more team members. 540 */ 541 if (IsHasBeen || Session.Type != GAME_NORMAL) { 542 543 /* 544 ** If this team had no members (i.e., the team object wasn't terminated by some 545 ** outside means), then pass through the logic triggers to see if one that 546 ** depends on this team leaving the map should be sprung. 547 */ 548 if (IsLeaveMap) { 549 for (int index = 0; index < LogicTriggers.Count(); index++) { 550 TriggerClass * trig = LogicTriggers[index]; 551 if (trig->Spring(TEVENT_LEAVES_MAP)) { 552 index--; 553 if (LogicTriggers.Count() == 0) break; 554 } 555 } 556 } 557 delete this; 558 return; 559 } 560 } 561 562 /* 563 ** If the team has gone from under strength to no longer under 564 ** strength than the team needs to reform. 565 */ 566 if (old_under != IsUnderStrength) { 567 IsReforming = true; 568 } 569 } 570 571 /* 572 ** If the team is under strength, then flag it to regroup. 573 */ 574 if (IsMoving && IsUnderStrength) { 575 IsMoving = false; 576 CurrentMission = -1; 577 if (Total) { 578 Calc_Center(Zone, ClosestMember); 579 580 /* 581 ** When a team is badly damaged and needs to regroup it should 582 ** pick a friendly building to go and regroup at. Its first preference 583 ** should be somewhere near repair factory. If it cannot find a repair 584 ** factory then it should pick another structure that is friendly to 585 ** its side. 586 */ 587 CELL dest = As_Cell(Zone); 588 int max = 0x7FFFFFFF; 589 590 for (int index = 0; index < Buildings.Count(); index++) { 591 BuildingClass * b = Buildings.Ptr(index); 592 593 if (b != NULL && !b->IsInLimbo && b->House == House && b->Class->PrimaryWeapon == NULL) { 594 CELL cell = Coord_Cell(b->Center_Coord()); 595 int dist = ::Distance(b->Center_Coord(), As_Coord(Zone)) * (Map.Cell_Threat(cell, House->Class->House) + 1); 596 597 if (*b == STRUCT_REPAIR) { 598 dist /= 2; 599 } 600 if (dist < max) { 601 cell = Fetch_A_Leader()->Safety_Point(As_Cell(Zone), cell, 2, 4); 602 // cell = Member->Safety_Point(As_Cell(Zone), cell, 2, 4); 603 if (cell != -1) { 604 max = dist; 605 dest = cell; 606 } 607 } 608 } 609 } 610 611 // Should calculate a regroup location. 612 Target = ::As_Target(dest); 613 Coordinate_Move(); 614 return; 615 } else { 616 Zone = TARGET_NONE; 617 } 618 } 619 620 /* 621 ** Flag this team into action when it gets to full strength. Human owned teams are only 622 ** used for reinforcement purposes -- always consider them at full strength. 623 */ 624 if (!IsMoving && (IsFullStrength || IsForcedActive)) { 625 IsMoving = true; 626 IsHasBeen = true; 627 IsUnderStrength = false; 628 629 /* 630 ** Infantry can do a gesture when they start their mission. Pick 631 ** a gesture at random. 632 */ 633 FootClass * techno = Member; 634 DoType doaction = Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2; 635 while (techno) { 636 if (_Is_It_Breathing(techno) && techno->What_Am_I() == RTTI_INFANTRY) { 637 ((InfantryClass *)techno)->Do_Action(doaction); 638 } 639 640 if (IsReforming || IsForcedActive) { 641 techno->IsInitiated = true; 642 } 643 644 techno = techno->Member; 645 } 646 CurrentMission = -1; 647 IsNextMission = true; 648 // IsForcedActive = false; 649 } 650 651 /* 652 ** If the team is moving or if there is no center position for 653 ** the team, then the center position must be recalculated. 654 */ 655 if (IsReforming || IsMoving || Zone == TARGET_NONE || ClosestMember == TARGET_NONE) { 656 Calc_Center(Zone, ClosestMember); 657 } 658 659 /* 660 ** Try to recruit members if there is room to do so for this team. 661 ** Only try to recruit members for a non player controlled team. 662 */ 663 if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((!House->IsHuman || !IsHasBeen) && Session.Type == GAME_NORMAL)) { 664 // if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((/*!House->IsHuman ||*/ !IsHasBeen) && Session.Type == GAME_NORMAL)) { 665 for (int index = 0; index < Class->ClassCount; index++) { 666 if (Quantity[index] < Class->Members[index].Quantity) { 667 Recruit(index); 668 } 669 } 670 } 671 672 /* 673 ** If there are no members of the team and the team has reached 674 ** full strength at one time, then delete the team. 675 */ 676 if (Member == NULL && IsHasBeen) { 677 678 /* 679 ** If this team had no members (i.e., the team object wasn't terminated by some 680 ** outside means), then pass through the logic triggers to see if one that 681 ** depends on this team leaving the map should be sprung. 682 */ 683 if (IsLeaveMap) { 684 for (int index = 0; index < LogicTriggers.Count(); index++) { 685 TriggerClass * trig = LogicTriggers[index]; 686 if (trig->Spring(TEVENT_LEAVES_MAP)) { 687 index--; 688 if (LogicTriggers.Count() == 0) break; 689 } 690 } 691 } 692 delete this; 693 return; 694 } 695 696 /* 697 ** If the mission should be advanced to the next entry, then do so at 698 ** this time. Various events may cause the mission to advance, but it 699 ** all boils down to the following change-mission code. 700 */ 701 if (IsMoving && !IsReforming && IsNextMission) { 702 IsNextMission = false; 703 CurrentMission++; 704 if (CurrentMission < Class->MissionCount) { 705 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 706 707 TimeOut = mission->Data.Value * (TICKS_PER_MINUTE/10); 708 Target = TARGET_NONE; 709 switch (mission->Mission) { 710 711 case TMISSION_MOVECELL: 712 Assign_Mission_Target(::As_Target((CELL)(mission->Data.Value))); 713 break; 714 715 case TMISSION_MOVE: 716 if ((unsigned)mission->Data.Value < WAYPT_COUNT && Member != NULL) { 717 FootClass * leader = Fetch_A_Leader(); 718 CELL movecell = Scen.Waypoint[mission->Data.Value]; 719 if (!Is_Leaving_Map()) { 720 if (leader->Can_Enter_Cell(movecell) != MOVE_OK) { 721 movecell = Map.Nearby_Location(movecell, leader->Techno_Type_Class()->Speed); 722 } 723 } 724 Assign_Mission_Target(::As_Target(movecell)); 725 Target = ::As_Target(movecell); 726 } 727 break; 728 729 case TMISSION_ATT_WAYPT: 730 case TMISSION_PATROL: 731 case TMISSION_SPY: 732 if ((unsigned)mission->Data.Value < WAYPT_COUNT) { 733 Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); 734 } 735 break; 736 737 case TMISSION_ATTACKTARCOM: 738 Assign_Mission_Target(mission->Data.Value); 739 break; 740 741 case TMISSION_UNLOAD: 742 default: 743 Assign_Mission_Target(TARGET_NONE); 744 break; 745 } 746 } else { 747 delete this; 748 return; 749 } 750 } 751 752 /* 753 ** Perform mission of the team. This depends on the mission list. 754 */ 755 if (Member != NULL && IsMoving && !IsReforming && !IsUnderStrength) { 756 757 /* 758 ** If the current Target has been dealt with but the mission target 759 ** has not, then the current target needs to be reset to the mission 760 ** target. 761 */ 762 if (!Target_Legal(Target)) { 763 Target = MissionTarget; 764 } 765 766 /* 767 ** If the current mission is one that times out, then check for 768 ** this case. If it has timed out then advance to the next 769 ** mission in the list or disband the team. 770 */ 771 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 772 // FootClass * member = Member; 773 774 switch (mission->Mission) { 775 case TMISSION_PATROL: 776 TMission_Patrol(); 777 break; 778 779 case TMISSION_FORMATION: 780 TMission_Formation(); 781 break; 782 783 case TMISSION_ATTACKTARCOM: 784 case TMISSION_ATTACK: 785 TMission_Attack(); 786 break; 787 788 case TMISSION_LOAD: 789 TMission_Load(); 790 break; 791 792 case TMISSION_DEPLOY: 793 TMission_Deploy(); 794 break; 795 796 case TMISSION_UNLOAD: 797 TMission_Unload(); 798 break; 799 800 case TMISSION_MOVE: 801 case TMISSION_MOVECELL: 802 Coordinate_Move(); 803 break; 804 805 /* 806 ** All members of this team become invulnerable as if by magic. 807 */ 808 case TMISSION_INVULNERABLE: 809 TMission_Invulnerable(); 810 break; 811 812 case TMISSION_GUARD: 813 Coordinate_Regroup(); 814 break; 815 816 case TMISSION_DO: 817 Coordinate_Do(); 818 break; 819 820 case TMISSION_SET_GLOBAL: 821 TMission_Set_Global(); 822 break; 823 824 case TMISSION_ATT_WAYPT: 825 if (!Target_Legal(MissionTarget)) { 826 Assign_Mission_Target(TARGET_NONE); 827 IsNextMission = true; 828 } else { 829 Coordinate_Attack(); 830 } 831 break; 832 833 case TMISSION_SPY: 834 TMission_Spy(); 835 break; 836 837 case TMISSION_HOUND_DOG: 838 TMission_Follow(); 839 break; 840 841 case TMISSION_LOOP: 842 TMission_Loop(); 843 break; 844 } 845 846 /* 847 ** Check for mission time out condition. If the mission does in fact time out, then 848 ** flag it so that the team mission list will advance. 849 */ 850 switch (mission->Mission) { 851 // case TMISSION_UNLOAD: 852 case TMISSION_GUARD: 853 if (TimeOut == 0) { 854 IsNextMission = true; 855 } 856 break; 857 } 858 859 } else { 860 861 if (IsMoving) { 862 IsReforming = !Coordinate_Regroup(); 863 } else { 864 Coordinate_Move(); 865 } 866 } 867 } 868 869 870 /*********************************************************************************************** 871 * TeamClass::Add -- Adds specified object to team. * 872 * * 873 * Use this routine to add the specified object to the team. The object is checked to make * 874 * sure that it can be assigned to the team. If it can't, then the object will be left * 875 * alone and false will be returned. * 876 * * 877 * INPUT: obj -- Pointer to the object that is to be assigned to this team. * 878 * * 879 * OUTPUT: bool; Was the unit added to the team? * 880 * * 881 * WARNINGS: none * 882 * * 883 * HISTORY: * 884 * 12/29/1994 JLB : Created. * 885 * 01/02/1995 JLB : Initiation flag setup. * 886 * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * 887 *=============================================================================================*/ 888 bool TeamClass::Add(FootClass * obj) 889 { 890 assert(IsActive); 891 assert(Teams.ID(this) == ID); 892 if (!obj) return(false); 893 894 int typeindex; 895 if (!Can_Add(obj, typeindex)) return(false); 896 897 /* 898 ** All is ok to add the object to the team, but if the object is already part of 899 ** another team, then it must be removed from that team first. 900 */ 901 if (obj->Team.Is_Valid()) { 902 obj->Team->Remove(obj); 903 } 904 905 /* 906 ** Actually add the object to the team. 907 */ 908 Quantity[typeindex]++; 909 obj->IsInitiated = (Member == NULL); 910 obj->Member = Member; 911 Member = obj; 912 obj->Team = this; 913 914 /* 915 ** If a common trigger is designated for this team type, then attach the 916 ** trigger to this team member. 917 */ 918 if (Trigger.Is_Valid()) { 919 obj->Attach_Trigger(Trigger); 920 } 921 922 Total++; 923 Risk += obj->Risk(); 924 if (Zone == TARGET_NONE) { 925 Calc_Center(Zone, ClosestMember); 926 } 927 928 /* 929 ** Return with success, since the object was added to the team. 930 */ 931 IsAltered = JustAltered = true; 932 return(true); 933 } 934 935 936 /*********************************************************************************************** 937 * TeamClass::Can_Add -- Determines if the specified object can be added to team. * 938 * * 939 * This routine will examine the team and determine if the specified object can be * 940 * properly added to this team. This is a security check to filter out those objects that * 941 * should not be added because of conflicting priorities or other restrictions. * 942 * * 943 * INPUT: obj -- Pointer to the candidate object that is being checked for legal * 944 * adding to this team. * 945 * * 946 * typeindex-- The class index number (according to the team type's class array) that * 947 * the candidate object is classified as. The routine processes much * 948 * faster if you can provide this information, but if you don't, the * 949 * routine will figure it out. * 950 * * 951 * OUTPUT: bool; Can the specified object be added to this team? * 952 * * 953 * WARNINGS: none * 954 * * 955 * HISTORY: * 956 * 02/27/1996 JLB : Created. * 957 *=============================================================================================*/ 958 bool TeamClass::Can_Add(FootClass * obj, int & typeindex) const 959 { 960 assert(IsActive); 961 assert(Teams.ID(this) == ID); 962 963 /* 964 ** Trying to add the team member to itself is an error condition. 965 */ 966 if (obj->Team == this) { 967 return(false); 968 } 969 970 /* 971 ** The object must be active, a member of this house. A special dispensation is given to 972 ** units that are in radio contact. It is presumed that they are very busy and should 973 ** not be disturbed. 974 */ 975 if (!_Is_It_Breathing(obj) || obj->In_Radio_Contact() || obj->House != House) { 976 return(false); 977 } 978 979 /* 980 ** If the object is doing some mission that precludes it from joining 981 ** a team then don't add it. 982 */ 983 if (obj->Mission != MISSION_NONE && !MissionClass::Is_Recruitable_Mission(obj->Mission)) { 984 return(false); 985 } 986 987 /* 988 ** If this object is part of another team, then check to make sure that it 989 ** is permitted to leave the other team in order to join this one. If not, 990 ** then no further processing is allowed -- bail. 991 */ 992 if (obj->Team.Is_Valid() && (obj->Team->Class->RecruitPriority >= Class->RecruitPriority)) { 993 return(false); 994 } 995 996 /* 997 ** Aircraft that have no ammo for their weapons cannot be recruited into a team. 998 */ 999 if (obj->What_Am_I() == RTTI_AIRCRAFT && obj->Techno_Type_Class()->PrimaryWeapon != NULL && !obj->Ammo) { 1000 return(false); 1001 } 1002 1003 /* 1004 ** Search for the exact member index that the candidate object matches. 1005 ** If no match could be found, then adding the object to the team cannot 1006 ** occur. 1007 */ 1008 for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { 1009 if (Class->Members[typeindex].Class == &obj->Class_Of()) { 1010 break; 1011 } 1012 } 1013 if (typeindex == Class->ClassCount) { 1014 return(false); 1015 } 1016 1017 /* 1018 ** If the team is already full of this type, then adding the object is not allowed. 1019 ** Return with a failure flag in this case. 1020 */ 1021 if (Quantity[typeindex] >= Class->Members[typeindex].Quantity) { 1022 return(false); 1023 } 1024 1025 return(true); 1026 } 1027 1028 1029 1030 /*********************************************************************************************** 1031 * TeamClass::Remove -- Removes the specified object from the team. * 1032 * * 1033 * Use this routine to remove an object from a team. Objects removed from the team are * 1034 * then available to be recruited by other teams, or even by the same team at a later time. * 1035 * * 1036 * INPUT: obj -- Pointer to the object that is to be removed from this team. * 1037 * * 1038 * typeindex-- Optional index of where this object type is specified in the type * 1039 * type class. This parameter can be omitted. It only serves to make * 1040 * the removal process faster. * 1041 * * 1042 * OUTPUT: bool; Was the object removed from this team? * 1043 * * 1044 * WARNINGS: none * 1045 * * 1046 * HISTORY: * 1047 * 12/29/1994 JLB : Created. * 1048 * 01/02/1995 JLB : Initiation tracking and team captain selection. * 1049 *=============================================================================================*/ 1050 bool TeamClass::Remove(FootClass * obj, int typeindex) 1051 { 1052 assert(IsActive); 1053 assert(Teams.ID(this) == ID); 1054 1055 /* 1056 ** Make sure that the object is in fact a member of this team. If not, then it can't 1057 ** be removed. Return success because the end result is the same. 1058 */ 1059 if (this != obj->Team) { 1060 return(true); 1061 } 1062 1063 /* 1064 ** Detach the common trigger for this team type. Only current and active members of the 1065 ** team have that trigger attached. The exception is for player team members that 1066 ** get removed from a reinforcement team. 1067 */ 1068 if (obj->Trigger == Trigger && !obj->House->IsPlayerControl) { 1069 obj->Attach_Trigger(NULL); 1070 } 1071 1072 /* 1073 ** If the proper team index was not provided, then find it in the type type class. The 1074 ** team type class will not be set if the appropriate type could not be found 1075 ** for this object. This indicates that the object was illegally added. Continue to 1076 ** process however, since removing this object from the team is a good idea. 1077 */ 1078 if (typeindex == -1) { 1079 for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { 1080 if (Class->Members[typeindex].Class == &obj->Class_Of()) { 1081 break; 1082 } 1083 } 1084 } 1085 1086 /* 1087 ** Decrement the counter for the team class. There is now one less of this object type. 1088 */ 1089 if (typeindex < Class->ClassCount) { 1090 Quantity[typeindex]--; 1091 } 1092 1093 /* 1094 ** Actually remove the object from the team. Scan through the team members 1095 ** looking for the one that matches the one specified. If it is found, it 1096 ** is unlinked from the member chain. During this scan, a check is made to 1097 ** ensure that at least one remaining member is still initiated. If not, then 1098 ** a new team captain must be chosen. 1099 */ 1100 bool initiated = false; 1101 FootClass * prev = 0; 1102 FootClass * curr = Member; 1103 bool found = false; 1104 while (curr != NULL && (!found || !initiated)) { 1105 if (curr == obj) { 1106 if (prev != NULL) { 1107 prev->Member = curr->Member; 1108 } else { 1109 Member = curr->Member; 1110 } 1111 FootClass * temp = curr->Member; 1112 curr->Member = 0; 1113 curr->Team = 0; 1114 curr->SuspendedMission = MISSION_NONE; 1115 curr->SuspendedNavCom = TARGET_NONE; 1116 curr->SuspendedTarCom = TARGET_NONE; 1117 curr = temp; 1118 Total--; 1119 found = true; 1120 Risk -= obj->Risk(); 1121 continue; 1122 } 1123 1124 /* 1125 ** If this (remaining) member is initiated, then keep a record of this. 1126 */ 1127 initiated |= curr->IsInitiated; 1128 1129 prev = curr; 1130 curr = curr->Member; 1131 } 1132 1133 /* 1134 ** A unit that breaks off of a team will enter idle mode. 1135 */ 1136 obj->Enter_Idle_Mode(); 1137 1138 /* 1139 ** If, after removing the team member, there are no initiated members left 1140 ** in the team, then just make the first remaining member of the team the 1141 ** team captain. Mark the center location of the team as invalid so that 1142 ** it will be centered around the captain. 1143 */ 1144 if (!initiated && Member != NULL) { 1145 Member->IsInitiated = true; 1146 Zone = TARGET_NONE; 1147 } 1148 1149 /* 1150 ** Must record that the team composition has changed. At the next opportunity, 1151 ** the team members will be counted and appropriate AI adjustments made. 1152 */ 1153 IsAltered = JustAltered = true; 1154 return(true); 1155 } 1156 1157 1158 /*********************************************************************************************** 1159 * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * 1160 * * 1161 * This routine will take the given index ID and scan for available objects of that type * 1162 * to recruit to the team. Recruiting will continue until that object type has either * 1163 * been exhausted or if the team's requirement for that type has been filled. * 1164 * * 1165 * INPUT: typeindex -- The index for the object type to recruit. The index is used to * 1166 * look into the type type's array of object types that make up this * 1167 * team. * 1168 * * 1169 * OUTPUT: Returns with the number of objects added to this team. * 1170 * * 1171 * WARNINGS: none * 1172 * * 1173 * HISTORY: * 1174 * 12/29/1994 JLB : Created. * 1175 * 04/10/1995 JLB : Scans for units too. * 1176 *=============================================================================================*/ 1177 int TeamClass::Recruit(int typeindex) 1178 { 1179 assert(IsActive); 1180 assert(Teams.ID(this) == ID); 1181 COORDINATE center = As_Coord(Zone); 1182 1183 if (Class->Origin != -1) { 1184 center = Cell_Coord(Scen.Waypoint[Class->Origin]); 1185 } 1186 1187 int added = 0; // Total number added to team. 1188 1189 /* 1190 ** Quick check to see if recruiting is really allowed for this index or not. 1191 */ 1192 if (Class->Members[typeindex].Quantity > Quantity[typeindex]) { 1193 switch (Class->Members[typeindex].Class->What_Am_I()) { 1194 1195 /* 1196 ** For infantry objects, sweep through the infantry in the game looking for 1197 ** ones owned by the house that owns the team. When found, try to add. 1198 */ 1199 case RTTI_INFANTRYTYPE: 1200 case RTTI_INFANTRY: 1201 { 1202 InfantryClass * best = 0; 1203 int bestdist = -1; 1204 1205 for (int index = 0; index < Infantry.Count(); index++) { 1206 InfantryClass * infantry = Infantry.Ptr(index); 1207 int d = infantry->Distance(center); 1208 1209 if ((d < bestdist || bestdist == -1) && Can_Add(infantry, typeindex)) { 1210 best = infantry; 1211 bestdist = d; 1212 } 1213 } 1214 1215 if (best) { 1216 best->Assign_Target(TARGET_NONE); 1217 Add(best); 1218 added++; 1219 } 1220 } 1221 break; 1222 1223 case RTTI_AIRCRAFTTYPE: 1224 case RTTI_AIRCRAFT: 1225 { 1226 AircraftClass * best = 0; 1227 int bestdist = -1; 1228 1229 for (int index = 0; index < Aircraft.Count(); index++) { 1230 AircraftClass * aircraft = Aircraft.Ptr(index); 1231 int d = aircraft->Distance(center); 1232 1233 if ((d < bestdist || bestdist == -1) && Can_Add(aircraft, typeindex)) { 1234 best = aircraft; 1235 bestdist = d; 1236 } 1237 } 1238 1239 if (best) { 1240 best->Assign_Target(TARGET_NONE); 1241 Add(best); 1242 added++; 1243 } 1244 } 1245 break; 1246 1247 case RTTI_UNITTYPE: 1248 case RTTI_UNIT: 1249 { 1250 UnitClass * best = 0; 1251 int bestdist = -1; 1252 1253 for (int index = 0; index < Units.Count(); index++) { 1254 UnitClass * unit = Units.Ptr(index); 1255 int d = unit->Distance(center); 1256 1257 if (unit->House == House && unit->Class == Class->Members[typeindex].Class) { 1258 1259 if ((d < bestdist || bestdist == -1) && Can_Add(unit, typeindex)) { 1260 best = unit; 1261 bestdist = d; 1262 } 1263 1264 } 1265 1266 if (best) { 1267 best->Assign_Target(TARGET_NONE); 1268 Add(best); 1269 added++; 1270 1271 /* 1272 ** If a transport is added to the team, the occupants 1273 ** are added by default. 1274 */ 1275 FootClass * f = best->Attached_Object(); 1276 while (f) { 1277 Add(f); 1278 f = (FootClass *)(ObjectClass *)f->Next; 1279 } 1280 } 1281 } 1282 } 1283 break; 1284 1285 case RTTI_VESSELTYPE: 1286 case RTTI_VESSEL: 1287 { 1288 VesselClass * best = 0; 1289 int bestdist = -1; 1290 1291 for (int index = 0; index < Vessels.Count(); index++) { 1292 VesselClass * vessel = Vessels.Ptr(index); 1293 int d = vessel->Distance(center); 1294 1295 if (vessel->House == House && vessel->Class == Class->Members[typeindex].Class) { 1296 1297 if ((d < bestdist || bestdist == -1) && Can_Add(vessel, typeindex)) { 1298 best = vessel; 1299 bestdist = d; 1300 } 1301 1302 } 1303 1304 if (best) { 1305 best->Assign_Target(TARGET_NONE); 1306 Add(best); 1307 added++; 1308 1309 /* 1310 ** If a transport is added to the team, the occupants 1311 ** are added by default. 1312 */ 1313 FootClass * f = best->Attached_Object(); 1314 while (f) { 1315 Add(f); 1316 f = (FootClass *)(ObjectClass *)f->Next; 1317 } 1318 } 1319 } 1320 } 1321 break; 1322 } 1323 } 1324 return(added); 1325 } 1326 1327 1328 /*********************************************************************************************** 1329 * TeamClass::Detach -- Removes specified target from team tracking. * 1330 * * 1331 * When a target object is about to be removed from the game (e.g., it was killed), then * 1332 * any team that is looking at that target must abort from that target. * 1333 * * 1334 * INPUT: target -- The target object that is going to be removed from the game. * 1335 * * 1336 * all -- Is the target going away for good as opposed to just cloaking/hiding? * 1337 * * 1338 * OUTPUT: none * 1339 * * 1340 * WARNINGS: none * 1341 * * 1342 * HISTORY: * 1343 * 12/29/1994 JLB : Created. * 1344 *=============================================================================================*/ 1345 void TeamClass::Detach(TARGET target, bool ) 1346 { 1347 assert(IsActive); 1348 assert(Teams.ID(this) == ID); 1349 1350 /* 1351 ** If the target to detach matches the target of this team, then remove 1352 ** the target from this team's Tar/Nav com and let the chips fall 1353 ** where they may. 1354 */ 1355 if (Target == target) { 1356 Target = TARGET_NONE; 1357 } 1358 if (MissionTarget == target) { 1359 MissionTarget = TARGET_NONE; 1360 } 1361 if (Trigger.Is_Valid() && Trigger->As_Target() == target) { 1362 Trigger = 0; 1363 } 1364 } 1365 1366 1367 /*********************************************************************************************** 1368 * TeamClass::Calc_Center -- Determines average location of team members. * 1369 * * 1370 * Use this routine to calculate the "center" location of the team. This is the average * 1371 * position of all members of the team. Using this center value it is possible to tell * 1372 * if a team member is too far away and where to head to in order to group up. * 1373 * * 1374 * INPUT: center -- Average center target location of the team. Only initiated members * 1375 * will be considered. * 1376 * * 1377 * close_member--Location (as target) of the unit that is closest to the team's * 1378 * target. * 1379 * * 1380 * OUTPUT: none * 1381 * * 1382 * WARNINGS: none * 1383 * * 1384 * HISTORY: * 1385 * 12/29/1994 JLB : Created. * 1386 *=============================================================================================*/ 1387 void TeamClass::Calc_Center(TARGET & center, TARGET & close_member) const 1388 { 1389 assert(IsActive); 1390 assert(Teams.ID(this) == ID); 1391 1392 /* 1393 ** Presume there is no center. This will be confirmed in the following scanning 1394 ** operation. 1395 */ 1396 close_member = TARGET_NONE; 1397 center = TARGET_NONE; 1398 1399 FootClass const * team_member = Member; // Working team member pointer. 1400 1401 /* 1402 ** If there are no members of the team, then there can be no center point of the team. 1403 */ 1404 if (team_member == NULL) return; 1405 1406 /* 1407 ** If the team is supposed to follow a nearby friendly unit, then the 1408 ** team's "center" will actually be that unit. Otherwise, calculated the 1409 ** average center location for the team. 1410 */ 1411 if (Class->MissionList[CurrentMission].Mission == TMISSION_HOUND_DOG) { 1412 1413 /* 1414 ** First pick a member of the team. The closest friendly object to that member 1415 ** will be picked. 1416 */ 1417 if (!team_member) return; 1418 1419 FootClass const * closest = NULL; // Current closest friendly object. 1420 int distance = -1; // Record of last closest distance calc. 1421 1422 /* 1423 ** Scan through all vehicles. 1424 */ 1425 for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { 1426 FootClass const * trial_unit = Units.Ptr(unit_index); 1427 1428 if (_Is_It_Breathing(trial_unit) && trial_unit->House->Is_Ally(House) && trial_unit->Team != this) { 1429 int trial_distance = team_member->Distance(trial_unit); 1430 1431 if (distance == -1 || trial_distance < distance) { 1432 distance = trial_distance; 1433 closest = trial_unit; 1434 } 1435 } 1436 } 1437 1438 /* 1439 ** Scan through all infantry. 1440 */ 1441 for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { 1442 FootClass const * trial_infantry = Infantry.Ptr(infantry_index); 1443 1444 if (_Is_It_Breathing(trial_infantry) && trial_infantry->House->Is_Ally(House) && trial_infantry->Team != this) { 1445 int trial_distance = team_member->Distance(trial_infantry); 1446 1447 if (distance == -1 || trial_distance < distance) { 1448 distance = trial_distance; 1449 closest = trial_infantry; 1450 } 1451 } 1452 } 1453 1454 /* 1455 ** Scan through all vessels. 1456 */ 1457 for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { 1458 FootClass const * trial_vessel = Vessels.Ptr(vessel_index); 1459 1460 if (_Is_It_Breathing(trial_vessel) && trial_vessel->House->Is_Ally(House) && trial_vessel->Team != this) { 1461 int trial_distance = team_member->Distance(trial_vessel); 1462 1463 if (distance == -1 || trial_distance < distance) { 1464 distance = trial_distance; 1465 closest = trial_vessel; 1466 } 1467 } 1468 } 1469 1470 /* 1471 ** Set the center location as actually the friendly object that is closest. If there 1472 ** is no friendly object, then don't set any center location at all. 1473 */ 1474 if (closest) { 1475 center = closest->As_Target(); 1476 close_member = Member->As_Target(); 1477 } 1478 1479 } else { 1480 1481 long x = 0; // Accumulated X coordinate. 1482 long y = 0; // Accumulated Y coordinate. 1483 int dist = 0; // Closest recorded distance to team target. 1484 int quantity = 0; // Number of team members counted. 1485 FootClass const * closest = 0; // Closest member to target. 1486 1487 /* 1488 ** Scan through all team members and accumulate the X and Y component of their 1489 ** location. Only team members that are active will be considered. Also keep 1490 ** track of the team member that is closest to the team's target. 1491 */ 1492 while (team_member != NULL) { 1493 if (_Is_It_Playing(team_member)) { 1494 1495 /* 1496 ** Accumulate X and Y components of qualified team members. 1497 */ 1498 x += Coord_X(team_member->Coord); 1499 y += Coord_Y(team_member->Coord); 1500 quantity++; 1501 1502 /* 1503 ** Keep a record of the team member that is nearest to the team's 1504 ** target. 1505 */ 1506 int try_dist = team_member->Distance(Target); 1507 if (!dist || try_dist < dist) { 1508 dist = try_dist; 1509 closest = team_member; 1510 } 1511 } 1512 1513 team_member = team_member->Member; 1514 } 1515 1516 /* 1517 ** If there were any qualifying members, then the team's center point can be 1518 ** determined. 1519 */ 1520 if (quantity) { 1521 x /= quantity; 1522 y /= quantity; 1523 COORDINATE coord = XY_Coord((int)x, (int)y); 1524 center = ::As_Target(coord); 1525 1526 1527 /* 1528 ** If the center location is impassable, then just pick the location of 1529 ** one of the team members. 1530 */ 1531 if (!closest->Can_Enter_Cell(As_Cell(center))) { 1532 // if (Class->Origin != -1) { 1533 // center = ::As_Target(Scen.Waypoint[Class->Origin]); 1534 // } else { 1535 center = ::As_Target(Coord_Cell(closest->Center_Coord())); 1536 // } 1537 } 1538 } 1539 1540 /* 1541 ** Record the position of the closest member to the team's target and 1542 ** that will be used as the regroup point. 1543 */ 1544 if (closest != NULL) { 1545 close_member = ::As_Target(Coord_Cell(closest->Center_Coord())); 1546 } 1547 } 1548 } 1549 1550 1551 /*********************************************************************************************** 1552 * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * 1553 * * 1554 * This routine is used when a team member takes damage. Usually the team will react in * 1555 * some fashion to the attack. This reaction can range from running away to assigning this * 1556 * new target as the team's target. * 1557 * * 1558 * INPUT: obj -- The team member that was damaged. * 1559 * * 1560 * result -- The severity of the damage taken. * 1561 * * 1562 * source -- The perpetrator of the damage. * 1563 * * 1564 * OUTPUT: none * 1565 * * 1566 * WARNINGS: none * 1567 * * 1568 * HISTORY: * 1569 * 12/29/1994 JLB : Created. * 1570 *=============================================================================================*/ 1571 void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) 1572 { 1573 assert(IsActive); 1574 assert(Teams.ID(this) == ID); 1575 1576 if ((result != RESULT_NONE) && (!Class->IsSuicide)) { 1577 if (!IsMoving) { 1578 1579 // TCTCTC 1580 // Should run to a better hiding place or disband into a group of hunting units. 1581 1582 } else { 1583 /* 1584 ** Respond to the attack, but not if we're an aircraft or a LST. 1585 */ 1586 if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT && (Member->What_Am_I() != RTTI_VESSEL || *(VesselClass *)((FootClass *)Member) != VESSEL_TRANSPORT)) { 1587 if (Target != source->As_Target()) { 1588 1589 /* 1590 ** Don't change target if the team's target is one that can fire as well. There is 1591 ** no point in endlessly shuffling between targets that have firepower. 1592 */ 1593 if (Target_Legal(Target)) { 1594 TechnoClass * techno = As_Techno(Target); 1595 1596 if (techno && ((TechnoTypeClass const &)techno->Class_Of()).PrimaryWeapon != NULL) { 1597 if (techno->In_Range(As_Coord(Zone), 0)) { 1598 return; 1599 } 1600 } 1601 } 1602 1603 /* 1604 ** Don't change target to aggressor if the aggressor cannot normally be attacked. 1605 */ 1606 if (source->What_Am_I() == RTTI_AIRCRAFT || (source->What_Am_I() == RTTI_VESSEL && (Member->What_Am_I() == RTTI_UNIT || Member->What_Am_I() == RTTI_INFANTRY))) { 1607 return; 1608 } 1609 1610 Target = source->As_Target(); 1611 } 1612 } 1613 } 1614 } 1615 } 1616 1617 1618 /*********************************************************************************************** 1619 * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * 1620 * * 1621 * This function is called when the team knows what it should attack. This routine will * 1622 * give the necessary orders to the members of the team. * 1623 * * 1624 * INPUT: none * 1625 * * 1626 * OUTPUT: none * 1627 * * 1628 * WARNINGS: none * 1629 * * 1630 * HISTORY: * 1631 * 04/06/1995 JLB : Created. * 1632 *=============================================================================================*/ 1633 void TeamClass::Coordinate_Attack(void) 1634 { 1635 assert(IsActive); 1636 assert(Teams.ID(this) == ID); 1637 1638 if (!Target_Legal(Target)) { 1639 Target = MissionTarget; 1640 } 1641 1642 /* 1643 ** Check if they're attacking a cell. If the contents of the cell are 1644 ** a bridge or a building/unit/techno, then it's a valid target. Otherwise, 1645 ** the target is invalid. This only applies to non-aircraft teams. An aircraft team 1646 ** can "attack" an empty cell and this is perfectly ok (paratrooper drop and parabombs 1647 ** are prime examples). 1648 */ 1649 if (Is_Target_Cell(Target) && Member != NULL && Fetch_A_Leader()->What_Am_I() != RTTI_AIRCRAFT) { 1650 CellClass *cellptr = &Map[As_Cell(Target)]; 1651 TemplateType tt = cellptr->TType; 1652 if (cellptr->Cell_Object()) { 1653 Target = cellptr->Cell_Object()->As_Target(); 1654 } else { 1655 if (tt != TEMPLATE_BRIDGE1 && tt != TEMPLATE_BRIDGE2 && 1656 tt != TEMPLATE_BRIDGE1H && tt != TEMPLATE_BRIDGE2H && 1657 tt != TEMPLATE_BRIDGE_1A && tt != TEMPLATE_BRIDGE_1B && 1658 tt != TEMPLATE_BRIDGE_2A && tt != TEMPLATE_BRIDGE_2B && 1659 tt != TEMPLATE_BRIDGE_3A && tt != TEMPLATE_BRIDGE_3B ) { 1660 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1661 FootClass *unit = Member; 1662 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 1663 if(unit->What_Am_I() != RTTI_UNIT || 1664 *(UnitClass *)unit != UNIT_CHRONOTANK || 1665 mission->Mission != TMISSION_SPY) 1666 #endif 1667 Target = 0; // invalidize the target so it'll go to next mission. 1668 } 1669 } 1670 } 1671 1672 if (!Target_Legal(Target)) { 1673 IsNextMission = true; 1674 1675 } else { 1676 1677 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 1678 1679 FootClass * unit = Member; 1680 while (unit != NULL) { 1681 1682 Coordinate_Conscript(unit); 1683 1684 if (_Is_It_Playing(unit)) { 1685 if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)unit == INFANTRY_SPY) { 1686 unit->Assign_Mission(MISSION_CAPTURE); 1687 unit->Assign_Target(Target); 1688 } else { 1689 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1690 if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_CHRONOTANK) { 1691 UnitClass *tank = (UnitClass *)unit; 1692 tank->Teleport_To(::As_Cell(Target)); 1693 tank->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; 1694 Scen.Do_BW_Fade(); 1695 Sound_Effect(VOC_CHRONOTANK1, unit->Coord); 1696 tank->Assign_Target(TARGET_NONE); 1697 tank->Assign_Mission(MISSION_GUARD); 1698 } else { 1699 #endif 1700 if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { 1701 unit->Transmit_Message(RADIO_OVER_OUT); 1702 unit->Assign_Mission(MISSION_ATTACK); 1703 unit->Assign_Target(TARGET_NONE); 1704 unit->Assign_Destination(TARGET_NONE); 1705 } 1706 } 1707 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1708 } 1709 #endif 1710 if (unit->TarCom != Target) { 1711 unit->Assign_Target(Target); 1712 } 1713 } 1714 1715 unit = unit->Member; 1716 } 1717 } 1718 } 1719 1720 1721 /*********************************************************************************************** 1722 * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * 1723 * * 1724 * This routine is called when the team must delay at its current location. Team members * 1725 * are grouped together by this function. It is called when the team needs to sit and * 1726 * wait. * 1727 * * 1728 * INPUT: none * 1729 * * 1730 * OUTPUT: bool; Has the team completely regrouped? * 1731 * * 1732 * WARNINGS: none * 1733 * * 1734 * HISTORY: * 1735 * 04/06/1995 JLB : Created. * 1736 *=============================================================================================*/ 1737 bool TeamClass::Coordinate_Regroup(void) 1738 { 1739 assert(IsActive); 1740 assert(Teams.ID(this) == ID); 1741 1742 FootClass * unit = Member; 1743 bool retval = true; 1744 1745 /* 1746 ** Regroup default logic. 1747 */ 1748 while (unit != NULL) { 1749 1750 Coordinate_Conscript(unit); 1751 1752 if (_Is_It_Playing(unit)) { 1753 1754 if (unit->Distance(Zone) > Rule.StrayDistance && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { 1755 if (!Target_Legal(unit->NavCom)) { 1756 // TCTCTC 1757 // if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, Zone) > Rule.StrayDistance) { 1758 unit->Assign_Mission(MISSION_MOVE); 1759 unit->Assign_Destination(Zone); 1760 1761 retval = false; 1762 if (!unit->IsFormationMove) { 1763 unit->Assign_Mission(MISSION_MOVE); 1764 CELL dest = unit->Adjust_Dest(As_Cell(Zone)); 1765 unit->Assign_Destination(::As_Target(dest)); 1766 } else { 1767 retval = true; // formations are always considered regrouped. 1768 } 1769 } 1770 } else { 1771 1772 /* 1773 ** The team is regrouping, so just sit here and wait. 1774 */ 1775 if (unit->Mission != MISSION_GUARD_AREA) { 1776 unit->Assign_Mission(MISSION_GUARD); 1777 unit->Assign_Destination(TARGET_NONE); 1778 } 1779 } 1780 1781 } 1782 1783 unit = unit->Member; 1784 } 1785 return(retval); 1786 } 1787 1788 1789 /*********************************************************************************************** 1790 * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * 1791 * * 1792 * This will assign the specified mission to the team. If there are team members that are * 1793 * too far away from the center of the team, then they will be told to move to the team's * 1794 * location. * 1795 * * 1796 * INPUT: none * 1797 * * 1798 * OUTPUT: none * 1799 * * 1800 * WARNINGS: This only works if the special mission the team members are to perform does not * 1801 * require extra parameters. The ATTACK and MOVE missions are particularly bad. * 1802 * * 1803 * HISTORY: * 1804 * 05/11/1996 JLB : Created. * 1805 *=============================================================================================*/ 1806 void TeamClass::Coordinate_Do(void) 1807 { 1808 assert(IsActive); 1809 assert(Teams.ID(this) == ID); 1810 1811 FootClass * unit = Member; 1812 MissionType do_mission = Class->MissionList[CurrentMission].Data.Mission; 1813 1814 /* 1815 ** For each unit either head it back to the team center or give it the main 1816 ** team mission order as appropriate. 1817 */ 1818 while (unit != NULL) { 1819 1820 Coordinate_Conscript(unit); 1821 1822 if (_Is_It_Playing(unit)) { 1823 1824 if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Distance(Zone) > Rule.StrayDistance * 2) { 1825 1826 /* 1827 ** Only if the unit isn't already heading to regroup with the team, will it 1828 ** be given orders to do so. 1829 */ 1830 unit->Assign_Mission(MISSION_MOVE); 1831 unit->Assign_Destination(Zone); 1832 unit->Assign_Mission(MISSION_MOVE); 1833 CELL dest = unit->Adjust_Dest(As_Cell(Zone)); 1834 unit->Assign_Destination(::As_Target(dest)); 1835 1836 } else { 1837 1838 /* 1839 ** The team is regrouping, so just sit here and wait. 1840 */ 1841 if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Mission != do_mission) { 1842 unit->ArchiveTarget = TARGET_NONE; 1843 unit->Assign_Mission(do_mission); 1844 unit->Assign_Target(TARGET_NONE); 1845 unit->Assign_Destination(TARGET_NONE); 1846 } 1847 } 1848 1849 } 1850 1851 unit = unit->Member; 1852 } 1853 } 1854 1855 1856 /*********************************************************************************************** 1857 * TeamClass::Coordinate_Move -- Handles team movement coordination. * 1858 * * 1859 * This routine is called when the team must move to a new location. Movement and grouping * 1860 * commands associated with this task are initiated here. * 1861 * * 1862 * INPUT: none * 1863 * * 1864 * OUTPUT: none * 1865 * * 1866 * WARNINGS: none * 1867 * * 1868 * HISTORY: * 1869 * 04/06/1995 JLB : Created. * 1870 *=============================================================================================*/ 1871 void TeamClass::Coordinate_Move(void) 1872 { 1873 assert(IsActive); 1874 assert(Teams.ID(this) == ID); 1875 1876 FootClass * unit = Member; 1877 bool finished = true; 1878 bool found = false; 1879 1880 if (!Target_Legal(Target)) { 1881 Target = MissionTarget; 1882 } 1883 1884 if (Target_Legal(Target)) { 1885 1886 if (!Lagging_Units()) { 1887 1888 while (unit != NULL) { 1889 1890 /* 1891 ** Tell the unit, if necessary, that it should regroup 1892 ** with the main team location. If the unit is regrouping, then 1893 ** the team should continue NOT qualify as fully reaching the desired 1894 ** location. 1895 */ 1896 if (Coordinate_Conscript(unit)) { 1897 finished = false; 1898 } 1899 1900 if (unit->Mission == MISSION_UNLOAD || unit->MissionQueue == MISSION_UNLOAD) { 1901 finished = false; 1902 } 1903 1904 if (_Is_It_Playing(unit) && unit->Mission != MISSION_UNLOAD && unit->MissionQueue != MISSION_UNLOAD) { 1905 int stray = Rule.StrayDistance; 1906 if (unit->What_Am_I() == RTTI_AIRCRAFT) { 1907 stray *= 3; 1908 } 1909 if (unit->What_Am_I() == RTTI_INFANTRY && ((InfantryClass const *)unit)->Class->IsDog) { 1910 if (Target_Legal(unit->TarCom)) stray = unit->Techno_Type_Class()->ThreatRange; 1911 if (Target_Legal(unit->TarCom) && unit->Distance(unit->TarCom) > stray) { 1912 unit->Assign_Target(TARGET_NONE); 1913 } 1914 } 1915 found = true; 1916 1917 int dist = unit->Distance(Target); 1918 if (unit->IsFormationMove) { 1919 if (::As_Target(Coord_Cell(unit->Coord)) != unit->NavCom) { 1920 dist = Rule.StrayDistance + 1; // formation moves must be exact. 1921 } 1922 } 1923 1924 if (dist > stray || 1925 (unit->What_Am_I() == RTTI_AIRCRAFT && 1926 // (unit->In_Which_Layer() == LAYER_TOP && 1927 ((AircraftClass *)unit)->Height > 0 && 1928 Coord_Cell(unit->Center_Coord()) != As_Cell(Target) && 1929 !((AircraftClass *)unit)->Class->IsFixedWing && 1930 Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { 1931 1932 bool wasform = false; 1933 1934 1935 if (unit->Mission != MISSION_MOVE) { 1936 unit->Assign_Mission(MISSION_MOVE); 1937 } 1938 1939 if (unit->NavCom != Target) { 1940 1941 /* 1942 ** Check if this destination should be adjusted for 1943 ** a formation move 1944 */ 1945 if (Is_Target_Cell(Target) && unit->IsFormationMove) { 1946 CELL newcell = unit->Adjust_Dest(As_Cell(Target)); 1947 if (Coord_Cell(unit->Coord) != newcell) { 1948 unit->Assign_Destination(::As_Target(newcell)); 1949 } else { 1950 unit->Assign_Mission(MISSION_GUARD); 1951 unit->Assign_Destination(TARGET_NONE); 1952 wasform = true; 1953 } 1954 } else { 1955 unit->Assign_Destination(Target); 1956 } 1957 } 1958 1959 if (!wasform) { 1960 finished = false; 1961 } 1962 1963 } else { 1964 if (unit->Mission == MISSION_MOVE && (!Target_Legal(unit->NavCom) || Distance(unit->NavCom) < CELL_LEPTON_W)) { 1965 unit->Assign_Destination(TARGET_NONE); 1966 unit->Enter_Idle_Mode(); 1967 } 1968 } 1969 1970 /* 1971 ** If any member still has a valid NavCom then consider this 1972 ** movement mission to still be in progress. This will ensure 1973 ** that the members come to a complete stop before the next 1974 ** mission commences. Without this, the team will prematurely 1975 ** start on the next mission even when all members aren't yet 1976 ** in their proper spot. 1977 */ 1978 if (Target_Legal(unit->NavCom)) { 1979 finished = false; 1980 } 1981 } 1982 1983 unit = unit->Member; 1984 } 1985 } else { 1986 finished = false; 1987 } 1988 } 1989 1990 /* 1991 ** If there are no initiated members to this team, then it certainly 1992 ** could not have managed to move to the target destination. 1993 */ 1994 if (!found) { 1995 finished = false; 1996 } 1997 1998 /* 1999 ** If all the team members are close enough to the desired destination, then 2000 ** move to the next mission. 2001 */ 2002 if (finished && IsMoving) { 2003 IsNextMission = true; 2004 } 2005 } 2006 2007 2008 /*********************************************************************************************** 2009 * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * 2010 * * 2011 * This routine will examine the team and find any lagging units. The units are then * 2012 * ordered to catch up to the team member that is closest to the team's destination. This * 2013 * routine will not do anything unless lagging members are suspected. This fact is * 2014 * indicated by setting the IsLagging flag. The flag is set by some outside agent. * 2015 * * 2016 * INPUT: none * 2017 * * 2018 * OUTPUT: none * 2019 * * 2020 * WARNINGS: Be sure to set IsLagging for the team if a lagging member is suspected. * 2021 * * 2022 * HISTORY: * 2023 * 08/01/1995 PWG : Created. * 2024 * 04/11/1996 JLB : Modified. * 2025 *=============================================================================================*/ 2026 bool TeamClass::Lagging_Units(void) 2027 { 2028 assert(IsActive); 2029 assert(Teams.ID(this) == ID); 2030 2031 FootClass * unit = Member; 2032 bool lag = false; 2033 2034 //BG: HACK - if it's in a formation move, then disable the check for 2035 // VG added NULL check laggers, 'cause they're all moving simultaneously. 2036 if (unit != NULL && unit->IsFormationMove) IsLagging = false; 2037 2038 /* 2039 ** If the IsLagging bit is not set, then obviously there are no lagging 2040 ** units. 2041 */ 2042 if (!IsLagging) return(false); 2043 2044 /* 2045 ** Scan through all of the units, searching for units who are having 2046 ** trouble keeping up with the pack. 2047 */ 2048 while (unit != NULL) { 2049 2050 if (_Is_It_Playing(unit)) { 2051 int stray = Rule.StrayDistance; 2052 if (unit->What_Am_I() == RTTI_AIRCRAFT) { 2053 stray *= 3; 2054 } 2055 2056 /* 2057 ** If we find a unit who has fallen too far away from the center of 2058 ** the pack, then we need to order that unit to catch up with the 2059 ** first unit. 2060 */ 2061 if (unit->Distance(ClosestMember) > stray) { 2062 // TCTCTC 2063 if (!Target_Legal(unit->NavCom)) { 2064 // if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, ClosestMember) > Rule.StrayDistance) { 2065 unit->Assign_Mission(MISSION_MOVE); 2066 unit->Assign_Destination(ClosestMember); 2067 } 2068 lag = true; 2069 2070 } else { 2071 /* 2072 ** We need to order all of the other units to hold their 2073 ** position until all lagging units catch up. 2074 */ 2075 if (unit->Mission != MISSION_GUARD) { 2076 unit->Assign_Mission(MISSION_GUARD); 2077 unit->Assign_Destination(TARGET_NONE); 2078 } 2079 } 2080 } 2081 unit = unit->Member; 2082 } 2083 2084 /* 2085 ** Once we have handled the loop we know whether there are any lagging 2086 ** units or not. 2087 */ 2088 IsLagging = lag; 2089 return(lag); 2090 } 2091 2092 2093 /*********************************************************************************************** 2094 * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * 2095 * * 2096 * This routine tells all transport vehicles to unload passengers now. * 2097 * * 2098 * INPUT: none * 2099 * * 2100 * OUTPUT: none * 2101 * * 2102 * WARNINGS: none * 2103 * * 2104 * HISTORY: * 2105 * 06/14/1995 JLB : Created. * 2106 *=============================================================================================*/ 2107 int TeamClass::TMission_Unload(void) 2108 { 2109 assert(IsActive); 2110 assert(Teams.ID(this) == ID); 2111 2112 FootClass * unit = Member; 2113 bool finished = true; 2114 2115 while (unit != NULL) { 2116 2117 Coordinate_Conscript(unit); 2118 2119 if (_Is_It_Playing(unit)) { 2120 /* 2121 ** Only assign the mission if the unit is carrying a passenger, OR 2122 ** if the unit is a minelayer, with mines in it, and the cell it's 2123 ** on doesn't have a building (read: mine) in it already. 2124 */ 2125 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2126 /* Also, allow unload if it's a MAD Tank. */ 2127 if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MAD )) { 2128 #else 2129 if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) ) { 2130 #endif 2131 if (unit->Is_Something_Attached()) { 2132 /* 2133 ** Passenger-carrying vehicles will always return false until 2134 ** they've unloaded all passengers. 2135 */ 2136 finished = false; 2137 } 2138 2139 /* 2140 ** The check for a building is located here because the mine layer may have 2141 ** already unloaded the mine but is still in the process of retracting 2142 ** the mine layer. During this time, it should not be considered to have 2143 ** finished its unload mission. 2144 */ 2145 if (Map[unit->Center_Coord()].Cell_Building() == NULL && unit->Mission != MISSION_UNLOAD) { 2146 unit->Assign_Destination(TARGET_NONE); 2147 unit->Assign_Target(TARGET_NONE); 2148 unit->Assign_Mission(MISSION_UNLOAD); 2149 finished = false; 2150 } 2151 2152 } else { 2153 2154 /* 2155 ** A loaner transport should vacate the map when all transported objects 2156 ** have been offloaded. 2157 */ 2158 if (unit->IsALoaner) { 2159 Remove(unit); 2160 unit->Assign_Mission(MISSION_RETREAT); 2161 unit->Commence(); 2162 } 2163 } 2164 } 2165 2166 unit = unit->Member; 2167 } 2168 2169 if (finished) { 2170 IsNextMission = true; 2171 } 2172 return(1); 2173 } 2174 2175 2176 /*********************************************************************************************** 2177 * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * 2178 * * 2179 * This routine tells all non-transport units in the team to climb onto the transport in the* 2180 * team. Note the transport must be a member of this team. * 2181 * * 2182 * INPUT: none * 2183 * * 2184 * OUTPUT: none * 2185 * * 2186 * WARNINGS: none * 2187 * * 2188 * HISTORY: * 2189 * 06/28/1996 BWG : Created. * 2190 *=============================================================================================*/ 2191 int TeamClass::TMission_Load(void) 2192 { 2193 assert(IsActive); 2194 assert(Teams.ID(this) == ID); 2195 2196 FootClass * unit = Member; 2197 FootClass * trans = 0; 2198 2199 /* 2200 ** First locate the transport in the team, if there is one. There should 2201 ** only be one transport in the team. 2202 */ 2203 while(unit != NULL && trans == NULL) { 2204 if (unit->Techno_Type_Class()->Max_Passengers() > 0) { 2205 trans = unit; 2206 break; 2207 } 2208 unit = unit->Member; 2209 } 2210 2211 /* 2212 ** In the case of no transport available, then consider the mission complete 2213 ** since it can never complete otherwise. 2214 */ 2215 if (trans == NULL) { 2216 IsNextMission = true; 2217 return(1); 2218 } 2219 2220 /* 2221 ** If the transport is already in radio contact, then this means that 2222 ** it is in the process of loading. During this time, don't bother to assign 2223 ** the enter mission to the other team members. 2224 */ 2225 if (trans->In_Radio_Contact()) { 2226 return(1); 2227 } 2228 2229 /* 2230 ** Find a member to assign the entry logic for. 2231 */ 2232 bool finished = true; 2233 unit = Member; // re-point at the first member of the team again. 2234 while (unit != NULL && Total > 1) { 2235 Coordinate_Conscript(unit); 2236 2237 /* 2238 ** Only assign the mission if the unit is not the transport. 2239 */ 2240 if (_Is_It_Playing(unit) && unit != trans) { 2241 if (unit->Mission != MISSION_ENTER) { 2242 unit->Assign_Mission(MISSION_ENTER); 2243 unit->Assign_Target(TARGET_NONE); 2244 unit->Assign_Destination(trans->As_Target()); 2245 finished = false; 2246 break; 2247 } 2248 finished = false; 2249 } 2250 2251 unit = unit->Member; 2252 } 2253 2254 if (finished) { 2255 IsNextMission = true; 2256 } 2257 return(1); 2258 } 2259 2260 2261 /*********************************************************************************************** 2262 * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * 2263 * * 2264 * This routine will give the movement orders to the conscript so that it will group * 2265 * with the other members of the team. * 2266 * * 2267 * INPUT: unit -- Pointer to the conscript unit. * 2268 * * 2269 * OUTPUT: bool; Is the unit still scurrying to reach the team's current location? * 2270 * * 2271 * WARNINGS: none * 2272 * * 2273 * HISTORY: * 2274 * 04/06/1995 JLB : Created. * 2275 *=============================================================================================*/ 2276 bool TeamClass::Coordinate_Conscript(FootClass * unit) 2277 { 2278 assert(IsActive); 2279 assert(Teams.ID(this) == ID); 2280 2281 if (_Is_It_Breathing(unit) && !unit->IsInitiated) { 2282 if (unit->Distance(Zone) > Rule.StrayDistance) { 2283 if (!Target_Legal(unit->NavCom)) { 2284 unit->Assign_Mission(MISSION_MOVE); 2285 unit->Assign_Target(TARGET_NONE); 2286 unit->IsFormationMove = false; 2287 unit->Assign_Destination(Zone); 2288 } 2289 return(true); 2290 2291 } else { 2292 2293 /* 2294 ** This unit has gotten close enough to the team center so that it is 2295 ** now considered initiated. An initiated unit is considered when calculating 2296 ** the center of the team. 2297 */ 2298 unit->IsInitiated = true; 2299 } 2300 } 2301 return(false); 2302 } 2303 2304 2305 /*************************************************************************** 2306 * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * 2307 * * 2308 * INPUT: none * 2309 * * 2310 * OUTPUT: none * 2311 * * 2312 * WARNINGS: * 2313 * * 2314 * HISTORY: * 2315 * 05/16/1995 PWG : Created. * 2316 *=========================================================================*/ 2317 bool TeamClass::Is_A_Member(void const * who) const 2318 { 2319 assert(IsActive); 2320 assert(Teams.ID(this) == ID); 2321 2322 FootClass * unit = Member; 2323 while (unit != NULL) { 2324 if (unit == who) { 2325 return(true); 2326 } 2327 unit = unit->Member; 2328 } 2329 return(false); 2330 } 2331 2332 2333 /*************************************************************************** 2334 * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * 2335 * * 2336 * INPUT: int priority - determines what is considered low priority. * 2337 * * 2338 * OUTPUT: * 2339 * * 2340 * WARNINGS: * 2341 * * 2342 * HISTORY: * 2343 * 06/19/1995 PWG : Created. * 2344 *=========================================================================*/ 2345 void TeamClass::Suspend_Teams(int priority, HouseClass const * house) 2346 { 2347 for (int index = 0; index < Teams.Count(); index++) { 2348 TeamClass * team = Teams.Ptr(index); 2349 2350 /* 2351 ** If a team is below the "survival priority level", then it gets 2352 ** destroyed. The team members are then free to be reassigned. 2353 */ 2354 if (team != NULL && team->House == house && team->Class->RecruitPriority < priority) { 2355 FootClass * unit = team->Member; 2356 while (team->Member) { 2357 team->Remove(team->Member); 2358 } 2359 team->IsAltered = team->JustAltered = true; 2360 team->SuspendTimer = Rule.SuspendDelay * TICKS_PER_MINUTE; 2361 team->Suspended = true; 2362 } 2363 } 2364 } 2365 2366 2367 /*********************************************************************************************** 2368 * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * 2369 * * 2370 * This routine is used to see if the team is leaving the map. A team that is leaving the * 2371 * map gives implicit permission for its members to leave the map. * 2372 * * 2373 * INPUT: none * 2374 * * 2375 * OUTPUT: bool; Is this team trying to leave the map? * 2376 * * 2377 * WARNINGS: none * 2378 * * 2379 * HISTORY: * 2380 * 04/30/1996 JLB : Created. * 2381 *=============================================================================================*/ 2382 bool TeamClass::Is_Leaving_Map(void) const 2383 { 2384 assert(IsActive); 2385 assert(Teams.ID(this) == ID); 2386 2387 if (IsMoving && CurrentMission >= 0) { 2388 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2389 2390 if (mission->Mission == TMISSION_MOVE && !Map.In_Radar(Scen.Waypoint[mission->Data.Value])) { 2391 return(true); 2392 } 2393 } 2394 return(false); 2395 } 2396 2397 2398 /*********************************************************************************************** 2399 * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * 2400 * * 2401 * This will examine all team members and only if all of them have entered the map, will * 2402 * it return true. This routine is used to recognize the case of a team that has been * 2403 * generated off map and one that has already entered game play. This knowledge can lead * 2404 * to more intelligent behavior regarding team and member disposition. * 2405 * * 2406 * INPUT: none * 2407 * * 2408 * OUTPUT: bool; Have all members of this team entered the map? * 2409 * * 2410 * WARNINGS: none * 2411 * * 2412 * HISTORY: * 2413 * 07/26/1996 JLB : Created. * 2414 *=============================================================================================*/ 2415 bool TeamClass::Has_Entered_Map(void) const 2416 { 2417 bool ok = true; 2418 FootClass * foot = Member; 2419 while (foot != NULL) { 2420 if (!foot->IsLocked) { 2421 ok = false; 2422 break; 2423 } 2424 foot = (FootClass *)(ObjectClass *)(foot->Next); 2425 } 2426 return(ok); 2427 } 2428 2429 2430 /*********************************************************************************************** 2431 * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * 2432 * * 2433 * This routine is used when one of the team members cannot get within range of the team's * 2434 * target. In such a case, the team must be assigned a new target and all members of that * 2435 * team must recognize that a restricted target scan is required. This is done by clearing * 2436 * out the team's target so that it will be forced to search for a new one. Also, since the * 2437 * members are flagged for short scanning, whichever team member is picked to scan for a * 2438 * target will scan for one that is within range. * 2439 * * 2440 * INPUT: none * 2441 * * 2442 * OUTPUT: none * 2443 * * 2444 * WARNINGS: The team will reassign its target as a result of this routine. * 2445 * * 2446 * HISTORY: * 2447 * 07/26/1996 JLB : Created. * 2448 *=============================================================================================*/ 2449 void TeamClass::Scan_Limit(void) 2450 { 2451 Assign_Mission_Target(TARGET_NONE); 2452 FootClass * foot = Member; 2453 while (foot != NULL) { 2454 foot->Assign_Target(TARGET_NONE); 2455 foot->IsScanLimited = true; 2456 foot = foot->Member; 2457 } 2458 } 2459 2460 2461 /*********************************************************************************************** 2462 * TeamClass::TMission_Formation -- Process team formation change command. * 2463 * * 2464 * This routine will change the team's formation to that specified in the team command * 2465 * parameter. It is presumed that the team will have further movement orders so that the * 2466 * formation can serve some purpose. Merely changing the formation doesn't alter the * 2467 * member's location. The team must be given a movement order before team member * 2468 * repositioning will occur. * 2469 * * 2470 * INPUT: none * 2471 * * 2472 * OUTPUT: Returns with the time to delay before further team actions should occur. * 2473 * * 2474 * WARNINGS: none * 2475 * * 2476 * HISTORY: * 2477 * 08/06/1996 JLB : Created. * 2478 *=============================================================================================*/ 2479 int TeamClass::TMission_Formation(void) 2480 { 2481 FootClass * member = Member; 2482 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2483 Formation = mission->Data.Formation; 2484 int group = ID + 10; 2485 int xdir = 0; 2486 int ydir = 0; 2487 bool evenodd = 1; 2488 HousesType house = (member != NULL) ? member->Owner() : HOUSE_NONE; 2489 2490 /* 2491 ** Assign appropriate formation offsets for each of the members 2492 ** of this team. 2493 */ 2494 switch (Formation) { 2495 case FORMATION_NONE: 2496 while (member != NULL) { 2497 member->Group = 0xFF; 2498 member->XFormOffset = 0x80000000; 2499 member->YFormOffset = 0x80000000; 2500 member->IsFormationMove = false; 2501 member = member->Member; 2502 } 2503 break; 2504 case FORMATION_TIGHT: 2505 while (member != NULL) { 2506 member->Group = group; 2507 member->XFormOffset = 0; 2508 member->YFormOffset = 0; 2509 member->IsFormationMove = true; 2510 member = member->Member; 2511 } 2512 break; 2513 case FORMATION_LOOSE: 2514 break; 2515 case FORMATION_WEDGE_N: 2516 ydir = -(Total / 2); 2517 xdir = 0; 2518 while (member != NULL) { 2519 member->Group = group; 2520 member->XFormOffset = xdir; 2521 member->YFormOffset = ydir; 2522 member->IsFormationMove = true; 2523 xdir = -xdir; 2524 evenodd ^= 1; 2525 if (!evenodd) { 2526 xdir -= 2; 2527 ydir += 2; 2528 } 2529 member = member->Member; 2530 } 2531 break; 2532 case FORMATION_WEDGE_E: 2533 xdir = (Total / 2); 2534 ydir = 0; 2535 while (member != NULL) { 2536 member->Group = group; 2537 member->XFormOffset = xdir; 2538 member->YFormOffset = ydir; 2539 member->IsFormationMove = true; 2540 ydir = -ydir; 2541 evenodd ^= 1; 2542 if (!evenodd) { 2543 xdir -= 2; 2544 ydir -= 2; 2545 } 2546 member = member->Member; 2547 } 2548 break; 2549 case FORMATION_WEDGE_S: 2550 ydir = (Total / 2); 2551 xdir = 0; 2552 while (member != NULL) { 2553 member->Group = group; 2554 member->XFormOffset = xdir; 2555 member->YFormOffset = ydir; 2556 member->IsFormationMove = true; 2557 xdir = -xdir; 2558 evenodd ^= 1; 2559 if (!evenodd) { 2560 xdir -= 2; 2561 ydir -= 2; 2562 } 2563 member = member->Member; 2564 } 2565 break; 2566 case FORMATION_WEDGE_W: 2567 xdir = -(Total / 2); 2568 ydir = 0; 2569 while (member != NULL) { 2570 member->Group = group; 2571 member->XFormOffset = xdir; 2572 member->YFormOffset = ydir; 2573 member->IsFormationMove = true; 2574 ydir = -ydir; 2575 evenodd ^= 1; 2576 if (!evenodd) { 2577 xdir += 2; 2578 ydir -= 2; 2579 } 2580 member = member->Member; 2581 } 2582 break; 2583 case FORMATION_LINE_NS: 2584 ydir = -(Total/2); 2585 while (member != NULL) { 2586 member->Group = group; 2587 member->XFormOffset = 0; 2588 member->YFormOffset = ydir; 2589 member->IsFormationMove = true; 2590 member = member->Member; 2591 ydir += 2; 2592 } 2593 break; 2594 case FORMATION_LINE_EW: 2595 xdir = -(Total/2); 2596 while (member != NULL) { 2597 member->Group = group; 2598 member->XFormOffset = xdir; 2599 member->YFormOffset = 0; 2600 member->IsFormationMove = true; 2601 member = member->Member; 2602 xdir += 2; 2603 } 2604 break; 2605 } 2606 2607 /* 2608 ** Now calculate the group's movement type and speed 2609 */ 2610 if (Formation != FORMATION_NONE && house != HOUSE_NONE) { 2611 TeamFormDataStruct& team_form_data = TeamFormData[house]; 2612 team_form_data.TeamSpeed[group] = SPEED_WHEEL; 2613 team_form_data.TeamMaxSpeed[group] = MPH_LIGHT_SPEED; 2614 member = Member; 2615 while (member != NULL) { 2616 RTTIType mytype = member->What_Am_I(); 2617 SpeedType memspeed; 2618 MPHType memmax; 2619 bool speedcheck = false; 2620 2621 if (mytype == RTTI_INFANTRY) { 2622 memspeed = SPEED_FOOT; 2623 memmax = ((InfantryClass *)member)->Class->MaxSpeed; 2624 speedcheck = true; 2625 } 2626 if (mytype == RTTI_UNIT) { 2627 memspeed = ((UnitClass *)member)->Class->Speed; 2628 memmax = ((UnitClass *)member)->Class->MaxSpeed; 2629 speedcheck = true; 2630 } 2631 2632 if (mytype == RTTI_VESSEL) { 2633 memspeed = ((VesselClass *)member)->Class->Speed; 2634 memmax = ((VesselClass *)member)->Class->MaxSpeed; 2635 speedcheck = true; 2636 } 2637 2638 if (speedcheck) { 2639 if (memmax < team_form_data.TeamMaxSpeed[group]) { 2640 team_form_data.TeamMaxSpeed[group] = memmax; 2641 team_form_data.TeamSpeed[group] = memspeed; 2642 } 2643 } 2644 member = member->Member; 2645 } 2646 2647 /* 2648 ** Now that it's all calculated, assign the movement type and 2649 ** speed to every member of the team. 2650 */ 2651 member = Member; 2652 while (member != NULL) { 2653 member->FormationSpeed = team_form_data.TeamSpeed[group]; 2654 member->FormationMaxSpeed = team_form_data.TeamMaxSpeed[group]; 2655 if (member->What_Am_I() == RTTI_INFANTRY) { 2656 member->FormationSpeed = SPEED_FOOT; 2657 member->FormationMaxSpeed = MPH_SLOW_ISH; 2658 } 2659 member = member->Member; 2660 } 2661 } 2662 2663 // Advance past the formation-setting command. 2664 IsNextMission = true; 2665 2666 return(1); 2667 } 2668 2669 2670 /*********************************************************************************************** 2671 * TeamClass::TMission_Attack -- Perform the team attack mission command. * 2672 * * 2673 * This will tell the team to attack the quarry specified in the team command. If the team * 2674 * already has a target, this it is presumed that this target take precidence and it won't * 2675 * be changed. * 2676 * * 2677 * INPUT: none * 2678 * * 2679 * OUTPUT: Returns with the delay before the next team logic operation should occur. * 2680 * * 2681 * WARNINGS: none * 2682 * * 2683 * HISTORY: * 2684 * 08/06/1996 JLB : Created. * 2685 *=============================================================================================*/ 2686 int TeamClass::TMission_Attack(void) 2687 { 2688 if (!Target_Legal(MissionTarget) && Member != NULL) { 2689 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2690 2691 /* 2692 ** Pick a team leader that has a weapon. Only in the case of no 2693 ** team members having any weapons, will a member without a weapon 2694 ** be chosen. 2695 */ 2696 FootClass const * candidate = Fetch_A_Leader(); 2697 2698 /* 2699 ** Have the team leader pick what the next team target will be. 2700 */ 2701 switch (mission->Data.Quarry) { 2702 case QUARRY_ANYTHING: 2703 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); 2704 break; 2705 2706 case QUARRY_BUILDINGS: 2707 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BUILDINGS)); 2708 break; 2709 2710 case QUARRY_HARVESTERS: 2711 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_TIBERIUM)); 2712 break; 2713 2714 case QUARRY_INFANTRY: 2715 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_INFANTRY)); 2716 break; 2717 2718 case QUARRY_VEHICLES: 2719 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_VEHICLES)); 2720 break; 2721 2722 case QUARRY_FACTORIES: 2723 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FACTORIES)); 2724 break; 2725 2726 case QUARRY_DEFENSE: 2727 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BASE_DEFENSE)); 2728 break; 2729 2730 case QUARRY_THREAT: 2731 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); 2732 break; 2733 2734 case QUARRY_POWER: 2735 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_POWER)); 2736 break; 2737 2738 case QUARRY_FAKES: 2739 Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FAKES)); 2740 break; 2741 2742 default: 2743 break; 2744 } 2745 if (!Target_Legal(MissionTarget)) IsNextMission = true; 2746 } 2747 Coordinate_Attack(); 2748 return(1); 2749 } 2750 2751 2752 /*********************************************************************************************** 2753 * TeamClass::TMission_Spy -- Perform the team spy mission. * 2754 * * 2755 * This will give the team a spy mission to the location specified. It is presumed that * 2756 * the location of the team mission actually resides under the building to be spied. If * 2757 * no building exists at the location, then the spy operation is presumed to be a mere * 2758 * move operation. * 2759 * * 2760 * INPUT: none * 2761 * * 2762 * OUTPUT: Returns with the delay before the next team logic operation should occur. * 2763 * * 2764 * WARNINGS: none * 2765 * * 2766 * HISTORY: * 2767 * 08/06/1996 JLB : Created. * 2768 *=============================================================================================*/ 2769 int TeamClass::TMission_Spy(void) 2770 { 2771 if (Is_Target_Cell(MissionTarget)) 2772 { 2773 CELL cell = ::As_Cell(MissionTarget); 2774 CellClass * cellptr = &Map[cell]; 2775 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2776 ObjectClass * bldg = cellptr->Cell_Building(); 2777 #else 2778 ObjectClass * bldg = cellptr->Cell_Object(); 2779 #endif 2780 if (bldg != NULL) 2781 { 2782 Assign_Mission_Target(bldg->As_Target()); 2783 Coordinate_Attack(); 2784 } 2785 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2786 else 2787 { 2788 FootClass *member = Member; 2789 if(member->What_Am_I() == RTTI_UNIT && *(UnitClass *)member == UNIT_CHRONOTANK) 2790 { 2791 bool finished = true; 2792 while (member) 2793 { 2794 if ( !((UnitClass *)member)->MoebiusCountDown) finished = false; 2795 member = member->Member; 2796 } 2797 2798 if (!finished) 2799 { 2800 Coordinate_Attack(); 2801 } 2802 else 2803 { 2804 Assign_Mission_Target(TARGET_NONE); 2805 IsNextMission = true; 2806 } 2807 } 2808 } 2809 #endif 2810 } 2811 else 2812 { 2813 if (!Target_Legal(MissionTarget)) 2814 { 2815 Assign_Mission_Target(TARGET_NONE); 2816 IsNextMission = true; 2817 } 2818 else 2819 { 2820 Coordinate_Attack(); 2821 } 2822 } 2823 return(1); 2824 } 2825 2826 2827 /*********************************************************************************************** 2828 * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * 2829 * * 2830 * This will cause the team members to search out and follow the nearest friendly object. * 2831 * * 2832 * INPUT: none * 2833 * * 2834 * OUTPUT: Returns with the delay before the next team logic operation should be performed. * 2835 * * 2836 * WARNINGS: none * 2837 * * 2838 * HISTORY: * 2839 * 08/06/1996 JLB : Created. * 2840 *=============================================================================================*/ 2841 int TeamClass::TMission_Follow(void) 2842 { 2843 Calc_Center(Zone, ClosestMember); 2844 Target = Zone; 2845 Coordinate_Move(); 2846 return(1); 2847 } 2848 2849 2850 /*********************************************************************************************** 2851 * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * 2852 * * 2853 * This is equivalent to a jump or goto command. It will alter the team command processing * 2854 * such that it will continue processing at the command number specified. * 2855 * * 2856 * INPUT: none * 2857 * * 2858 * OUTPUT: Returns with the delay before the next team logic operation should be performed. * 2859 * * 2860 * WARNINGS: none * 2861 * * 2862 * HISTORY: * 2863 * 08/06/1996 JLB : Created. * 2864 *=============================================================================================*/ 2865 int TeamClass::TMission_Loop(void) 2866 { 2867 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2868 CurrentMission = mission->Data.Value-1; 2869 IsNextMission = true; 2870 return(1); 2871 } 2872 2873 2874 /*********************************************************************************************** 2875 * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of time * 2876 * * 2877 * This is a team mission that simulates the Iron Curtain device. It will make all team * 2878 * members invlunerable for a temporary period of time. * 2879 * * 2880 * INPUT: none * 2881 * * 2882 * OUTPUT: Returns with the time delay before the next team logic operation should occur. * 2883 * * 2884 * WARNINGS: none * 2885 * * 2886 * HISTORY: * 2887 * 08/06/1996 JLB : Created. * 2888 *=============================================================================================*/ 2889 int TeamClass::TMission_Invulnerable(void) 2890 { 2891 FootClass * foot = Member; 2892 while (foot != NULL) { 2893 foot->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; 2894 foot->Mark(MARK_CHANGE); 2895 foot = foot->Member; 2896 } 2897 IsNextMission = true; 2898 return(1); 2899 } 2900 2901 2902 /*********************************************************************************************** 2903 * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * 2904 * * 2905 * This routine is used by the team to set a global variable but otherwise perform no * 2906 * visible effect on the team. By using this routine, sophisticated trigger dependencies * 2907 * can be implemented. * 2908 * * 2909 * INPUT: none * 2910 * * 2911 * OUTPUT: Returns with the delay before the next team logic operation should occur. * 2912 * * 2913 * WARNINGS: none * 2914 * * 2915 * HISTORY: * 2916 * 08/06/1996 JLB : Created. * 2917 *=============================================================================================*/ 2918 int TeamClass::TMission_Set_Global(void) 2919 { 2920 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2921 Scen.Set_Global_To(mission->Data.Value, true); 2922 IsNextMission = true; 2923 return(1); 2924 } 2925 2926 2927 /*********************************************************************************************** 2928 * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * 2929 * * 2930 * A patrolling team will move to the designated waypoint, but along the way it will * 2931 * periodically scan for nearby enemies. If an enemy is found, the patrol mission turns * 2932 * into an attack mission until the target is destroyed -- after which it resumes its * 2933 * patrol duties. * 2934 * * 2935 * INPUT: none * 2936 * * 2937 * OUTPUT: Returns with the delay before the next call to this routine is needed. * 2938 * * 2939 * WARNINGS: none * 2940 * * 2941 * HISTORY: * 2942 * 08/12/1996 JLB : Created. * 2943 *=============================================================================================*/ 2944 int TeamClass::TMission_Patrol(void) 2945 { 2946 /* 2947 ** Reassign the movement destination if the target has been prematurely 2948 ** cleared (probably because the object has been destroyed). 2949 */ 2950 if (!Target_Legal(Target)) { 2951 TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; 2952 if ((unsigned)mission->Data.Value < WAYPT_COUNT) { 2953 Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); 2954 } 2955 } 2956 2957 /* 2958 ** Every so often, scan for a nearby enemy. 2959 */ 2960 if (Frame % (Rule.PatrolTime * TICKS_PER_MINUTE) == 0) { 2961 FootClass * leader = Fetch_A_Leader(); 2962 if (leader != NULL) { 2963 TARGET target = leader->Greatest_Threat(THREAT_NORMAL|THREAT_RANGE); 2964 2965 if (Target_Legal(target)) { 2966 Assign_Mission_Target(target); 2967 } else { 2968 Assign_Mission_Target(TARGET_NONE); 2969 } 2970 } 2971 } 2972 2973 /* 2974 ** If the mission target looks like it should be attacked, then do so, otherwise 2975 ** treat it as a movement destination. 2976 */ 2977 if (Is_Target_Object(Target)) { 2978 Coordinate_Attack(); 2979 } else { 2980 Coordinate_Move(); 2981 } 2982 return(1); 2983 } 2984 2985 2986 int TeamClass::TMission_Deploy(void) 2987 { 2988 assert(IsActive); 2989 assert(Teams.ID(this) == ID); 2990 2991 FootClass * unit = Member; 2992 bool finished = true; 2993 2994 while (unit != NULL) { 2995 2996 Coordinate_Conscript(unit); 2997 2998 if (_Is_It_Playing(unit)) { 2999 3000 if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MCV) { 3001 if (unit->Mission != MISSION_UNLOAD) { 3002 unit->Assign_Destination(TARGET_NONE); 3003 unit->Assign_Target(TARGET_NONE); 3004 unit->Assign_Mission(MISSION_UNLOAD); 3005 finished = false; 3006 } 3007 } 3008 3009 if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo != 0) { 3010 /* 3011 ** The check for a building is located here because the mine layer may have 3012 ** already unloaded the mine but is still in the process of retracting 3013 ** the mine layer. During this time, it should not be considered to have 3014 ** finished its unload mission. 3015 */ 3016 if (!Map[unit->Center_Coord()].Cell_Building() && unit->Mission != MISSION_UNLOAD) { 3017 unit->Assign_Destination(TARGET_NONE); 3018 unit->Assign_Target(TARGET_NONE); 3019 unit->Assign_Mission(MISSION_UNLOAD); 3020 finished = false; 3021 } 3022 } 3023 3024 } 3025 3026 unit = unit->Member; 3027 } 3028 3029 if (finished) { 3030 IsNextMission = true; 3031 } 3032 return(1); 3033 } 3034 3035 3036 /*********************************************************************************************** 3037 * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * 3038 * * 3039 * This will scan through the team members looking for one that is suitable as a leader * 3040 * type. A team can sometimes contain limboed or unarmed members. These members are not * 3041 * suitable for leadership roles. * 3042 * * 3043 * INPUT: none * 3044 * * 3045 * OUTPUT: Returns with a suitable leader type unit. * 3046 * * 3047 * WARNINGS: none * 3048 * * 3049 * HISTORY: * 3050 * 08/27/1996 JLB : Created. * 3051 *=============================================================================================*/ 3052 FootClass * TeamClass::Fetch_A_Leader(void) const 3053 { 3054 FootClass * leader = Member; 3055 3056 /* 3057 ** Scan through the team members trying to find one that is an active member and 3058 ** is equipped with a weapon. 3059 */ 3060 while (leader != NULL) { 3061 if (_Is_It_Playing(leader) && leader->Is_Weapon_Equipped()) break; 3062 leader = leader->Member; 3063 } 3064 3065 /* 3066 ** If no suitable leader was found, then just return with the first conveniently 3067 ** accessable team member. This presumes that some member is better than no member 3068 ** at all. 3069 */ 3070 if (leader == NULL) { 3071 leader = Member; 3072 } 3073 3074 return(leader); 3075 }