TEAMTYPE.CPP (76202B)
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/TEAMTYPE.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 : TEAMTYPE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 06/28/96 * 28 * * 29 * Last Update : July 30, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * TeamMissionClass::Description -- Compose a text description of team mi * 34 * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * 35 * TeamMission_Needs -- Determines what extra data is needed by team miss * 36 * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * 37 * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * 38 * TeamTypeClass::Create_One_Of -- Creates a team of this type. * 39 * TeamTypeClass::Description -- Builds a description of the team. * 40 * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * 41 * TeamTypeClass::Detach -- Detach the specified target from this team type. * 42 * TeamTypeClass::Draw_It -- Display the team type in a list box. * 43 * TeamTypeClass::Edit -- Edit the team type. * 44 * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * 45 * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * 46 * TeamTypeClass::Init -- pre-scenario initialization * 47 * TeamTypeClass::Member_Description -- Builds a member description string * 48 * TeamTypeClass::Mission_From_Name -- returns mission for given name * 49 * TeamTypeClass::Name_From_Mission -- returns name for given mission * 50 * TeamTypeClass::Read_INI -- reads INI data * 51 * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * 52 * TeamTypeClass::TeamTypeClass -- class constructor * 53 * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * 54 * TeamTypeClass::operator delete -- 'delete' operator * 55 * TeamTypeClass::operator new -- 'new' operator * 56 * TeamTypeClass::~TeamTypeClass -- class destructor * 57 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 58 59 #include "function.h" 60 61 62 TeamMissionClass TeamMissions[TMISSION_COUNT] = { 63 {TMISSION_ATTACK}, 64 {TMISSION_ATT_WAYPT}, 65 {TMISSION_FORMATION}, 66 {TMISSION_MOVE}, 67 {TMISSION_MOVECELL}, 68 {TMISSION_GUARD}, 69 {TMISSION_LOOP}, 70 {TMISSION_ATTACKTARCOM}, 71 {TMISSION_UNLOAD}, 72 {TMISSION_DEPLOY}, 73 {TMISSION_HOUND_DOG}, 74 {TMISSION_DO}, 75 {TMISSION_SET_GLOBAL}, 76 {TMISSION_LOAD}, 77 {TMISSION_SPY}, 78 {TMISSION_PATROL}, 79 }; 80 81 82 83 int atoh(char * str); 84 85 86 #if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) 87 /*********************************************************************************************** 88 * TeamTypeClass::Draw_It -- Display the team type in a list box. * 89 * * 90 * This is a helper routine that is used when this team type is stored into a list box * 91 * and must be displayed. It will display the team type as a single line of text. * 92 * * 93 * INPUT: see below... * 94 * * 95 * OUTPUT: none * 96 * * 97 * WARNINGS: none * 98 * * 99 * HISTORY: * 100 * 07/30/1996 JLB : Created. * 101 *=============================================================================================*/ 102 void TeamTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const 103 { 104 RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); 105 static int _tabs[] = {35, 60, 80, 100}; 106 if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { 107 108 if (selected) { 109 flags = flags | TPF_BRIGHT_COLOR; 110 LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); 111 } else { 112 if (!(flags & TPF_USE_GRAD_PAL)) { 113 flags = flags | TPF_MEDIUM_COLOR; 114 } 115 } 116 117 Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); 118 } else { 119 Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); 120 } 121 } 122 #endif 123 124 125 /* 126 ********************************** Globals ********************************** 127 */ 128 char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { 129 "Attack...", 130 "Attack Waypoint...", 131 "Change Formation to...", 132 "Move to waypoint...", 133 "Move to Cell...", 134 "Guard area (1/10th min)...", 135 "Jump to line #...", 136 "Attack Tarcom", 137 "Unload", 138 "Deploy", 139 "Follow friendlies", 140 "Do this...", 141 "Set global...", 142 "Invulnerable", 143 "Load onto Transport", 144 "Spy on bldg @ waypt...", 145 "Patrol to waypoint..." 146 }; 147 148 149 /*************************************************************************** 150 * TeamTypeClass::TeamTypeClass -- class constructor * 151 * * 152 * INPUT: * 153 * * 154 * OUTPUT: * 155 * * 156 * WARNINGS: * 157 * * 158 * HISTORY: * 159 * 12/07/1994 BR : Created. * 160 * 11/22/1995 JLB : Uses initializer constructor method. * 161 *=========================================================================*/ 162 TeamTypeClass::TeamTypeClass(void) : 163 AbstractTypeClass(RTTI_TEAMTYPE, TeamTypes.ID(this), TXT_NONE, ""), 164 IsRoundAbout(false), 165 IsSuicide(false), 166 IsAutocreate(false), 167 IsPrebuilt(true), 168 IsReinforcable(true), 169 IsTransient(false), 170 RecruitPriority(7), 171 InitNum(0), 172 MaxAllowed(0), 173 Fear(0), 174 House(HOUSE_NONE), 175 Trigger(0), 176 Origin(-1), 177 Number(0), 178 MissionCount(0), 179 ClassCount(0) 180 { 181 for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { 182 Members[i].Class = NULL; 183 Members[i].Quantity = 0; 184 } 185 } 186 187 188 /*************************************************************************** 189 * TeamTypeClass::Init -- pre-scenario initialization * 190 * * 191 * INPUT: * 192 * * 193 * OUTPUT: * 194 * * 195 * WARNINGS: * 196 * * 197 * HISTORY: * 198 * 12/07/1994 BR : Created. * 199 *=========================================================================*/ 200 void TeamTypeClass::Init(void) 201 { 202 TeamTypes.Free_All(); 203 } 204 205 206 /*************************************************************************** 207 * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * 208 * * 209 * INPUT: * 210 * name name of teamtype * 211 * * 212 * OUTPUT: * 213 * ptr to TeamType with that name * 214 * * 215 * WARNINGS: * 216 * none. * 217 * * 218 * HISTORY: * 219 * 12/07/1994 BR : Created. * 220 *=========================================================================*/ 221 TeamTypeClass * TeamTypeClass::As_Pointer(char const * name) 222 { 223 if (name) { 224 for (int index = 0; index < TeamTypes.Count(); index++) { 225 if (!stricmp(name, TeamTypes.Ptr(index)->IniName)) { 226 return(TeamTypes.Ptr(index)); 227 } 228 } 229 } 230 return(NULL); 231 } 232 233 234 /*************************************************************************** 235 * TeamTypeClass::Mission_From_Name -- returns team mission for given name * 236 * * 237 * INPUT: * 238 * name name to compare * 239 * * 240 * OUTPUT: * 241 * mission for that name * 242 * * 243 * WARNINGS: * 244 * none. * 245 * * 246 * HISTORY: * 247 * 12/13/1994 BR : Created. * 248 *=========================================================================*/ 249 TeamMissionType TeamTypeClass::Mission_From_Name(char const * name) 250 { 251 if (name) { 252 for (TeamMissionType order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { 253 if (stricmp(TMissions[order], name) == 0) { 254 return(order); 255 } 256 } 257 } 258 259 return(TMISSION_NONE); 260 } 261 262 263 /*************************************************************************** 264 * TeamTypeClass::Name_From_Mission -- returns name for given mission * 265 * * 266 * INPUT: * 267 * order mission to get name for * 268 * * 269 * OUTPUT: * 270 * name of mission * 271 * * 272 * WARNINGS: * 273 * none. * 274 * * 275 * HISTORY: * 276 * 12/13/1994 BR : Created. * 277 *=========================================================================*/ 278 char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) 279 { 280 assert((unsigned)order < TMISSION_COUNT); 281 282 return(TMissions[order]); 283 } 284 285 286 /*************************************************************************** 287 * TeamTypeClass::operator new -- 'new' operator * 288 * * 289 * INPUT: * 290 * none. * 291 * * 292 * OUTPUT: * 293 * pointer to new TeamType * 294 * * 295 * WARNINGS: * 296 * none. * 297 * * 298 * HISTORY: * 299 * 11/28/1994 BR : Created. * 300 *=========================================================================*/ 301 void * TeamTypeClass::operator new(size_t ) 302 { 303 void * ptr = TeamTypes.Allocate(); 304 if (ptr) { 305 ((TeamTypeClass *)ptr)->IsActive = true; 306 } 307 return(ptr); 308 } 309 310 311 /*************************************************************************** 312 * TeamTypeClass::operator delete -- 'delete' operator * 313 * * 314 * INPUT: * 315 * ptr pointer to delete * 316 * * 317 * OUTPUT: * 318 * none. * 319 * * 320 * WARNINGS: * 321 * none. * 322 * * 323 * HISTORY: * 324 * 11/28/1994 BR : Created. * 325 *=========================================================================*/ 326 void TeamTypeClass::operator delete(void * ptr) 327 { 328 if (ptr) { 329 ((TeamTypeClass *)ptr)->IsActive = false; 330 } 331 TeamTypes.Free((TeamTypeClass *)ptr); 332 } 333 334 335 /*********************************************************************************************** 336 * TeamTypeClass::Create_One_Of -- Creates a team of this type. * 337 * * 338 * Use this routine to create a team object from this team type. * 339 * * 340 * INPUT: none * 341 * * 342 * OUTPUT: Returns with a pointer to the newly created team object. If one could not be * 343 * created, then NULL is returned. * 344 * * 345 * WARNINGS: none * 346 * * 347 * HISTORY: * 348 * 09/21/1995 JLB : Created. * 349 *=============================================================================================*/ 350 TeamClass * TeamTypeClass::Create_One_Of(void) const 351 { 352 if (ScenarioInit || Number < MaxAllowed) { 353 // if (ScenarioInit || TeamClass::Number[ID] < MaxAllowed) { 354 return(new TeamClass(this, HouseClass::As_Pointer(House))); 355 } 356 return(NULL); 357 } 358 359 360 /*********************************************************************************************** 361 * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * 362 * * 363 * This routine will destroy all teams of this type. Typical use of this is from a trigger * 364 * event. * 365 * * 366 * INPUT: none * 367 * * 368 * OUTPUT: none * 369 * * 370 * WARNINGS: none * 371 * * 372 * HISTORY: * 373 * 09/21/1995 JLB : Created. * 374 *=============================================================================================*/ 375 void TeamTypeClass::Destroy_All_Of(void) const 376 { 377 for (int index = 0; index < Teams.Count(); index++) { 378 TeamClass * team = Teams.Ptr(index); 379 380 if (team->Class == this) { 381 delete team; 382 index--; 383 } 384 } 385 } 386 387 388 /*********************************************************************************************** 389 * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * 390 * * 391 * This routine will scan through the team types available and create teams of the * 392 * type that can best utilize the existing unit mix. * 393 * * 394 * INPUT: house -- Pointer to the house that this team is to be created for. * 395 * * 396 * atypes -- A bit mask of the aircraft types available for this house. * 397 * * 398 * utypes -- A bit mask of the unit types available for this house. * 399 * * 400 * itypes -- A bit mask of the infantry types available for this house. * 401 * * 402 * vtypes -- A bit mask of the vessel types available for this house. * 403 * * 404 * alerted -- Is this house alerted? If true, then the Autocreate teams will be * 405 * considered in the selection process. * 406 * * 407 * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * 408 * be created, then it returns NULL. * 409 * * 410 * WARNINGS: none * 411 * * 412 * HISTORY: * 413 * 07/13/1995 JLB : Created. * 414 * 07/21/1995 JLB : Will autocreate team even if no members in field. * 415 *=============================================================================================*/ 416 TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long , long , long , long , bool alerted) 417 //TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted) 418 { 419 // TeamTypeClass const * best = NULL; 420 // int bestvalue = 0; 421 422 TeamTypeClass const * choices[20]; 423 int choicecount = 0; 424 425 for (int index = 0; index < TeamTypes.Count(); index++) { 426 TeamTypeClass const * ttype = TeamTypes.Ptr(index); 427 428 assert(ttype != NULL); 429 430 int maxnum = ttype->MaxAllowed; 431 if ((alerted && !ttype->IsAutocreate) || (!alerted && ttype->IsAutocreate)) { 432 maxnum = 0; 433 } 434 435 if (choicecount >= ARRAY_SIZE(choices)) break; 436 437 if (ttype != NULL && ttype->House == house->Class->House && ttype->Number < maxnum) { 438 439 choices[choicecount++] = ttype; 440 441 #ifdef OBSOLETE 442 /* 443 ** Determine what kind of units this team requires. 444 */ 445 long uneeded = 0; 446 long ineeded = 0; 447 long vneeded = 0; 448 long aneeded = 0; 449 for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { 450 switch (ttype->Members[ctype].Class->What_Am_I()) { 451 case RTTI_INFANTRYTYPE: 452 ineeded |= (1 << ((InfantryTypeClass *)ttype->Members[ctype].Class)->Type); 453 break; 454 455 case RTTI_UNITTYPE: 456 uneeded |= (1 << ((UnitTypeClass *)ttype->Members[ctype].Class)->Type); 457 break; 458 459 case RTTI_VESSELTYPE: 460 vneeded |= (1 << ((VesselTypeClass *)ttype->Members[ctype].Class)->Type); 461 break; 462 463 case RTTI_AIRCRAFTTYPE: 464 aneeded |= (1 << ((AircraftTypeClass *)ttype->Members[ctype].Class)->Type); 465 break; 466 } 467 } 468 469 /* 470 ** If this team can use the types required, then consider it a possible 471 ** team type to create. 472 */ 473 int value = 0; 474 if ((aneeded & atypes) != 0 || (ineeded & itypes) != 0 || (uneeded & utypes) != 0 || (vneeded & vtypes) != 0) { 475 value = ttype->RecruitPriority; 476 } else { 477 value = ttype->RecruitPriority/2; 478 } 479 480 if (best == NULL || bestvalue < value) { 481 bestvalue = value; 482 best = ttype; 483 } 484 #endif 485 } 486 } 487 488 if (choicecount > 0) { 489 return(choices[Random_Pick(0, choicecount-1)]); 490 } 491 return(NULL); 492 493 // return(best); 494 } 495 496 497 /*********************************************************************************************** 498 * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * 499 * * 500 * This routine is used to convert an ASCII name of a team type into the corresponding * 501 * team type pointer. * 502 * * 503 * INPUT: name -- Pointer to the ASCII name of the team type. * 504 * * 505 * OUTPUT: Returns with a pointer to the team type that this ASCII name represents. If there * 506 * is no match, the NULL is returned. * 507 * * 508 * WARNINGS: none * 509 * * 510 * HISTORY: * 511 * 01/26/1996 JLB : Created. * 512 *=============================================================================================*/ 513 TeamTypeClass * TeamTypeClass::From_Name(char const * name) 514 { 515 if (name) { 516 for (int index = 0; index < TeamTypes.Count(); index++) { 517 if (stricmp(name, TeamTypes.Ptr(index)->IniName) == 0) { 518 return(TeamTypes.Ptr(index)); 519 } 520 } 521 } 522 return(0); 523 } 524 525 526 /*********************************************************************************************** 527 * TeamMission_Needs -- Determines what extra data is needed by team mission. * 528 * * 529 * This routine will return the required extra data that the specified team mission will * 530 * need. * 531 * * 532 * INPUT: tmtype -- The team mission type to check. * 533 * * 534 * OUTPUT: Returns with the data type needed for this team mission. * 535 * * 536 * WARNINGS: none * 537 * * 538 * HISTORY: * 539 * 01/26/1996 JLB : Created. * 540 *=============================================================================================*/ 541 NeedType TeamMission_Needs(TeamMissionType tmtype) 542 { 543 switch (tmtype) { 544 /* 545 ** Requires a formation type. 546 */ 547 case TMISSION_FORMATION: 548 return(NEED_FORMATION); 549 550 /* 551 ** Team mission requires a target quarry value. 552 */ 553 case TMISSION_ATTACK: 554 return(NEED_QUARRY); 555 556 /* 557 ** Team mission requires a data value. 558 */ 559 case TMISSION_MOVECELL: 560 return(NEED_HEX_NUMBER); 561 562 case TMISSION_SET_GLOBAL: 563 case TMISSION_GUARD: 564 case TMISSION_LOOP: 565 return(NEED_NUMBER); 566 567 /* 568 ** Team mission requires a waypoint. 569 */ 570 case TMISSION_PATROL: 571 case TMISSION_MOVE: 572 case TMISSION_ATT_WAYPT: 573 case TMISSION_SPY: 574 return(NEED_WAYPOINT); 575 576 /* 577 ** Team mission requires a general mission type. 578 */ 579 case TMISSION_DO: 580 return(NEED_MISSION); 581 582 default: 583 break; 584 } 585 return(NEED_NONE); 586 } 587 588 589 #ifdef SCENARIO_EDITOR 590 /*********************************************************************************************** 591 * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * 592 * * 593 * This routine will display a team mission list box entry. It converts the index number * 594 * into the appropriate text string and then displays the text at the coordinates * 595 * specified. * 596 * * 597 * INPUT: index -- The index number to use when rendering the team mission. * 598 * * 599 * x,y -- The X,Y coordinate to use when drawing the team mission entry. * 600 * * 601 * width, height -- The dimensions of the area that the description can be * 602 * rendered into. * 603 * * 604 * selected -- Is this entry currently selected? If so, then it should be rendered * 605 * differently. * 606 * * 607 * flags -- Text print control flags used for when the text is printed. * 608 * * 609 * OUTPUT: none * 610 * * 611 * WARNINGS: none * 612 * * 613 * HISTORY: * 614 * 01/05/1996 JLB : Created. * 615 *=============================================================================================*/ 616 void TeamMissionClass::Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) 617 { 618 RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); 619 static int _tabs[] = {13,40}; 620 if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { 621 622 if (selected) { 623 flags = flags | TPF_BRIGHT_COLOR; 624 LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, 625 scheme->Shadow); 626 } else { 627 if (!(flags & TPF_USE_GRAD_PAL)) { 628 flags = flags | TPF_MEDIUM_COLOR; 629 } 630 } 631 632 Conquer_Clip_Text_Print(Description(index), x, y, scheme, TBLACK, flags, width, _tabs); 633 } else { 634 Conquer_Clip_Text_Print(Description(index), x, y, 635 (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); 636 } 637 } 638 639 640 /*********************************************************************************************** 641 * TeamTypeClass::Edit -- Edit the team type. * 642 * * 643 * This routine handles the editing dialog box for the team type. * 644 * * 645 * INPUT: none * 646 * * 647 * OUTPUT: Was the team type edited? A FALSE indicates that the dialog was canceled. * 648 * * 649 * WARNINGS: none * 650 * * 651 * HISTORY: * 652 * 01/05/1996 JLB : Created. * 653 *=============================================================================================*/ 654 bool TeamTypeClass::Edit(void) 655 { 656 /* 657 ** Dialog & button dimensions 658 */ 659 enum { 660 D_DIALOG_W = 400, 661 D_DIALOG_H = 250, 662 D_DIALOG_X = 0, 663 D_DIALOG_Y = 0, 664 665 D_NAME_X=D_DIALOG_X+35, 666 D_NAME_Y=D_DIALOG_Y+27, 667 ED_WIDTH=40, 668 669 D_CHECK_X=D_DIALOG_X+35, // Start of check box attribute list. 670 D_CHECK_Y=D_NAME_Y+25, 671 672 CB_SPACING_Y=9, // Vertical spacing between check box lines. 673 CB_SPACING_X=8, // Horizontal spacing for check box description text. 674 D_SPACING_X=9, // Horizontal spacing between data entry fields. 675 676 D_CANCEL_W = 50, 677 D_CANCEL_H = 9, 678 D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+35), 679 D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - (D_CANCEL_H+20), 680 681 D_OK_W = 50, 682 D_OK_H = 9, 683 D_OK_X = D_DIALOG_X + D_DIALOG_W - (D_OK_W+18)*2, 684 D_OK_Y = D_CANCEL_Y 685 }; 686 687 /* 688 ** Button enumerations: 689 */ 690 enum { 691 BUTTON_NAME=100, 692 BUTTON_RECRUIT, 693 BUTTON_MAXNUM, 694 BUTTON_INITNUM, 695 BUTTON_HOUSE, 696 BUTTON_ROUNDABOUT, 697 BUTTON_LEARNING, 698 BUTTON_SUICIDE, 699 BUTTON_AUTO, 700 BUTTON_PREBUILT, 701 BUTTON_REINFORCE, 702 BUTTON_MISSION1, 703 BUTTON_MISSION2, 704 BUTTON_ADD, 705 BUTTON_INSERT, 706 BUTTON_DELETE, 707 BUTTON_REPLACE, 708 BUTTON_ARG, 709 BUTTON_FORMATION, 710 BUTTON_MEMBERS, 711 BUTTON_MISSION, 712 BUTTON_TRIGGER, 713 BUTTON_ORIGIN, 714 BUTTON_OK, 715 BUTTON_CANCEL, 716 BUTTON_QUARRY, 717 }; 718 719 /* 720 ** Dialog variables: 721 */ 722 ControlClass * commands = 0; 723 RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); 724 725 /* 726 ** Team name edit field. 727 */ 728 char name_buf[10]; 729 EditClass name_edt(BUTTON_NAME, name_buf, sizeof(name_buf), 730 TPF_EFNT | TPF_NOSHADOW, 731 D_NAME_X, D_NAME_Y, ED_WIDTH, 9, EditClass::ALPHANUMERIC); 732 strcpy(name_buf, IniName); 733 commands = &name_edt; 734 735 /* 736 ** House ownership of this team. 737 */ 738 char housetext[25] = ""; 739 DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), 740 TPF_EFNT | TPF_NOSHADOW, 741 name_edt.X+name_edt.Width+D_SPACING_X, name_edt.Y, 55, 8*5, 742 MFCD::Retrieve("EBTN-UP.SHP"), 743 MFCD::Retrieve("EBTN-DN.SHP")); 744 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 745 housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); 746 } 747 if (House == HOUSE_NONE) House = HOUSE_GOOD; 748 housebtn.Set_Selected_Index(House); 749 housebtn.Add(*commands); 750 751 /* 752 ** Recruit priority for this team. 753 */ 754 char recr_buf[4]; 755 EditClass recr_edt(BUTTON_RECRUIT, recr_buf, sizeof(recr_buf), 756 TPF_EFNT | TPF_NOSHADOW, 757 housebtn.X+housebtn.Width+5+D_SPACING_X, housebtn.Y, ED_WIDTH, 9, EditClass::NUMERIC); 758 sprintf(recr_buf, "%d", RecruitPriority); 759 recr_edt.Add(*commands); 760 761 /* 762 ** Maximum allowed for this team type. 763 */ 764 char maxnum_buf[4]; 765 EditClass maxnum_edt(BUTTON_MAXNUM, maxnum_buf, sizeof(maxnum_buf), 766 TPF_EFNT | TPF_NOSHADOW, 767 recr_edt.X+recr_edt.Width+D_SPACING_X, recr_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); 768 sprintf(maxnum_buf, "%d", MaxAllowed); 769 maxnum_edt.Add(*commands); 770 771 /* 772 ** Initial number for this team type. 773 */ 774 char initnum_buf[4]; 775 EditClass initnum_edt(BUTTON_INITNUM, initnum_buf, sizeof(initnum_buf), 776 TPF_EFNT | TPF_NOSHADOW, 777 maxnum_edt.X+maxnum_edt.Width+D_SPACING_X, maxnum_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); 778 sprintf(initnum_buf, "%d", InitNum); 779 initnum_edt.Add(*commands); 780 781 /* 782 ** Waypoint preference to create/reinforce this team. 783 */ 784 char origin[4]; 785 EditClass originbtn(BUTTON_ORIGIN, origin, sizeof(origin), 786 TPF_EFNT | TPF_NOSHADOW, 787 initnum_edt.X+initnum_edt.Width+D_SPACING_X, initnum_edt.Y, 20, 9, EditClass::ALPHA); 788 *originbtn.Get_Text() = '\0'; 789 if (Origin != -1) { 790 if (Origin < 26) { 791 sprintf(originbtn.Get_Text(), "%c", Origin + 'A'); 792 } else { 793 sprintf(originbtn.Get_Text(), "%c%c", (Origin/26) + 'A'-1, Origin % 26 + 'A'); 794 } 795 } 796 originbtn.Add(*commands); 797 798 /* 799 ** Members of this team control button. 800 */ 801 TextButtonClass membersbtn(BUTTON_MEMBERS, "Members", TPF_EBUTTON, name_edt.X, name_edt.Y+12, 50); 802 membersbtn.Add(*commands); 803 804 /* 805 ** Trigger to assign to each member of this team (when object joins team). 806 */ 807 char trigtext[25] = ""; 808 DropListClass triggerbtn(BUTTON_TRIGGER, trigtext, sizeof(trigtext), 809 TPF_EFNT | TPF_NOSHADOW, 810 D_DIALOG_X+D_DIALOG_W-95, membersbtn.Y, 60, 8*5, 811 MFCD::Retrieve("EBTN-UP.SHP"), 812 MFCD::Retrieve("EBTN-DN.SHP")); 813 triggerbtn.Add_Item("<NONE>"); 814 for (int index = 0; index < TriggerTypes.Count(); index++) { 815 triggerbtn.Add_Item(TriggerTypes.Ptr(index)->IniName); 816 } 817 if (Trigger.Is_Valid()) { 818 triggerbtn.Set_Selected_Index(Trigger->Name()); 819 } else { 820 triggerbtn.Set_Selected_Index(0); 821 } 822 triggerbtn.Add(*commands); 823 824 /* 825 ** Roundabout travel logic attribute for this team. 826 */ 827 CheckBoxClass roundbtn(BUTTON_ROUNDABOUT, D_CHECK_X, D_CHECK_Y); 828 if (IsRoundAbout) { 829 roundbtn.Turn_On(); 830 } else { 831 roundbtn.Turn_Off(); 832 } 833 roundbtn.Add(*commands); 834 835 /* 836 ** Suicide travel to target attribute. 837 */ 838 CheckBoxClass suicidebtn(BUTTON_SUICIDE, D_CHECK_X, roundbtn.Y+CB_SPACING_Y); 839 if (IsSuicide) { 840 suicidebtn.Turn_On(); 841 } else { 842 suicidebtn.Turn_Off(); 843 } 844 suicidebtn.Add(*commands); 845 846 /* 847 ** Autocreate attribute for this team. 848 */ 849 CheckBoxClass autocreatebtn(BUTTON_AUTO, D_CHECK_X, suicidebtn.Y+CB_SPACING_Y); 850 if (IsAutocreate) { 851 autocreatebtn.Turn_On(); 852 } else { 853 autocreatebtn.Turn_Off(); 854 } 855 autocreatebtn.Add(*commands); 856 857 /* 858 ** Prebuild team members attribute for this team. 859 */ 860 CheckBoxClass prebuildbtn(BUTTON_PREBUILT, D_CHECK_X, autocreatebtn.Y+CB_SPACING_Y); 861 if (IsPrebuilt) { 862 prebuildbtn.Turn_On(); 863 } else { 864 prebuildbtn.Turn_Off(); 865 } 866 prebuildbtn.Add(*commands); 867 868 /* 869 ** Reinforce this team in progress attribute. 870 */ 871 CheckBoxClass reinforcebtn(BUTTON_REINFORCE, D_CHECK_X, prebuildbtn.Y+CB_SPACING_Y); 872 if (IsReinforcable) { 873 reinforcebtn.Turn_On(); 874 } else { 875 reinforcebtn.Turn_Off(); 876 } 877 reinforcebtn.Add(*commands); 878 879 /* 880 ** Create drop box of possible team missions. This is used when building/editing the 881 ** team mission list. 882 */ 883 char droptext[45]; 884 DropListClass missionlist1(BUTTON_MISSION1, droptext, sizeof(droptext), 885 TPF_EFNT | TPF_NOSHADOW, 886 reinforcebtn.X, reinforcebtn.Y+15, 170, 8*8, 887 MFCD::Retrieve("EBTN-UP.SHP"), 888 MFCD::Retrieve("EBTN-DN.SHP")); 889 for (TeamMissionType tm = TMISSION_FIRST; tm < TMISSION_COUNT; tm++) { 890 missionlist1.Add_Item(TeamTypeClass::Name_From_Mission(tm)); 891 } 892 missionlist1.Set_Selected_Index(0); 893 missionlist1.Add_Tail(*commands); 894 895 /* 896 ** Optional mission argument entry field. 897 */ 898 char arg_buf[6] = {0}; 899 EditClass arg_edt(BUTTON_ARG, arg_buf, sizeof(arg_buf), 900 TPF_EFNT | TPF_NOSHADOW, 901 missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 60, -1, EditClass::ALPHANUMERIC); 902 // arg_edt.Add(*commands); 903 904 char qtext[55]; 905 DropListClass qlist(BUTTON_QUARRY, qtext, sizeof(qtext), 906 TPF_EFNT | TPF_NOSHADOW, 907 missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, 908 MFCD::Retrieve("EBTN-UP.SHP"), 909 MFCD::Retrieve("EBTN-DN.SHP")); 910 for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { 911 qlist.Add_Item(QuarryName[q]); 912 } 913 qlist.Set_Selected_Index(0); 914 qlist.Add_Tail(*commands); 915 916 char ftext[55]; 917 DropListClass flist(BUTTON_FORMATION, ftext, sizeof(ftext), 918 TPF_EFNT | TPF_NOSHADOW, 919 missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, 920 MFCD::Retrieve("EBTN-UP.SHP"), 921 MFCD::Retrieve("EBTN-DN.SHP")); 922 for (FormationType f = FORMATION_FIRST; f < FORMATION_COUNT; f++) { 923 flist.Add_Item(FormationName[f]); 924 } 925 flist.Set_Selected_Index(0); 926 flist.Add_Tail(*commands); 927 928 char mtext[55]; 929 DropListClass mlist(BUTTON_MISSION, mtext, sizeof(mtext), 930 TPF_EFNT | TPF_NOSHADOW, 931 missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, 932 MFCD::Retrieve("EBTN-UP.SHP"), 933 MFCD::Retrieve("EBTN-DN.SHP")); 934 for (MissionType m = MISSION_FIRST; m < MISSION_COUNT; m++) { 935 mlist.Add_Item(MissionClass::Mission_Name(m)); 936 } 937 mlist.Set_Selected_Index(0); 938 mlist.Add_Tail(*commands); 939 940 TListClass<TeamMissionClass *> missionlist2(BUTTON_MISSION2, missionlist1.X+60, missionlist1.Y+22, 240, 8*7, 941 TPF_EFNT | TPF_NOSHADOW, 942 MFCD::Retrieve("EBTN-UP.SHP"), 943 MFCD::Retrieve("EBTN-DN.SHP")); 944 for (index = 0; index < MissionCount; index++) { 945 missionlist2.Add_Item(new TeamMissionClass(MissionList[index])); 946 // missionlist2.Add_Item(&TeamMissions[MissionList[index].Mission]); 947 } 948 static int tabs[] = {13, 40}; // list box tab stops 949 missionlist2.Set_Tabs(tabs); 950 missionlist2.Add_Tail(*commands); 951 952 /* 953 ** Mission editing command buttons. 954 */ 955 TextButtonClass addbtn(BUTTON_ADD, "Append", TPF_EBUTTON, D_NAME_X, missionlist1.Y+missionlist1.Height+1, 50); 956 addbtn.Add(*commands); 957 958 TextButtonClass insertbtn(BUTTON_INSERT, "Insert", TPF_EBUTTON, addbtn.X, addbtn.Y+10, 50); 959 insertbtn.Add(*commands); 960 961 TextButtonClass delbtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, insertbtn.X, insertbtn.Y+10, 50); 962 delbtn.Add(*commands); 963 964 TextButtonClass repbtn(BUTTON_REPLACE, "Replace", TPF_EBUTTON, delbtn.X, delbtn.Y+10, 50); 965 repbtn.Add(*commands); 966 967 968 TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); 969 TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); 970 971 /* 972 ** Initialize 973 */ 974 Set_Logic_Page(SeenBuff); 975 976 /* 977 ** Create the list 978 */ 979 okbtn.Add(*commands); 980 cancelbtn.Add_Tail(*commands); 981 982 /* 983 ** Main Processing Loop 984 */ 985 int lastindex = -1; 986 int lastcount = -1; 987 int lastbutton = -1; 988 bool cancel = false; 989 bool display = true; 990 bool process = true; 991 while (process) { 992 993 /* 994 ** Invoke game callback 995 */ 996 Call_Back(); 997 998 /* 999 ** Refresh display if needed 1000 */ 1001 if (display) { 1002 1003 /* 1004 ** Add the optional data entry field as necessary. 1005 */ 1006 arg_edt.Remove(); 1007 qlist.Remove(); 1008 flist.Remove(); 1009 mlist.Remove(); 1010 switch (TeamMission_Needs(TeamMissionType(missionlist1.Current_Index()))) { 1011 default: 1012 break; 1013 1014 case NEED_MISSION: 1015 mlist.Add(*commands); 1016 break; 1017 1018 case NEED_FORMATION: 1019 flist.Add(*commands); 1020 break; 1021 1022 case NEED_WAYPOINT: 1023 case NEED_NUMBER: 1024 case NEED_HEX_NUMBER: 1025 arg_edt.Add(*commands); 1026 break; 1027 1028 case NEED_QUARRY: 1029 qlist.Add(*commands); 1030 break; 1031 } 1032 1033 /* 1034 ** Display the dialog box 1035 */ 1036 Hide_Mouse(); 1037 Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); 1038 1039 Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); 1040 1041 /* 1042 ** Draw the captions 1043 */ 1044 Fancy_Text_Print("Name:", name_edt.X, name_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1045 Fancy_Text_Print("House:", housebtn.X, housebtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1046 Fancy_Text_Print("Pri:", recr_edt.X, recr_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1047 Fancy_Text_Print("Max:", maxnum_edt.X, maxnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1048 Fancy_Text_Print("Num:", initnum_edt.X, initnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1049 Fancy_Text_Print("Loc:", originbtn.X, originbtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1050 Fancy_Text_Print("# Team Mission", missionlist2.X, missionlist2.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1051 Fancy_Text_Print("Trigger:", triggerbtn.X-4, triggerbtn.Y+1, scheme, TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); 1052 1053 Fancy_Text_Print(Member_Description(), membersbtn.X + membersbtn.Width + 3, membersbtn.Y+1, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1054 1055 Fancy_Text_Print("Use safest, possibly longer, route to target?", roundbtn.X+CB_SPACING_X, roundbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1056 Fancy_Text_Print("Charge toward target ignoring distractions?", suicidebtn.X+CB_SPACING_X, suicidebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1057 Fancy_Text_Print("Only 'Autocreate A.I.' uses this team type?", autocreatebtn.X+CB_SPACING_X, autocreatebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1058 Fancy_Text_Print("Prebuild team members before team is created?", prebuildbtn.X+CB_SPACING_X, prebuildbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1059 Fancy_Text_Print("Automatically reinforce team whenever possible?", reinforcebtn.X+CB_SPACING_X, reinforcebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); 1060 1061 /* 1062 ** Redraw the buttons 1063 */ 1064 commands->Draw_All(); 1065 Show_Mouse(); 1066 1067 display = false; 1068 } 1069 1070 /* 1071 ** Get user input 1072 */ 1073 KeyNumType input = commands->Input(); 1074 1075 /* 1076 ** Process input 1077 */ 1078 switch (input) { 1079 1080 /* 1081 ** Select a Mission on the right-hand mission list; update the Argument 1082 ** field to reflect the current value. This only serves as an aide to editing 1083 ** the team mission list. 1084 */ 1085 case BUTTON_MISSION2 | KN_BUTTON: 1086 if (missionlist2.Count() && lastcount == missionlist2.Count() && lastbutton == BUTTON_MISSION2 && lastindex == missionlist2.Current_Index()) { 1087 missionlist1.Set_Selected_Index(missionlist2.Current_Item()->Mission); 1088 1089 switch (TeamMission_Needs(missionlist2.Current_Item()->Mission)) { 1090 case NEED_MISSION: 1091 mlist.Set_Selected_Index(missionlist2.Current_Item()->Data.Mission); 1092 break; 1093 1094 case NEED_FORMATION: 1095 flist.Set_Selected_Index(missionlist2.Current_Item()->Data.Formation); 1096 break; 1097 1098 case NEED_NUMBER: 1099 sprintf(arg_edt.Get_Text(), "%d", missionlist2.Current_Item()->Data.Value); 1100 break; 1101 1102 case NEED_HEX_NUMBER: 1103 sprintf(arg_edt.Get_Text(), "%x", missionlist2.Current_Item()->Data.Value); 1104 break; 1105 1106 case NEED_QUARRY: 1107 strcpy(qlist.Get_Text(), QuarryName[missionlist2.Current_Item()->Data.Quarry]); 1108 break; 1109 1110 case NEED_WAYPOINT: 1111 if (missionlist2.Current_Item()->Data.Value < 26) { 1112 sprintf(arg_edt.Get_Text(), "%c", missionlist2.Current_Item()->Data.Value + 'A'); 1113 } else { 1114 sprintf(arg_edt.Get_Text(), "%c%c", (missionlist2.Current_Item()->Data.Value)/26 + 'A'-1, (missionlist2.Current_Item()->Data.Value % 26) + 'A'); 1115 } 1116 break; 1117 } 1118 } 1119 lastindex = missionlist2.Current_Index(); 1120 lastcount = missionlist2.Count(); 1121 display = true; 1122 break; 1123 1124 /* 1125 ** Add current mission data to current position of team mission list. Any 1126 ** subsequent missions get moved downward. 1127 */ 1128 case BUTTON_INSERT | KN_BUTTON: 1129 if (missionlist2.Count() < MAX_TEAM_MISSIONS) { 1130 TeamMissionClass * tm = new TeamMissionClass; 1131 tm->Mission = TeamMissionType(missionlist1.Current_Index()); 1132 tm->Data.Value = 0; 1133 switch (TeamMission_Needs(tm->Mission)) { 1134 case NEED_MISSION: 1135 tm->Data.Mission = MissionType(mlist.Current_Index()); 1136 break; 1137 1138 case NEED_FORMATION: 1139 tm->Data.Formation = FormationType(flist.Current_Index()); 1140 break; 1141 1142 case NEED_QUARRY: 1143 tm->Data.Quarry = QuarryType(qlist.Current_Index()); 1144 break; 1145 1146 case NEED_NUMBER: 1147 tm->Data.Value = atoi(arg_edt.Get_Text()); 1148 break; 1149 1150 case NEED_HEX_NUMBER: 1151 tm->Data.Value = atoh(arg_edt.Get_Text()); 1152 break; 1153 1154 case NEED_WAYPOINT: 1155 tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; 1156 if ( *((arg_edt.Get_Text())+1)) { 1157 tm->Data.Value = (tm->Data.Value+1)*26; 1158 tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; 1159 } 1160 if ((unsigned)tm->Data.Value >= WAYPT_HOME) { 1161 tm->Data.Value = 0; 1162 } 1163 break; 1164 1165 default: 1166 tm->Data.Value = 0; 1167 break; 1168 } 1169 missionlist2.Insert_Item(tm); 1170 } 1171 display = true; 1172 break; 1173 1174 /* 1175 ** Add mission data to the end of the mission list. 1176 */ 1177 case BUTTON_ADD | KN_BUTTON: 1178 if (missionlist2.Count() < MAX_TEAM_MISSIONS) { 1179 TeamMissionClass * tm = new TeamMissionClass; 1180 tm->Mission = TeamMissionType(missionlist1.Current_Index()); 1181 tm->Data.Value = 0; 1182 switch (TeamMission_Needs(tm->Mission)) { 1183 case NEED_MISSION: 1184 tm->Data.Mission = MissionType(mlist.Current_Index()); 1185 break; 1186 1187 case NEED_FORMATION: 1188 tm->Data.Formation = FormationType(flist.Current_Index()); 1189 break; 1190 1191 case NEED_QUARRY: 1192 tm->Data.Quarry = QuarryType(qlist.Current_Index()); 1193 break; 1194 1195 case NEED_NUMBER: 1196 tm->Data.Value = atoi(arg_edt.Get_Text()); 1197 break; 1198 1199 case NEED_HEX_NUMBER: 1200 tm->Data.Value = atoh(arg_edt.Get_Text()); 1201 break; 1202 1203 case NEED_WAYPOINT: 1204 tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A';; 1205 if ( *((arg_edt.Get_Text())+1)) { 1206 tm->Data.Value = (tm->Data.Value+1)*26; 1207 tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; 1208 } 1209 if ((unsigned)tm->Data.Value >= WAYPT_HOME) { 1210 tm->Data.Value = 0; 1211 } 1212 break; 1213 1214 default: 1215 tm->Data.Value = 0; 1216 break; 1217 } 1218 missionlist2.Add_Item(tm); 1219 display = true; 1220 } 1221 break; 1222 1223 /* 1224 ** Replace the currently selected mission with the work mission data. 1225 */ 1226 case BUTTON_REPLACE | KN_BUTTON: 1227 if (missionlist2.Count()) { 1228 TeamMissionClass * tm = missionlist2.Current_Item(); 1229 tm->Mission = TeamMissionType(missionlist1.Current_Index()); 1230 tm->Data.Value = 0; 1231 switch (TeamMission_Needs(tm->Mission)) { 1232 case NEED_MISSION: 1233 tm->Data.Mission = MissionType(mlist.Current_Index()); 1234 break; 1235 1236 case NEED_FORMATION: 1237 tm->Data.Formation = FormationType(flist.Current_Index()); 1238 break; 1239 1240 case NEED_QUARRY: 1241 tm->Data.Quarry = QuarryType(qlist.Current_Index()); 1242 break; 1243 1244 case NEED_NUMBER: 1245 tm->Data.Value = atoi(arg_edt.Get_Text()); 1246 break; 1247 1248 case NEED_HEX_NUMBER: 1249 tm->Data.Value = atoh(arg_edt.Get_Text()); 1250 break; 1251 1252 case NEED_WAYPOINT: 1253 tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; 1254 if ( *((arg_edt.Get_Text())+1)) { 1255 tm->Data.Value = (tm->Data.Value+1)*26; 1256 tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A'; 1257 } 1258 if ((unsigned)tm->Data.Value >= WAYPT_HOME) { 1259 tm->Data.Value = 0; 1260 } 1261 break; 1262 1263 default: 1264 tm->Data.Value = 0; 1265 break; 1266 } 1267 missionlist2[missionlist2.Current_Index()] = tm; 1268 } 1269 display = true; 1270 break; 1271 1272 /* 1273 ** Delete the currently selected mission. 1274 */ 1275 case BUTTON_DELETE | KN_BUTTON: 1276 if (missionlist2.Count()) { 1277 TeamMissionClass * tm = missionlist2.Current_Item(); 1278 missionlist2.Remove_Index(missionlist2.Current_Index()); 1279 delete tm; 1280 } 1281 display = true; 1282 break; 1283 1284 /* 1285 ** Invoke the members dialog 1286 */ 1287 case BUTTON_MEMBERS | KN_BUTTON: 1288 1289 /* 1290 ** Take editor focus away 1291 */ 1292 membersbtn.Turn_Off(); 1293 1294 /* 1295 ** Invoke the dialog 1296 */ 1297 Map.Team_Members(HousesType(housebtn.Current_Index())); 1298 1299 /* 1300 ** Redraw 1301 */ 1302 display = true; 1303 break; 1304 1305 /* 1306 ** When the OK button is selected, lift the values from the dialog box 1307 ** and place them into the team type object. 1308 */ 1309 case BUTTON_OK | KN_BUTTON: 1310 strtrim(name_edt.Get_Text()); 1311 if (strlen(name_edt.Get_Text()) != 0) { 1312 strcpy(IniName, name_edt.Get_Text()); 1313 } else { 1314 strcpy(IniName, "----"); 1315 } 1316 1317 IsRoundAbout = roundbtn.IsOn; 1318 IsSuicide = suicidebtn.IsOn; 1319 IsAutocreate = autocreatebtn.IsOn; 1320 IsPrebuilt = prebuildbtn.IsOn; 1321 IsReinforcable = reinforcebtn.IsOn; 1322 1323 RecruitPriority = atoi(recr_edt.Get_Text()); 1324 InitNum = atoi(initnum_edt.Get_Text()); 1325 MaxAllowed = atoi(maxnum_edt.Get_Text()); 1326 House = HousesType(housebtn.Current_Index()); 1327 Trigger = NULL; 1328 if (triggerbtn.Current_Index() > 0) { 1329 Trigger = TriggerTypes.Ptr(triggerbtn.Current_Index()-1); 1330 } 1331 1332 MissionCount = missionlist2.Count(); 1333 for (index = 0; index < MissionCount; index++) { 1334 MissionList[index].Data.Value = 0; // Clears extra bits. 1335 MissionList[index] = *missionlist2[index]; 1336 } 1337 1338 #ifdef TOFIX 1339 // the mission class objects pointed to in the list should be deleted? 1340 #endif 1341 1342 if (strlen(originbtn.Get_Text())) { 1343 if (strlen(originbtn.Get_Text()) == 1) { 1344 Origin = toupper(*originbtn.Get_Text()) - 'A'; 1345 } else { 1346 Origin = (toupper(*originbtn.Get_Text())+1 - 'A' ) * 26; 1347 Origin += toupper(*(originbtn.Get_Text()+1)) - 'A'; 1348 } 1349 } else { 1350 Origin = -1; 1351 } 1352 cancel = false; 1353 process = false; 1354 break; 1355 1356 /* 1357 ** Cancel: return 1358 */ 1359 case BUTTON_CANCEL | KN_BUTTON: 1360 cancel = true; 1361 process = false; 1362 break; 1363 1364 /* 1365 ** Unrecognized events are ignored. If any button related event is 1366 ** detected, then collapse any drop down list boxes. This keeps the 1367 ** dialog box clean. 1368 */ 1369 default: 1370 if (input & KN_BUTTON) { 1371 housebtn.Collapse(); 1372 missionlist1.Collapse(); 1373 triggerbtn.Collapse(); 1374 display = true; 1375 } 1376 break; 1377 } 1378 1379 /* 1380 ** Record the last dialog control touched so that a double click 1381 ** can be detected. 1382 */ 1383 if (input & KN_BUTTON) { 1384 lastbutton = (input & ~KN_BUTTON); 1385 } 1386 } 1387 1388 return(!cancel); 1389 } 1390 1391 1392 int atoh(char * str) 1393 { 1394 int retval = 0; 1395 while (*str) { 1396 retval *= 16; 1397 if (*str >= '0' && *str <= '9') { 1398 retval += *str-'0'; 1399 } else { 1400 if (*str >= 'a' && *str <= 'f') { 1401 retval += 10 + (*str - 'a'); 1402 } else { 1403 if (*str >= 'A' && *str <= 'F') { 1404 retval += 10 + (*str - 'A'); 1405 } 1406 } 1407 } 1408 str++; 1409 } 1410 return(retval); 1411 } 1412 1413 #endif 1414 1415 1416 #if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) 1417 /*********************************************************************************************** 1418 * TeamTypeClass::Member_Description -- Builds a member description string. * 1419 * * 1420 * This routine will build a team member description string. The string will be composed * 1421 * of the team member type and quantity. As many team member types will be listed that * 1422 * can fit within a reasonable size. * 1423 * * 1424 * INPUT: none * 1425 * * 1426 * OUTPUT: Returns with a pointer to the text string that contains a description of the team * 1427 * type members. * 1428 * * 1429 * WARNINGS: The return string may be truncated if necessary to fit within reasonable size * 1430 * limits. * 1431 * * 1432 * HISTORY: * 1433 * 01/05/1996 JLB : Created. * 1434 *=============================================================================================*/ 1435 char const * TeamTypeClass::Member_Description(void) const 1436 { 1437 static char buffer[128]; 1438 1439 buffer[0] = '\0'; 1440 1441 /* 1442 ** Fill in class & count for all classes 1443 */ 1444 for (int index = 0; index < ClassCount; index++) { 1445 char txt[10]; 1446 1447 strcat(buffer, Members[index].Class->IniName); 1448 strcat(buffer, ":"); 1449 1450 sprintf(txt, "%d", Members[index].Quantity); 1451 strcat(buffer, txt); 1452 1453 if (index < ClassCount-1) { 1454 strcat(buffer, ","); 1455 } 1456 } 1457 1458 if (strlen(buffer) > 25) { 1459 strcpy(&buffer[25-3], "..."); 1460 } 1461 1462 return(buffer); 1463 } 1464 1465 1466 /*********************************************************************************************** 1467 * TeamTypeClass::Description -- Builds a description of the team. * 1468 * * 1469 * This routine will build a brief description of the team type. This description is used * 1470 * in the team type list. * 1471 * * 1472 * INPUT: none * 1473 * * 1474 * OUTPUT: Returns with the composed text string that represents the team type. * 1475 * * 1476 * WARNINGS: none * 1477 * * 1478 * HISTORY: * 1479 * 01/05/1996 JLB : Created. * 1480 *=============================================================================================*/ 1481 char const * TeamTypeClass::Description(void) const 1482 { 1483 static char _buffer[128]; 1484 char extra = ' '; 1485 char loc[3]; 1486 1487 loc[0] = loc[1] = loc[2] = 0; 1488 if (IsAutocreate) extra = '*'; 1489 if (Origin > -1) { 1490 // if (Origin != -1) { 1491 if (Origin < 26) { 1492 loc[0] = 'A' + Origin; 1493 } else { 1494 loc[0] = Origin / 26 + 'A'-1; 1495 loc[1] = Origin % 26 + 'A'; 1496 } 1497 } 1498 1499 sprintf(_buffer, "%s\t%s\t%c%s\t%d\t%s", IniName, HouseTypeClass::As_Reference(House).Suffix, extra, loc, MissionCount, Member_Description()); 1500 return(_buffer); 1501 } 1502 1503 1504 /*********************************************************************************************** 1505 * TeamMissionClass::Description -- Compose a text description of team mission. * 1506 * * 1507 * This routine will create a text representation of a team mission. This description will * 1508 * be used in the list of team missions for display purposes. * 1509 * * 1510 * INPUT: index -- The index to assign to this team. The index is used since some team * 1511 * missions refer to the another team mission by index number. * 1512 * * 1513 * OUTPUT: Returns with a pointer to the team mission text. * 1514 * * 1515 * WARNINGS: none * 1516 * * 1517 * HISTORY: * 1518 * 01/05/1996 JLB : Created. * 1519 *=============================================================================================*/ 1520 char const * TeamMissionClass::Description(int index) const 1521 { 1522 static char buffer[64]; 1523 1524 sprintf(buffer, "%d\t%s", index, TeamTypeClass::Name_From_Mission(Mission)); 1525 1526 switch (TeamMission_Needs(Mission)) { 1527 case NEED_MISSION: 1528 strcat(buffer, MissionClass::Mission_Name(Data.Mission)); 1529 break; 1530 1531 case NEED_FORMATION: 1532 strcat(buffer, FormationName[Data.Quarry]); 1533 break; 1534 1535 case NEED_NUMBER: 1536 sprintf(&buffer[strlen(buffer)], "%d", Data.Value); 1537 break; 1538 1539 case NEED_HEX_NUMBER: 1540 sprintf(&buffer[strlen(buffer)], "%x", Data.Value); 1541 break; 1542 1543 case NEED_QUARRY: 1544 strcat(buffer, QuarryName[Data.Quarry]); 1545 break; 1546 1547 case NEED_WAYPOINT: 1548 if (Data.Value < 26) { 1549 sprintf(&buffer[strlen(buffer)], "%c", Data.Value + 'A'); 1550 } else { 1551 sprintf(&buffer[strlen(buffer)], "%c%c", (Data.Value/26) + 'A'-1, (Data.Value % 26) + 'A'); 1552 } 1553 break; 1554 1555 } 1556 1557 return(buffer); 1558 } 1559 #endif 1560 1561 1562 /*********************************************************************************************** 1563 * TeamTypeClass::Detach -- Detach the specified target from this team type. * 1564 * * 1565 * This routine is called when some object is about to be removed from the game system and * 1566 * all references to it must be severed. This will check to see if the specified object * 1567 * is a trigger that this team refers to. If so, then the reference will be cleared. * 1568 * * 1569 * INPUT: target -- The target object to remove references to. * 1570 * * 1571 * OUTPUT: none * 1572 * * 1573 * WARNINGS: none * 1574 * * 1575 * HISTORY: * 1576 * 07/30/1996 JLB : Created. * 1577 *=============================================================================================*/ 1578 void TeamTypeClass::Detach(TARGET target, bool) 1579 { 1580 if (Is_Target_TriggerType(target) && Trigger.Is_Valid() && Trigger == As_TriggerType(target)) { 1581 Trigger = NULL; 1582 } 1583 } 1584 1585 1586 /*************************************************************************** 1587 * TeamTypeClass::Read_INI -- reads INI data * 1588 * * 1589 * INI entry format: * 1590 * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * 1591 * RecruitPriority,MaxAllowed,InitNum,Fear, * 1592 * ClassCount,Class:Num,Class:Num,..., * 1593 * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * 1594 * * 1595 * INPUT: * 1596 * buffer buffer to hold the INI data * 1597 * * 1598 * OUTPUT: * 1599 * none. * 1600 * * 1601 * WARNINGS: * 1602 * none. * 1603 * * 1604 * HISTORY: * 1605 * 12/07/1994 BR : Created. * 1606 * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * 1607 *=========================================================================*/ 1608 void TeamTypeClass::Read_INI(CCINIClass & ini) 1609 { 1610 TeamTypeClass * team; // Working team pointer. 1611 char buf[500]; // INI entry buffer 1612 1613 int len = ini.Entry_Count(INI_Name()); 1614 1615 /* 1616 ** Loop for all team entries, create and fill in. 1617 */ 1618 for (int index = 0; index < len; index++) { 1619 team = new TeamTypeClass(); 1620 if (team != NULL) { 1621 char const * entry = ini.Get_Entry(INI_Name(), index); 1622 ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); 1623 team->Fill_In((char *)entry, buf); 1624 } 1625 } 1626 } 1627 1628 1629 /*********************************************************************************************** 1630 * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * 1631 * * 1632 * This routine fills in the given teamtype with the given name, and values from * 1633 * the given INI entry. * 1634 * * 1635 * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * 1636 * * 1637 * INI entry format: * 1638 * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * 1639 * RecruitPriority,MaxAllowed,InitNum,Fear, * 1640 * ClassCount,Class:Num,Class:Num,..., * 1641 * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * 1642 * * 1643 * INPUT: * 1644 * name mnemonic for the desired trigger * 1645 * entry INI entry to parse * 1646 * * 1647 * OUTPUT: * 1648 * none. * 1649 * * 1650 * WARNINGS: * 1651 * none. * 1652 * * 1653 * HISTORY: * 1654 * 11/28/1994 BR : Created. * 1655 * 11/29/1995 JLB : Revamped to use new team class. * 1656 *=============================================================================================*/ 1657 void TeamTypeClass::Fill_In(char * name, char * entry) 1658 { 1659 assert(TeamTypes.ID(this) == ID); 1660 1661 /* 1662 ** Set its name 1663 */ 1664 Set_Name(name); 1665 1666 House = HousesType(atoi(strtok(entry, ","))); 1667 1668 int code; 1669 switch (NewINIFormat) { 1670 default: 1671 code = atoi(strtok(NULL, ",")); 1672 IsRoundAbout = ((code & 0x0001) != 0); 1673 IsSuicide = ((code & 0x0002) != 0); 1674 IsAutocreate = ((code & 0x0004) != 0); 1675 IsPrebuilt = ((code & 0x0008) != 0); 1676 IsReinforcable = ((code & 0x0010) != 0); 1677 break; 1678 1679 case 0: 1680 case 1: 1681 IsRoundAbout = atoi(strtok(NULL, ",")); 1682 IsSuicide = atoi(strtok(NULL, ",")); 1683 IsAutocreate = atoi(strtok(NULL, ",")); 1684 IsPrebuilt = atoi(strtok(NULL, ",")); 1685 IsReinforcable = atoi(strtok(NULL, ",")); 1686 break; 1687 } 1688 1689 RecruitPriority = atoi(strtok(NULL, ",")); 1690 InitNum = atoi(strtok(NULL, ",")); 1691 MaxAllowed = atoi(strtok(NULL, ",")); 1692 Origin = atoi(strtok(NULL, ",")); 1693 1694 switch (NewINIFormat) { 1695 default: 1696 Trigger.Set_Raw(atoi(strtok(NULL, ","))); 1697 break; 1698 1699 case 0: 1700 case 1: 1701 // Throw this token away -- it isn't used. 1702 strtok(NULL, ","); 1703 break; 1704 } 1705 1706 /* 1707 ** Fetch the team member types and quantity values. 1708 */ 1709 ClassCount = atoi(strtok(NULL, ",")); 1710 for (int index = 0; index < ClassCount; index++) { 1711 char * p1 = strtok(NULL, ",:"); 1712 char * p2 = strtok(NULL, ",:"); 1713 TechnoTypeClass const * otype = NULL; 1714 1715 /* 1716 ** See if this is an infantry name 1717 */ 1718 InfantryType i_id = InfantryTypeClass::From_Name(p1); 1719 if (i_id != INFANTRY_NONE) { 1720 otype = &InfantryTypeClass::As_Reference(i_id); 1721 } else { 1722 1723 /* 1724 ** See if this is a unit name 1725 */ 1726 UnitType u_id = UnitTypeClass::From_Name(p1); 1727 if (u_id != UNIT_NONE) { 1728 otype = &UnitTypeClass::As_Reference(u_id); 1729 } else { 1730 1731 /* 1732 ** See if this is an aircraft name 1733 */ 1734 AircraftType a_id = AircraftTypeClass::From_Name(p1); 1735 if (a_id != AIRCRAFT_NONE) { 1736 otype = &AircraftTypeClass::As_Reference(a_id); 1737 } else { 1738 1739 /* 1740 ** See if this is a vessel name. 1741 */ 1742 VesselType v_id = VesselTypeClass::From_Name(p1); 1743 if (v_id != VESSEL_NONE) { 1744 otype = &VesselTypeClass::As_Reference(v_id); 1745 } 1746 } 1747 } 1748 } 1749 1750 /* 1751 ** If the name was resolved, add this class 1752 */ 1753 if (otype) { 1754 if (index < MAX_TEAM_CLASSCOUNT) { 1755 Members[index].Class = otype; 1756 Members[index].Quantity = atoi(p2); 1757 } 1758 } else { 1759 ClassCount--; 1760 if (index == 0) break; 1761 index--; 1762 } 1763 } 1764 ClassCount = min(MAX_TEAM_CLASSCOUNT, ClassCount); 1765 1766 /* 1767 ** Fetch the missions assigned to this team type. 1768 */ 1769 MissionCount = atoi(strtok(NULL, ",")); 1770 for (int index = 0; index < MissionCount; index++) { 1771 MissionList[index].Mission = TeamMissionType(atoi(strtok(NULL, ",:"))); 1772 MissionList[index].Data.Value = atoi(strtok(NULL, ",:")); 1773 } 1774 1775 if (NewINIFormat < 2) { 1776 /* 1777 ** Fetch the trigger ID. 1778 */ 1779 Trigger.Set_Raw(atoi(strtok(NULL, ","))); 1780 } 1781 } 1782 1783 1784 /*********************************************************************************************** 1785 * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * 1786 * * 1787 * This routine will take all team types and write them out to the INI database specified. * 1788 * * 1789 * INPUT: ini -- Reference to the INI database that will hold al the teams. * 1790 * * 1791 * OUTPUT: none * 1792 * * 1793 * WARNINGS: All preexisting team data in the database will be erased by this routine. * 1794 * * 1795 * HISTORY: * 1796 * 07/30/1996 JLB : Created. * 1797 *=============================================================================================*/ 1798 void TeamTypeClass::Write_INI(CCINIClass & ini) 1799 { 1800 ini.Clear("TeamTypes"); 1801 ini.Clear(INI_Name()); 1802 1803 /* 1804 ** Now write all the team data out 1805 */ 1806 for (int index = 0; index < TeamTypes.Count(); index++) { 1807 // for (int index = TeamTypes.Count()-1; index >= 0; index--) { 1808 TeamTypeClass * team = TeamTypes.Ptr(index); 1809 char buf[256]; 1810 1811 buf[0] = 0; 1812 team->Build_INI_Entry(buf); 1813 ini.Put_String(INI_Name(), team->IniName, buf); 1814 } 1815 } 1816 1817 1818 /*********************************************************************************************** 1819 * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * 1820 * * 1821 * This routine is used to build the text string that will go into the INI database for * 1822 * a team of this type. This text string will be parsed back into a team object when the * 1823 * scenario INI is read in. * 1824 * * 1825 * INPUT: buf -- Pointer to a buffer that will hold the team text entry. * 1826 * * 1827 * OUTPUT: none * 1828 * * 1829 * WARNINGS: Be sure that the buffer can hold the string build. A size of 80 or so is * 1830 * usually sufficient. * 1831 * * 1832 * HISTORY: * 1833 * 07/30/1996 JLB : Created. * 1834 *=============================================================================================*/ 1835 void TeamTypeClass::Build_INI_Entry(char * buf) 1836 { 1837 int code = 0; 1838 code |= IsRoundAbout ? 0x0001 : 0; 1839 code |= IsSuicide ? 0x0002 : 0; 1840 code |= IsAutocreate ? 0x0004 : 0; 1841 code |= IsPrebuilt ? 0x0008 : 0; 1842 code |= IsReinforcable ? 0x0010 : 0; 1843 1844 /* 1845 ** Output the general data for this team type. 1846 */ 1847 sprintf(buf, "%d,%d,%d,%d,%d,%d,%d", 1848 House, 1849 code, 1850 RecruitPriority, 1851 InitNum, 1852 MaxAllowed, 1853 Origin, 1854 TriggerTypes.Logical_ID(Trigger) 1855 ); 1856 buf += strlen(buf); 1857 1858 /* 1859 ** For every class in the team, record the class's name & desired count 1860 */ 1861 sprintf (buf, ",%d", ClassCount); 1862 buf += strlen(buf); 1863 for (int i = 0; i < ClassCount; i++) { 1864 sprintf (buf, ",%s:%d", Members[i].Class->IniName, Members[i].Quantity); 1865 buf += strlen(buf); 1866 } 1867 1868 /* 1869 ** Record the # of missions, and each mission name & argument value. 1870 */ 1871 sprintf(buf, ",%d", MissionCount); 1872 buf += strlen(buf); 1873 for (int i = 0; i < MissionCount; i++) { 1874 sprintf (buf, ",%d:%d", MissionList[i].Mission, MissionList[i].Data.Value); 1875 buf += strlen(buf); 1876 } 1877 } 1878 1879