SCENARIO.CPP (129951B)
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 //Mono_Printf("%d %s\n",__LINE__,__FILE__); 17 /* $Header: /CounterStrike/SCENARIO.CPP 15 3/13/97 2:06p Steve_tall $ */ 18 /*********************************************************************************************** 19 *** 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 *** 20 *********************************************************************************************** 21 * * 22 * Project Name : Command & Conquer * 23 * * 24 * File Name : SCENARIO.CPP * 25 * * 26 * Programmer : Joe L. Bostic * 27 * * 28 * Start Date : September 10, 1993 * 29 * * 30 * Last Update : October 21, 1996 [JLB] * 31 * * 32 * This module handles the scenario reading and writing. Scenario related * 33 * code that is executed between scenario play can also be here. * 34 * * 35 *---------------------------------------------------------------------------------------------* 36 * Functions: * 37 * Assign_Houses -- Assigns multiplayer houses to various players * 38 * Clear_Flag_Spots -- Clears flag overlays off the map * 39 * Clear_Scenario -- Clears all data in preparation for scenario load. * 40 * Clip_Move -- moves in given direction from given cell; clips to map * 41 * Clip_Scatter -- randomly scatters from given cell; won't fall off map * 42 * Create_Units -- Creates infantry & units, for non-base multiplayer * 43 * Do_Lose -- Display losing comments. * 44 * Do_Restart -- Handle the restart mission process. * 45 * Do_Win -- Display winning congratulations. * 46 * Fill_In_Data -- Recreate all data that is not loaded with scenario. * 47 * Post_Load_Game -- Fill in an inferred data from the game state. * 48 * Read_Scenario -- Reads a scenario from disk. * 49 * Read_Scenario_INI -- Read specified scenario INI file. * 50 * Remove_AI_Players -- Removes the computer AI houses & their units * 51 * Restate_Mission -- Handles restating the mission objective. * 52 * Scan_Place_Object -- places an object >near< the given cell * 53 * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * 54 * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * 55 * Set_Scenario_Name -- Creates the INI scenario name string. * 56 * Start_Scenario -- Starts the scenario. * 57 * Write_Scenario_INI -- Write the scenario INI file. * 58 * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * 59 * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * 60 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 61 62 #include "function.h" 63 #ifdef WIN32 64 #include "tcpip.h" 65 #include "ccdde.h" 66 67 extern bool SpawnedFromWChat; 68 #endif 69 extern int PreserveVQAScreen; 70 71 void Display_Briefing_Text_GlyphX(); 72 73 extern void GlyphX_Assign_Houses(void); //ST - 8/8/2019 12:35PM 74 extern bool UseGlyphXStartLocations; //ST - 3/31/2020 9:54AM 75 76 //#include "WolDebug.h" 77 78 #ifdef FIXIT_VERSION_3 // Stalemate games. 79 #include "WolStrng.h" 80 #endif 81 82 static void Remove_AI_Players(void); 83 static void Create_Units(bool official); 84 static CELL Clip_Scatter(CELL cell, int maxdist); 85 static CELL Clip_Move(CELL cell, FacingType facing, int dist); 86 87 // Made this non-static so we can access it from the updated assign players function. ST - 8/9/2019 10:35AM 88 int _build_tech[11] = { 89 2,2, // Tech level 0 and 1 are the same (tech 0 is never used). 90 4, 91 5, 92 7, 93 8, 94 9, 95 10, 96 11, 97 12, 98 13 99 }; 100 101 102 #ifdef FRENCH 103 #define TXT_HACKHACK "Accomplie" 104 #endif 105 #if defined(ENGLISH) || defined(GERMAN) 106 #define TXT_HACKHACK Text_String(TXT_ACCOMPLISHED) 107 #endif 108 109 #ifdef FIXIT_CSII // checked - ajw 9/28/98 110 bool Is_Mission_Counterstrike (char *file_name); 111 bool Is_Mission_Aftermath (char *file_name); 112 #endif 113 114 /*********************************************************************************************** 115 * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * 116 * * 117 * This constructs the default scenario control object. Normally, all the default values * 118 * are meaningless since the act of starting a scenario will fill in all of the values with * 119 * settings retrieved from the scenario control file. * 120 * * 121 * INPUT: none * 122 * * 123 * OUTPUT: none * 124 * * 125 * WARNINGS: none * 126 * * 127 * HISTORY: * 128 * 07/03/1996 JLB : Created. * 129 *=============================================================================================*/ 130 ScenarioClass::ScenarioClass(void) : 131 Difficulty(DIFF_NORMAL), 132 CDifficulty(DIFF_NORMAL), 133 Timer(0), 134 MissionTimer(0), 135 ShroudTimer(TICKS_PER_MINUTE * Rule.ShroudRate), 136 Scenario(1), 137 Theater(THEATER_TEMPERATE), 138 IntroMovie(VQ_NONE), 139 BriefMovie(VQ_NONE), 140 WinMovie(VQ_NONE), 141 WinMovie2(VQ_NONE), 142 WinMovie3(VQ_NONE), 143 WinMovie4(VQ_NONE), 144 LoseMovie(VQ_NONE), 145 ActionMovie(VQ_NONE), 146 TransitTheme(THEME_NONE), 147 PlayerHouse(HOUSE_GREECE), 148 CarryOverPercent(0), 149 CarryOverMoney(0), 150 CarryOverCap(0), 151 Percent(0), 152 BridgeCount(0), 153 CarryOverTimer(0), 154 IsBridgeChanged(false), 155 IsGlobalChanged(false), 156 IsToCarryOver(false), 157 IsToInherit(false), 158 IsTanyaEvac(false), 159 IsFadingBW(false), 160 IsFadingColor(false), 161 IsEndOfGame(false), 162 IsInheritTimer(false), 163 IsNoSpyPlane(false), 164 IsSkipScore(false), 165 IsOneTimeOnly(false), 166 IsNoMapSel(false), 167 IsTruckCrate(false), 168 IsMoneyTiberium(false), 169 #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. 170 #define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40 171 AutoSonarTimer( AUTOSONAR_PERIOD ), 172 #endif 173 FadeTimer(0) 174 { 175 for (int index = 0; index < ARRAY_SIZE(Waypoint); index++) { 176 Waypoint[index] = -1; 177 } 178 strcpy(Description, ""); 179 strcpy(ScenarioName, ""); 180 strcpy(BriefingText, ""); 181 memset(GlobalFlags, '\0', sizeof(GlobalFlags)); 182 memset(Views, '\0', sizeof(Views)); 183 } 184 185 186 /*********************************************************************************************** 187 * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * 188 * * 189 * This routine will start the palette to fade to B/W for a brief moment. * 190 * * 191 * INPUT: none * 192 * * 193 * OUTPUT: none * 194 * * 195 * WARNINGS: none * 196 * * 197 * HISTORY: * 198 * 10/21/1996 JLB : Created. * 199 *=============================================================================================*/ 200 void ScenarioClass::Do_BW_Fade(void) 201 { 202 IsFadingBW = true; 203 IsFadingColor = false; 204 FadeTimer = GRAYFADETIME; 205 } 206 207 208 /*********************************************************************************************** 209 * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * 210 * * 211 * This routine will handle the maintenance of the palette fading effect. It should be * 212 * called once per game frame. * 213 * * 214 * INPUT: none * 215 * * 216 * OUTPUT: none * 217 * * 218 * WARNINGS: none * 219 * * 220 * HISTORY: * 221 * 10/21/1996 JLB : Created. * 222 *=============================================================================================*/ 223 void ScenarioClass::Do_Fade_AI(void) 224 { 225 if (IsFadingColor) { 226 if (FadeTimer == 0) { 227 IsFadingColor = false; 228 } 229 fixed newsat = Options.Get_Saturation() * fixed(GRAYFADETIME-FadeTimer, GRAYFADETIME); 230 Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); 231 GamePalette.Set(); 232 } 233 if (IsFadingBW) { 234 if (FadeTimer == 0) { 235 IsFadingBW = false; 236 } 237 fixed newsat = Options.Get_Saturation() * fixed(FadeTimer, GRAYFADETIME); 238 Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); 239 GamePalette.Set(); 240 if (!IsFadingBW) { 241 IsFadingColor = true; 242 FadeTimer = GRAYFADETIME; 243 } 244 } 245 } 246 247 248 /*********************************************************************************************** 249 * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * 250 * * 251 * This routine will set the global flag to the falue (true/false) specified. It will * 252 * also scan for and spring any triggers that are dependant upon that global. * 253 * * 254 * INPUT: global -- The global flag to change. * 255 * * 256 * value -- The value to change the global flag to. * 257 * * 258 * OUTPUT: Returns with the previous value of the flag. * 259 * * 260 * WARNINGS: none * 261 * * 262 * HISTORY: * 263 * 07/26/1996 JLB : Created. * 264 *=============================================================================================*/ 265 bool ScenarioClass::Set_Global_To(int global, bool value) 266 { 267 if ((unsigned)global < ARRAY_SIZE(Scen.GlobalFlags)) { 268 269 bool previous = GlobalFlags[global]; 270 if (previous != value) { 271 GlobalFlags[global] = value; 272 IsGlobalChanged = true; 273 274 /* 275 ** Special case to scan through all triggers and if any are found that depend on this 276 ** global being set/cleared, then if there is an elapsed time event associated, it 277 ** will be reset at this time. 278 */ 279 for (int index = 0; index < Triggers.Count(); index++) { 280 TriggerClass * tp = Triggers.Ptr(index); 281 if ((tp->Class->Event1.Event == TEVENT_GLOBAL_SET || tp->Class->Event1.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event1.Data.Value == global) { 282 tp->Class->Event2.Reset(tp->Event1); 283 } 284 if ((tp->Class->Event2.Event == TEVENT_GLOBAL_SET || tp->Class->Event2.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event2.Data.Value == global) { 285 tp->Class->Event1.Reset(tp->Event1); 286 } 287 } 288 } 289 return(previous); 290 } 291 return(false); 292 } 293 294 295 /*********************************************************************************************** 296 * Start_Scenario -- Starts the scenario. * 297 * * 298 * This routine will start the scenario. In addition to loading the scenario data, it will * 299 * play the briefing and action movies. * 300 * * 301 * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * 302 * * 303 * briefing -- Should the briefing be played? Normally this is true except when the * 304 * scenario is restarting. * 305 * * 306 * OUTPUT: Was the scenario started without error? * 307 * * 308 * WARNINGS: none * 309 * * 310 * HISTORY: * 311 * 07/04/1995 JLB : Created. * 312 *=============================================================================================*/ 313 bool Start_Scenario(char * name, bool briefing) 314 { 315 //BG Theme.Queue_Song(THEME_QUIET); 316 Theme.Stop(); 317 IsTanyaDead = SaveTanya; 318 if (!Read_Scenario(name)) { 319 return(false); 320 } 321 322 /* Swap Lt. Blue and Blue color remaps in skirmish/multiplayer */ 323 if (Session.Type != GAME_NORMAL) { 324 RemapControlType temp; 325 memcpy(&temp, &ColorRemaps[PCOLOR_LTBLUE], sizeof(RemapControlType)); 326 memcpy(&ColorRemaps[PCOLOR_LTBLUE], &ColorRemaps[PCOLOR_BLUE], sizeof(RemapControlType)); 327 memcpy(&ColorRemaps[PCOLOR_BLUE], &temp, sizeof(RemapControlType)); 328 } 329 330 /* 331 ** Play the winning movie and then start the next scenario. 332 */ 333 RequiredCD = -1; 334 // if (RequiredCD != -2 && Session.Type == GAME_NORMAL) { 335 // if (Scen.Scenario == 1) 336 // RequiredCD = -1; 337 // else { 338 // if((Scen.Scenario >= 20 && Scen.ScenarioName[2] == 'G' || Scen.ScenarioName[2] == 'U') || Scen.ScenarioName[2] == 'A' 339 // || (Scen.ScenarioName[2] == 'M' && Scen.Scenario >= 25)) 340 // RequiredCD = 2; 341 // else if(Scen.ScenarioName[2] == 'U') 342 // RequiredCD = 1; 343 // else if(Scen.ScenarioName[2] == 'G') 344 // RequiredCD = 0; 345 // } 346 // 347 //#ifdef FIXIT_FORCE_CD 348 // Forces the CD to be inserted according to the scenario being loaded. 349 //Hide_Mouse(); 350 //VisiblePage.Clear(); 351 //Show_Mouse(); 352 //GamePalette.Set(); 353 //if (!Force_CD_Available(RequiredCD)) { 354 // Prog_End(); 355 // exit(EXIT_FAILURE); 356 //} 357 //#endif 358 359 360 361 // } 362 Theme.Stop(); 363 364 if (briefing) { 365 Hide_Mouse(); 366 VisiblePage.Clear(); 367 Show_Mouse(); 368 Play_Movie(Scen.IntroMovie); 369 Play_Movie(Scen.BriefMovie); 370 } 371 372 /* 373 ** If there's no briefing movie, restate the mission at the beginning. 374 */ 375 #if 1 // 12/04/2019 - LLL 376 if (Session.Type == GAME_NORMAL && Scen.BriefMovie == VQ_NONE) { 377 Display_Briefing_Text_GlyphX(); 378 } 379 #else 380 char buffer[25]; 381 if (Scen.BriefMovie != VQ_NONE) { 382 sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); 383 } 384 if (Session.Type == GAME_NORMAL && Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { 385 /* 386 ** Make sure the mouse is visible before showing the restatement. 387 */ 388 while(Get_Mouse_State()) { 389 Show_Mouse(); 390 } 391 Restate_Mission(Scen.ScenarioName, TXT_OK, TXT_NONE); 392 } 393 #endif 394 395 if (briefing) { 396 Hide_Mouse(); 397 VisiblePage.Clear(); 398 Show_Mouse(); 399 Play_Movie(Scen.ActionMovie, Scen.TransitTheme); 400 } 401 402 if (Scen.TransitTheme == THEME_NONE) { 403 Theme.Queue_Song(THEME_FIRST); 404 } 405 406 /* 407 ** Set the options values, since the palette has been initialized by Read_Scenario 408 */ 409 Options.Set(); 410 411 return(true); 412 } 413 414 415 /*********************************************************************************************** 416 * Set_Scenario_Difficulty -- Sets the difficulty of the scenario. * 417 * * 418 * Updates the player's difficulty in single-player mode. * 419 * * 420 * INPUT: difficulty -- Scenario difficulty * 421 * * 422 * OUTPUT: none * 423 * * 424 * WARNINGS: Only works in single-player. * 425 * Must call Start_Scenario first to initialize the player. * 426 * * 427 * HISTORY: * 428 * 10/02/2019 SKY : Created. * 429 *=============================================================================================*/ 430 void Set_Scenario_Difficulty(int difficulty) 431 { 432 if (Session.Type == GAME_NORMAL) { 433 switch (difficulty) { 434 case 0: 435 PlayerPtr->Assign_Handicap(DIFF_EASY); 436 break; 437 case 1: 438 PlayerPtr->Assign_Handicap(DIFF_NORMAL); 439 break; 440 case 2: 441 PlayerPtr->Assign_Handicap(DIFF_HARD); 442 break; 443 default: 444 break; 445 } 446 } 447 } 448 449 450 /*********************************************************************************************** 451 * Read_Scenario -- Reads a scenario from disk. * 452 * * 453 * This will read a scenario from disk. Use this to begin a scenario. * 454 * It doesn't perform any rendering, it merely sets up the system * 455 * with the proper data. Setting of the right game state will start * 456 * the scenario running. * 457 * * 458 * INPUT: root -- Scenario root filename * 459 * * 460 * OUTPUT: none * 461 * * 462 * WARNINGS: You must clear out the system variables before calling * 463 * this function. Use the Clear_Scenario() function. * 464 * It is assumed that Scenario is set to the current scenario number. * 465 * * 466 * HISTORY: * 467 * 07/22/1991 : Created. * 468 * 02/03/1992 JLB : Uses house identification. * 469 *=============================================================================================*/ 470 bool Read_Scenario(char * name) 471 { 472 BStart(BENCH_SCENARIO); 473 Clear_Scenario(); 474 ScenarioInit++; 475 if (Read_Scenario_INI(name)) { 476 #ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode case. 477 bool readini = false; 478 switch(Session.Type) { 479 case GAME_NORMAL: 480 readini = false; 481 break; 482 case GAME_SKIRMISH: 483 #ifdef FIXIT_VERSION_3 484 readini = bAftermathMultiplayer; 485 #endif 486 break; 487 case GAME_INTERNET: 488 #ifndef FIXIT_VERSION_3 // Loading of Aftermath rules depends on bAftermathMultiplayer now. 489 if (Is_Mission_Counterstrike(name)) { 490 readini = false; // Don't allow AM units on a CS map in WChat 491 break; 492 } 493 #endif // ( Note lack of break; ) 494 default: 495 #ifdef FIXIT_VERSION_3 496 readini = bAftermathMultiplayer; 497 #else 498 if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { 499 readini = true; 500 } 501 #endif 502 break; 503 } 504 if(readini) { 505 /* 506 ** Find out if the CD in the current drive is the Aftermath disc. 507 */ 508 #ifdef FIXIT_VERSION_3 509 int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); 510 if( !( Using_DVD() && cd_index == 5 ) && cd_index != 3 ) { 511 #else 512 if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { 513 #endif 514 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 515 RequiredCD = 3; 516 if (!Force_CD_Available(RequiredCD)) { // force Aftermath CD in drive. 517 #ifndef WOLAPI_INTEGRATION 518 #ifdef WIN32 519 if(Special.IsFromWChat || SpawnedFromWChat) { 520 char packet[10] = {"Hello"}; 521 Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); 522 } 523 #endif 524 #endif 525 if (!RunningAsDLL) { //PG 526 Emergency_Exit(EXIT_FAILURE); 527 } 528 } 529 } 530 CCINIClass ini; 531 if (ini.Load(CCFileClass("MPLAYER.INI"), false)) { 532 Rule.General(ini); 533 Rule.Recharge(ini); 534 Rule.AI(ini); 535 Rule.Powerups(ini); 536 Rule.Land_Types(ini); 537 Rule.Themes(ini); 538 Rule.IQ(ini); 539 Rule.Objects(ini); 540 Rule.Difficulty(ini); 541 } 542 } 543 #endif 544 Fill_In_Data(); 545 Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); 546 } else { 547 548 #if (1) 549 char message[200]; 550 if (name) { 551 sprintf(message, "Failed to load scenario %s", name); 552 GlyphX_Debug_Print(message); 553 } else { 554 GlyphX_Debug_Print("Failed to load scenario"); 555 } 556 #else 557 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 558 // Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); 559 Show_Mouse(); 560 WWMessageBox().Process(TXT_UNABLE_READ_SCENARIO); 561 Hide_Mouse(); 562 #endif 563 564 BEnd(BENCH_SCENARIO); 565 return(false); 566 } 567 ScenarioInit--; 568 BEnd(BENCH_SCENARIO); 569 return(true); 570 } 571 572 573 /*********************************************************************************************** 574 * Fill_In_Data -- Recreate all data that is not loaded with scenario. * 575 * * 576 * This routine is called after the INI file for the scenario has been processed. It will * 577 * infer the game state from the scenario INI data. * 578 * * 579 * INPUT: none * 580 * * 581 * OUTPUT: none * 582 * * 583 * WARNINGS: none * 584 * * 585 * HISTORY: * 586 * 10/07/1992 JLB : Created. * 587 *=============================================================================================*/ 588 void Fill_In_Data(void) 589 { 590 /* 591 ** The basic scenario data load does not contain the full set of 592 ** game data. We now must fill in the missing pieces. 593 */ 594 ScenarioInit++; 595 int index; 596 for (index = 0; index < Buildings.Count(); index++) { 597 Buildings.Ptr(index)->Update_Buildables(); 598 } 599 600 Map.Flag_To_Redraw(true); 601 602 /* 603 ** Reset the movement zones according to the terrain passability. 604 */ 605 Map.Zone_Reset(MZONEF_ALL); 606 607 608 #ifdef WIN32 609 /* 610 ** Since the sidebar starts up activated, adjust the home start position so that 611 ** the right edge of the map will still be visible. 612 */ 613 if (!Debug_Map) { 614 Map.SidebarClass::Activate(1); 615 // if (Session.Type == GAME_NORMAL) { 616 Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; 617 Map.Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); 618 // } 619 } 620 #endif 621 622 /* 623 ** Handle any data resetting that can be safely inferred from the actual 624 ** data that has been loaded. 625 */ 626 /* 627 ** Distribute the trigger pointers to the appropriate working lists. 628 */ 629 for (index = 0; index < TriggerTypes.Count(); index++) { 630 TriggerTypeClass * tp = TriggerTypes.Ptr(index); 631 632 assert(tp != NULL); 633 634 if (tp->Attaches_To() & ATTACH_MAP) { 635 MapTriggers.Add(Find_Or_Make(tp)); 636 } 637 if (tp->Attaches_To() & ATTACH_GENERAL) { 638 LogicTriggers.Add(Find_Or_Make(tp)); 639 } 640 if (tp->Attaches_To() & ATTACH_HOUSE) { 641 HouseTriggers[tp->House].Add(Find_Or_Make(tp)); 642 } 643 } 644 645 ScenarioInit--; 646 647 /* 648 ** Now go through and set all the cells ringing the map to be visible, so 649 ** we won't get the wall of shadow at the edge of the map. 650 */ 651 int x,y; 652 for (x = Map.MapCellX-1; x < ((Map.MapCellX + Map.MapCellWidth + 1)); x++) { 653 Map[XY_Cell(x, Map.MapCellY-1)].IsVisible = 654 Map[XY_Cell(x, Map.MapCellY-1)].IsMapped = true; 655 656 Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsVisible = 657 Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsMapped = true; 658 } 659 for (y = Map.MapCellY; y < (Map.MapCellY + Map.MapCellHeight); y++) { 660 Map[XY_Cell(Map.MapCellX-1, y)].IsVisible = 661 Map[XY_Cell(Map.MapCellX-1, y)].IsMapped = true; 662 Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsVisible = 663 Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsMapped = true; 664 } 665 666 /* 667 ** If inheriting from a previous scenario was indicated, then create the carry over 668 ** objects at this time. 669 */ 670 if (Scen.IsToInherit) { 671 CarryoverClass * cptr = Carryover; 672 while (cptr != NULL) { 673 cptr->Create(); 674 cptr = (CarryoverClass *)cptr->Get_Next(); 675 } 676 } 677 678 /* 679 ** The "allow win" action is a special case that is handled here. The total number 680 ** of triggers that have this action must be recorded. 681 */ 682 for (index = 0; index < TriggerTypes.Count(); index++) { 683 TriggerTypeClass * tp = TriggerTypes.Ptr(index); 684 if (tp->Action1.Action == TACTION_ALLOWWIN || 685 (tp->ActionControl != MULTI_ONLY && tp->Action2.Action == TACTION_ALLOWWIN)) { 686 687 HouseClass::As_Pointer(tp->House)->Blockage++; 688 } 689 } 690 691 /* 692 ** Move available money to silos, if the scenario flag so indicates. 693 */ 694 if (Scen.IsMoneyTiberium) { 695 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 696 HouseClass * hptr = HouseClass::As_Pointer(house); 697 if (hptr != NULL) { 698 int tomove = hptr->Capacity - hptr->Tiberium; 699 hptr->Credits -= tomove; 700 hptr->Tiberium += tomove; 701 } 702 } 703 } 704 705 /* 706 ** Count all non-destroyed bridges on the map. 707 */ 708 Scen.BridgeCount = Map.Intact_Bridge_Count(); 709 710 Map.All_To_Look(PlayerPtr, true); 711 } 712 713 714 /*********************************************************************************************** 715 * Post_Load_Game -- Fill in an inferred data from the game state. * 716 * * 717 * This routine is typically called after a game has been loaded. Some working data lists * 718 * can be rebuild from the game state. This working data is rebuilt rather than being * 719 * stored with the game data file. * 720 * * 721 * INPUT: load_multi -- true if we're loading a multiplayer game * 722 * * 723 * OUTPUT: none * 724 * * 725 * WARNINGS: Although it is safe to call this routine whenever, it is only needed after a * 726 * game load. * 727 * * 728 * HISTORY: * 729 * 11/30/1995 JLB : Created. * 730 *=============================================================================================*/ 731 void Post_Load_Game(int load_multi) 732 { 733 Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); 734 // 735 // Do NOT call Overpass if we're loading a multiplayer game; it calls the 736 // random # generator, which throws the games out of sync if they were 737 // saved on different frame #'s. 738 // 739 if (!load_multi) { 740 Map.Overpass(); 741 } 742 Scen.BridgeCount = Map.Intact_Bridge_Count(); 743 Map.Zone_Reset(MZONEF_ALL); 744 } 745 746 747 /*********************************************************************************************** 748 * Clear_Scenario -- Clears all data in preparation for scenario load. * 749 * * 750 * This routine will clear out all data specific to a scenario in * 751 * preparation for a subsequent scenario data load. This will free * 752 * all units, animations, and icon maps. * 753 * * 754 * INPUT: none * 755 * * 756 * OUTPUT: none * 757 * * 758 * WARNINGS: none * 759 * * 760 * HISTORY: * 761 * 07/22/1991 : Created. * 762 * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * 763 * 07/13/1995 JLB : End count down moved here. * 764 *=============================================================================================*/ 765 void Clear_Scenario(void) 766 { 767 // TCTCTC -- possibly just use in-place new of scenario object? 768 769 Scen.MissionTimer = 0; 770 Scen.MissionTimer.Stop(); 771 Scen.Timer = 0; 772 Scen.ShroudTimer = 0; 773 Scen.IntroMovie = VQ_NONE; 774 Scen.BriefMovie = VQ_NONE; 775 Scen.WinMovie = VQ_NONE; 776 Scen.WinMovie2 = VQ_NONE; 777 Scen.WinMovie3 = VQ_NONE; 778 Scen.WinMovie4 = VQ_NONE; 779 Scen.LoseMovie = VQ_NONE; 780 Scen.ActionMovie = VQ_NONE; 781 Scen.IsNoSpyPlane = false; 782 Scen.IsTanyaEvac = false; 783 Scen.IsEndOfGame = false; 784 Scen.IsInheritTimer = false; 785 Scen.IsToCarryOver = false; 786 Scen.IsSkipScore = false; 787 Scen.IsOneTimeOnly = false; 788 Scen.IsTruckCrate = false; 789 Scen.IsMoneyTiberium = false; 790 Scen.IsNoMapSel = false; 791 Scen.CarryOverCap = 0; 792 Scen.CarryOverPercent = 0; 793 Scen.TransitTheme = THEME_NONE; 794 Scen.Percent = 0; 795 796 memset(Scen.GlobalFlags, 0, sizeof(Scen.GlobalFlags)); 797 798 MapTriggers.Clear(); 799 LogicTriggers.Clear(); 800 801 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 802 HouseTriggers[house].Clear(); 803 } 804 805 /* 806 ** Call everyone's Init routine, except the Map's; for the Map, only call 807 ** MapClass::Init, which clears the Cell array. The Display::Init requires 808 ** a Theater argument, and the theater is not known at this point; also, it 809 ** would reload MixFiles, which isn't desired. Display::Read_INI calls its 810 ** own Init, which will Init the entire Map hierarchy. 811 */ 812 Map.Init_Clear(); 813 Score.Init(); 814 Logic.Init(); 815 816 HouseClass::Init(); 817 ObjectClass::Init(); 818 TeamTypeClass::Init(); 819 TeamClass::Init(); 820 TriggerClass::Init(); 821 TriggerTypeClass::Init(); 822 AircraftClass::Init(); 823 AnimClass::Init(); 824 BuildingClass::Init(); 825 BulletClass::Init(); 826 InfantryClass::Init(); 827 OverlayClass::Init(); 828 SmudgeClass::Init(); 829 TemplateClass::Init(); 830 TerrainClass::Init(); 831 UnitClass::Init(); 832 VesselClass::Init(); 833 834 FactoryClass::Init(); 835 836 Base.Init(); 837 838 CurrentObject.Clear_All(); 839 840 for (int index = 0; index < WAYPT_COUNT; index++) { 841 Scen.Waypoint[index] = -1; 842 } 843 844 #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. 845 bAutoSonarPulse = false; 846 #endif 847 848 #ifdef FIXIT_VERSION_3 // Stalemate games. 849 Scen.bLocalProposesDraw = false; 850 Scen.bOtherProposesDraw = false; 851 #endif 852 853 } 854 855 856 /*********************************************************************************************** 857 * Do_Win -- Display winning congratulations. * 858 * * 859 * Perform the win the mission process. This will display any winning movies and the score * 860 * screen. Followed by the map selection screen and then the load of the new scenario. * 861 * * 862 * INPUT: none * 863 * * 864 * OUTPUT: none * 865 * * 866 * WARNINGS: none * 867 * * 868 * HISTORY: * 869 * 08/05/1992 JLB : Created. * 870 * 01/01/1995 JLB : Carries money forward into next scenario. * 871 *=============================================================================================*/ 872 void Do_Win(void) 873 { 874 Map.Set_Default_Mouse(MOUSE_NORMAL); 875 Hide_Mouse(); 876 Theme.Queue_Song(THEME_QUIET); 877 878 /* 879 ** If this is a multiplayer game, clear the game's name so we won't respond 880 ** to game queries any more (in Call_Back) 881 */ 882 if (Session.Type != GAME_NORMAL) { 883 Session.GameName[0] = 0; 884 } 885 886 /* 887 ** Determine a cosmetic center point for the text. 888 */ 889 int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); 890 891 /* 892 ** Hack section. If it's allied scenario 10, variation A, then skip the 893 ** score and map selection, don't increment scenario, and set it to 894 ** variation B. 895 */ 896 #ifdef FIXIT_ANTS 897 if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore || AntsEnabled) { 898 #else 899 if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore ) { 900 #endif //FIXIT_ANTS 901 902 /* 903 ** Announce win to player. 904 */ 905 Set_Logic_Page(SeenBuff); 906 Map.Flag_To_Redraw (true); 907 Map.Render(); 908 #ifdef WIN32 909 Fancy_Text_Print(TXT_SCENARIO_WON, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); 910 #else 911 Fancy_Text_Print(TXT_MISSION, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); 912 Fancy_Text_Print(TXT_HACKHACK, x, 110*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); 913 #endif 914 CountDownTimer = TIMER_SECOND * 3; 915 while (Is_Speaking()) {}; 916 Speak(VOX_ACCOMPLISHED); 917 while (CountDownTimer || Is_Speaking()) { 918 Call_Back(); 919 } 920 } 921 922 /* 923 ** Stop here if this is a multiplayer game. 924 */ 925 if (Session.Type != GAME_NORMAL) { 926 if (!Session.Play) { 927 Session.GamesPlayed++; 928 Multi_Score_Presentation(); 929 Session.CurGame++; 930 if (Session.CurGame >= MAX_MULTI_GAMES) { 931 Session.CurGame = MAX_MULTI_GAMES - 1; 932 } 933 } 934 GameActive = 0; 935 Show_Mouse(); 936 return; 937 } 938 939 Hide_Mouse(); 940 VisiblePage.Clear(); 941 Show_Mouse(); 942 Play_Movie(Scen.WinMovie); 943 Play_Movie(Scen.WinMovie2); 944 Play_Movie(Scen.WinMovie3); 945 Play_Movie(Scen.WinMovie4); 946 947 Keyboard->Clear(); 948 949 SaveTanya = IsTanyaDead; 950 Scen.CarryOverTimer = Scen.MissionTimer; 951 // int timer = Scen.MissionTimer; 952 953 /* 954 ** Do the ending screens only if not playing back a recorded game. 955 */ 956 if (!Session.Play) { 957 /* 958 ** If the score presentation should be performed, then do 959 ** so now. 960 */ 961 Keyboard->Clear(); 962 if (!Scen.IsSkipScore) { 963 Score.Presentation(); 964 } 965 966 967 if (Scen.IsOneTimeOnly) { 968 GameActive = false; 969 Show_Mouse(); 970 #ifdef FIXIT_ANTS 971 AntsEnabled = false; 972 // Mono_Printf("Scenario.cpp one time only antsenabled is false\n"); 973 #endif 974 return; 975 } 976 977 /* 978 ** If this scenario is flagged as ending the game then print the credits and exit. 979 */ 980 #if (0)//PG 981 if (Scen.IsEndOfGame) { 982 if (PlayerPtr->ActLike == HOUSE_USSR) { 983 Play_Movie(VQ_SOVFINAL); 984 } else { 985 Play_Movie(VQ_ALLYEND); 986 } 987 Show_Who_Was_Responsible(); 988 GameActive = false; 989 Show_Mouse(); 990 #ifdef FIXIT_ANTS 991 AntsEnabled = false; 992 #endif 993 return; 994 } 995 #endif 996 /* 997 ** Hack section. If it's allied scenario 10, variation A, then skip the 998 ** score and map selection, don't increment scenario, and set it to 999 ** variation B. 1000 */ 1001 if (Scen.IsNoMapSel) { 1002 // force it to play the second half of scenario 10 1003 #ifdef FIXIT_ANTS 1004 if (AntsEnabled) { 1005 char scenarioname[24]; 1006 strcpy(scenarioname, Scen.ScenarioName); 1007 char buf[10]; 1008 Scen.Scenario++; 1009 sprintf(buf, "%02d", Scen.Scenario); 1010 memcpy(&scenarioname[3], buf, 2); 1011 Scen.Set_Scenario_Name(scenarioname); 1012 } else { 1013 Scen.ScenarioName[6] = 'B'; 1014 } 1015 1016 #else 1017 Scen.ScenarioName[6] = 'B'; 1018 #endif 1019 1020 } else { 1021 Scen.Set_Scenario_Name(Map_Selection()); 1022 } 1023 1024 Keyboard->Clear(); 1025 } 1026 1027 Scen.CarryOverMoney = PlayerPtr->Credits; 1028 1029 /* 1030 ** If requested, record the scenario's objects in the carry over list 1031 ** for possible use in a future scenario. 1032 */ 1033 if (Scen.IsToCarryOver) { 1034 1035 /* 1036 ** First delete any existing carry over list. Any old list will be 1037 ** blasted over by the new list -- there is only one logic carryover 1038 ** list to be maintained. 1039 */ 1040 while (Carryover) { 1041 CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); 1042 Carryover->Remove(); 1043 delete Carryover; 1044 Carryover = cptr; 1045 } 1046 1047 /* 1048 ** Record all objects, that are to be part of the carry over set, into 1049 ** the carry over list. 1050 */ 1051 for (int building_index = 0; building_index < Buildings.Count(); building_index++) { 1052 BuildingClass * building = Buildings.Ptr(building_index); 1053 1054 if (building && !building->IsInLimbo && building->Strength > 0) { 1055 CarryoverClass * cptr = new CarryoverClass(building); 1056 1057 if (cptr) { 1058 if (Carryover) { 1059 cptr->Add_Tail(*Carryover); 1060 } else { 1061 Carryover = cptr; 1062 } 1063 } 1064 } 1065 } 1066 for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { 1067 UnitClass * unit = Units.Ptr(unit_index); 1068 1069 if (unit && !unit->IsInLimbo && unit->Strength > 0) { 1070 CarryoverClass * cptr = new CarryoverClass(unit); 1071 1072 if (cptr) { 1073 if (Carryover) { 1074 cptr->Add_Tail(*Carryover); 1075 } else { 1076 Carryover = cptr; 1077 } 1078 } 1079 } 1080 } 1081 for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { 1082 InfantryClass * infantry = Infantry.Ptr(infantry_index); 1083 1084 if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { 1085 CarryoverClass * cptr = new CarryoverClass(infantry); 1086 1087 if (cptr) { 1088 if (Carryover) { 1089 cptr->Add_Tail(*Carryover); 1090 } else { 1091 Carryover = cptr; 1092 } 1093 } 1094 } 1095 } 1096 for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { 1097 VesselClass * vessel = Vessels.Ptr(vessel_index); 1098 1099 if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { 1100 CarryoverClass * cptr = new CarryoverClass(vessel); 1101 1102 if (cptr) { 1103 if (Carryover) { 1104 cptr->Add_Tail(*Carryover); 1105 } else { 1106 Carryover = cptr; 1107 } 1108 } 1109 } 1110 } 1111 } 1112 1113 /* 1114 ** Generate a new scenario filename 1115 */ 1116 // Scen.Set_Scenario_Name(Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, Scen.ScenVar); 1117 Start_Scenario(Scen.ScenarioName); 1118 1119 /* 1120 ** If the mission timer is to be inheriteded from the previous scenario then do it now. 1121 */ 1122 if (Scen.IsInheritTimer) { 1123 Scen.MissionTimer = Scen.CarryOverTimer; 1124 Scen.MissionTimer.Start(); 1125 } 1126 1127 // PlayerPtr->NukePieces = nukes; 1128 1129 Map.Render(); 1130 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 1131 // Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); 1132 Show_Mouse(); 1133 } 1134 1135 1136 /*********************************************************************************************** 1137 * Do_Lose -- Display losing comments. * 1138 * * 1139 * Performs the lose mission processing. This will generally display a "would you like * 1140 * to replay" dialog and then either reload the scenario or set flags such that the main * 1141 * menu will appear. * 1142 * * 1143 * INPUT: none * 1144 * * 1145 * OUTPUT: none * 1146 * * 1147 * WARNINGS: none * 1148 * * 1149 * HISTORY: * 1150 * 08/05/1992 JLB : Created. * 1151 *=============================================================================================*/ 1152 void Do_Lose(void) 1153 { 1154 Map.Set_Default_Mouse(MOUSE_NORMAL); 1155 Hide_Mouse(); 1156 1157 Theme.Queue_Song(THEME_QUIET); 1158 1159 /* 1160 ** If this is a multiplayer game, clear the game's name so we won't respond 1161 ** to game queries any more (in Call_Back) 1162 */ 1163 if (Session.Type != GAME_NORMAL) { 1164 Session.GameName[0] = 0; 1165 } 1166 1167 /* 1168 ** Determine a cosmetic center point for the text. 1169 */ 1170 int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); 1171 1172 /* 1173 ** Announce win to player. 1174 */ 1175 Set_Logic_Page(SeenBuff); 1176 Fancy_Text_Print(TXT_SCENARIO_LOST, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); 1177 CountDownTimer = TIMER_SECOND * 3; 1178 while (Is_Speaking()) {}; 1179 Speak(VOX_FAIL); 1180 while (CountDownTimer || Is_Speaking()) { 1181 Call_Back(); 1182 } 1183 1184 /* 1185 ** Stop here if this is a multiplayer game. 1186 */ 1187 if (Session.Type != GAME_NORMAL) { 1188 if (!Session.Play) { 1189 Session.GamesPlayed++; 1190 Multi_Score_Presentation(); 1191 Session.CurGame++; 1192 if (Session.CurGame >= MAX_MULTI_GAMES) { 1193 Session.CurGame = MAX_MULTI_GAMES - 1; 1194 } 1195 } 1196 GameActive = 0; 1197 Show_Mouse(); 1198 return; 1199 } 1200 1201 Hide_Mouse(); 1202 VisiblePage.Clear(); 1203 Show_Mouse(); 1204 #ifdef CHEAT_KEYS 1205 // Mono_Printf("Trying to play lose movie\n"); 1206 #endif //CHEAT_KEYS 1207 Play_Movie(Scen.LoseMovie); 1208 1209 /* 1210 ** Start same scenario again 1211 */ 1212 GamePalette.Set(); 1213 Show_Mouse(); 1214 if (!Session.Play && !WWMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { 1215 Hide_Mouse(); 1216 Keyboard->Clear(); 1217 Start_Scenario(Scen.ScenarioName, false); 1218 1219 /* 1220 ** Start the scenario timer with the carried over value if necessary. 1221 */ 1222 if (Scen.IsInheritTimer) { 1223 Scen.MissionTimer = Scen.CarryOverTimer; 1224 Scen.MissionTimer.Start(); 1225 } 1226 1227 Map.Render(); 1228 } else { 1229 Hide_Mouse(); 1230 GameActive = 0; 1231 } 1232 1233 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 1234 Show_Mouse(); 1235 } 1236 1237 #ifdef FIXIT_VERSION_3 // Stalemate games. 1238 /*********************************************************************************************** 1239 * Do_Draw -- Parallels Do_Win and Do_Lose, for multiplayer games that end in a draw. 1240 *=============================================================================================*/ 1241 void Do_Draw(void) 1242 { 1243 Map.Set_Default_Mouse(MOUSE_NORMAL); 1244 Hide_Mouse(); 1245 1246 Theme.Queue_Song(THEME_QUIET); 1247 1248 /* 1249 ** If this is a multiplayer game, clear the game's name so we won't respond 1250 ** to game queries any more (in Call_Back) 1251 */ 1252 if (Session.Type != GAME_NORMAL) { 1253 Session.GameName[0] = 0; 1254 } 1255 1256 /* 1257 ** Determine a cosmetic center point for the text. 1258 */ 1259 int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); 1260 1261 /* 1262 ** Announce win to player. 1263 */ 1264 Set_Logic_Page(SeenBuff); 1265 Fancy_Text_Print(TXT_WOL_DRAW, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); 1266 CountDownTimer = TIMER_SECOND * 3; 1267 while (Is_Speaking()) {}; 1268 Speak(VOX_CONTROL_EXIT); 1269 while (CountDownTimer || Is_Speaking()) { 1270 Call_Back(); 1271 } 1272 1273 /* 1274 ** Stop here if this is a multiplayer game. 1275 */ 1276 if (!Session.Play) { 1277 Session.GamesPlayed++; 1278 Multi_Score_Presentation(); 1279 Session.CurGame++; 1280 if (Session.CurGame >= MAX_MULTI_GAMES) { 1281 Session.CurGame = MAX_MULTI_GAMES - 1; 1282 } 1283 } 1284 GameActive = 0; 1285 Show_Mouse(); 1286 } 1287 #endif 1288 1289 /*********************************************************************************************** 1290 * Do_Restart -- Handle the restart mission process. * 1291 * * 1292 * This routine is called in the main game loop when the mission must be restarted. This * 1293 * routine will throw away the current game and reload the appropriate mission. The * 1294 * game will "resume" at the start of the mission. * 1295 * * 1296 * INPUT: none * 1297 * * 1298 * OUTPUT: none * 1299 * * 1300 * WARNINGS: none * 1301 * * 1302 * HISTORY: * 1303 * 08/24/1995 JLB : Created. * 1304 *=============================================================================================*/ 1305 void Do_Restart(void) 1306 { 1307 /* 1308 ** Start a timer going, before we restart the scenario 1309 */ 1310 CDTimerClass<SystemTimerClass> timer; 1311 timer = TICKS_PER_SECOND * 4; 1312 Theme.Queue_Song(THEME_QUIET); 1313 1314 WWMessageBox().Process(TXT_RESTARTING, TXT_NONE); 1315 1316 Map.Set_Default_Mouse(MOUSE_NORMAL); 1317 Keyboard->Clear(); 1318 Start_Scenario(Scen.ScenarioName, false); 1319 1320 /* 1321 ** Start the scenario timer with the carried over value if necessary. 1322 */ 1323 if (Scen.IsInheritTimer) { 1324 Scen.MissionTimer = Scen.CarryOverTimer; 1325 Scen.MissionTimer.Start(); 1326 } 1327 1328 /* 1329 ** Make sure the message stays displayed for at least 1 second 1330 */ 1331 while (timer > 0) { 1332 Call_Back(); 1333 } 1334 Keyboard->Clear(); 1335 1336 Map.Render(); 1337 } 1338 1339 1340 /*********************************************************************************************** 1341 * Restate_Mission -- Handles restating the mission objective. * 1342 * * 1343 * This routine will display the mission objective (as text). It will also give the * 1344 * option to redisplay the mission briefing video. * 1345 * * 1346 * INPUT: name -- The scenario name. This is the unique identifier for the scenario * 1347 * briefing text as it appears in the "MISSION.INI" file. * 1348 * * 1349 * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * 1350 * requested, or 0 if the return to game options button was selected. * 1351 * * 1352 * WARNINGS: none * 1353 * * 1354 * HISTORY: * 1355 * 06/23/1995 JLB : Created. * 1356 * 08/06/1995 JLB : Uses preloaded briefing text. * 1357 *=============================================================================================*/ 1358 bool Restate_Mission(char const * name, int button1, int button2) 1359 { 1360 if (name) { 1361 1362 bool brief = true; 1363 char buffer[25]; 1364 if (Scen.BriefMovie != VQ_NONE) { 1365 sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); 1366 } 1367 1368 if (Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { 1369 button2 = TXT_OK; 1370 button1 = TXT_NONE; 1371 brief = false; 1372 } 1373 1374 /* 1375 ** If mission object text was found, then display it. 1376 */ 1377 if (strlen(Scen.BriefingText)) { 1378 strcpy(_ShapeBuffer, Scen.BriefingText); 1379 BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); 1380 if (BGMessageBox(_ShapeBuffer, button2, button1)) { 1381 return(true); 1382 } 1383 if (!brief) return(true); 1384 return(false); 1385 } 1386 } 1387 return(false); 1388 } 1389 1390 1391 #define BUTTON_1 1 1392 #define BUTTON_2 2 1393 #define BUTTON_3 3 1394 #define BUTTON_FLAG 0x8000 1395 int BGMessageBox(char const * msg, int btn1, int btn2) 1396 { 1397 #define BUFFSIZE 511 1398 char buffer[BUFFSIZE]; 1399 int retval; 1400 bool process; // loop while true 1401 KeyNumType input; // user input 1402 int selection; 1403 bool pressed; 1404 int curbutton; 1405 TextButtonClass * buttons[3]; 1406 BOOL display; // display level 1407 int realval[5]; 1408 int morebutton = 3; // which button says "more": 2 or 3? 1409 1410 const char * b1txt = Text_String(btn1); 1411 const char * b2txt = Text_String(btn2); 1412 #ifdef FRENCH 1413 const char * b3txt = "SUITE"; 1414 #else 1415 #ifdef GERMAN 1416 const char * b3txt = "MEHR"; 1417 #else 1418 const char * b3txt = "MORE"; 1419 #endif 1420 #endif 1421 1422 const void *briefsnd = MFCD::Retrieve("BRIEFING.AUD"); 1423 1424 GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_TYPE]); 1425 1426 /* 1427 ** If the message won't be needing the 'more' button, get rid of it. 1428 */ 1429 if (strlen(msg) <= BUFFSIZE-1) { 1430 b3txt = ""; 1431 } 1432 1433 #ifdef WIN32 1434 GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); 1435 #endif 1436 1437 /* 1438 ** If there's no text for button one, zero it out. 1439 */ 1440 if (*b1txt == '\0') { 1441 b1txt = b2txt; 1442 b2txt = ""; 1443 if(*b1txt == '\0') { 1444 b1txt=0; 1445 } 1446 } 1447 1448 /* 1449 ** If there's no text for button two, zero it out. However, if there 1450 ** is text for button three, move its text (always "MORE") to button two, 1451 ** and set the morebutton flag to point to button two. Then, clear out 1452 ** button 3. 1453 */ 1454 if (*b2txt == '\0') { 1455 b2txt = 0; 1456 if (*b3txt != '\0') { 1457 b2txt = b3txt; 1458 b3txt = ""; 1459 morebutton = 1; 1460 } 1461 } 1462 1463 /* 1464 ** If there's no text for button three, zero it out. 1465 */ 1466 if (*b3txt == '\0') b3txt = 0; 1467 1468 Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); 1469 /* 1470 ** Examine the optional button parameters. Fetch the width and starting 1471 ** characters for each. 1472 */ 1473 char b1char, b2char, b3char; // 1st char of each string 1474 int bwidth, bheight; // button width and height 1475 int numbuttons = 0; 1476 if (b1txt) { 1477 b1char = toupper(b1txt[0]); 1478 1479 /* 1480 ** Build the button list. 1481 */ 1482 bheight = FontHeight + FontYSpacing + 2; 1483 bwidth = max((String_Pixel_Width(b1txt) + 8), 80); 1484 if (b2txt) { 1485 numbuttons = 2; 1486 b2char = toupper(b2txt[0]); 1487 bwidth = max(((int)String_Pixel_Width( b2txt ) + 8), bwidth); 1488 // b1x = x + 10; // left side 1489 1490 if (b3txt) { 1491 numbuttons = 3; 1492 b3char = toupper(b3txt[0]); 1493 bwidth = max(((int)String_Pixel_Width( b3txt ) + 8), bwidth); 1494 } 1495 1496 } else { 1497 numbuttons = 1; 1498 // b1x = x + ((width - bwidth) >> 1); // centered 1499 } 1500 } 1501 1502 /* 1503 ** Determine the dimensions of the text to be used for the dialog box. 1504 ** These dimensions will control how the dialog box looks. 1505 */ 1506 buffer[BUFFSIZE-1] = 0; 1507 int buffend = BUFFSIZE-1; 1508 strncpy(buffer, msg, BUFFSIZE-1); 1509 /* 1510 ** Scan through the string to see if it got clipped, and if so, we'll 1511 ** trim it back to the last space so it'll clip on a word. 1512 */ 1513 if (strlen(buffer) != strlen(msg)) { 1514 while (buffer[buffend] != ' ') buffend--; 1515 buffer[buffend]=0; 1516 } 1517 Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); 1518 int width; 1519 int height; 1520 Format_Window_String(buffer, 300, width, height); 1521 height += (numbuttons == 0) ? 30 : 60; 1522 1523 int x = (SeenBuff.Get_Width() - width) / 2; 1524 int y = (SeenBuff.Get_Height() - height) / 2; 1525 1526 /* 1527 ** Other inits. 1528 */ 1529 Set_Logic_Page(SeenBuff); 1530 1531 /* 1532 ** Initialize the button structures. All are initialized, even though one (or none) may 1533 ** actually be added to the button list. 1534 */ 1535 TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, 1536 x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : 10), y + height - (bheight + 5), bwidth); 1537 1538 TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, 1539 x + width - (bwidth + 10), y + height - (bheight + 5), bwidth); 1540 1541 TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, 1542 0, y + height - (bheight + 5)); 1543 button3.X = x + ((width - button3.Width) >> 1); 1544 1545 TextButtonClass * buttonlist = 0; 1546 curbutton = 0; 1547 1548 /* 1549 ** Add and initialize the buttons to the button list. 1550 */ 1551 if (numbuttons) { 1552 buttonlist = &button1; 1553 buttons[0] = &button1; 1554 realval[0] = BUTTON_1; 1555 if (numbuttons > 2) { 1556 button3.Add(*buttonlist); 1557 buttons[1] = &button3; 1558 realval[1] = BUTTON_3; 1559 button2.Add(*buttonlist); 1560 buttons[2] = &button2; 1561 realval[2] = BUTTON_2; 1562 buttons[curbutton]->Turn_On(); 1563 } else if (numbuttons == 2) { 1564 button2.Add(*buttonlist); 1565 buttons[1] = &button2; 1566 realval[1] = BUTTON_2; 1567 buttons[curbutton]->Turn_On(); 1568 } 1569 } 1570 1571 /* 1572 ** Draw the dialog. 1573 */ 1574 Hide_Mouse(); 1575 1576 PaletteClass temp; 1577 #ifdef WIN32 1578 char *filename = "SOVPAPER.PCX"; 1579 if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { 1580 filename = "ALIPAPER.PCX"; 1581 } 1582 Load_Title_Screen(filename, &HidPage, (unsigned char*)temp.Get_Data()); 1583 #else 1584 char *filename = "SOVPAPER.CPS"; 1585 if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { 1586 filename = "ALIPAPER.CPS"; 1587 } 1588 Load_Uncompress(CCFileClass(filename), HidPage, HidPage, temp); 1589 #endif 1590 HidPage.Blit(SeenPage); 1591 1592 #ifdef WIN32 1593 VisiblePage.Blit(seen_buff_save); 1594 #endif 1595 1596 static unsigned char _scorepal[]={0,1,12,13,4,5,6,7,8,9,10,255,252,253,14,248}; 1597 Set_Font_Palette(_scorepal); 1598 temp.Set(FADE_PALETTE_MEDIUM, Call_Back); 1599 1600 /* 1601 ** Main Processing Loop. 1602 */ 1603 1604 int bufindex = 0; 1605 1606 Keyboard->Clear(); 1607 1608 Set_Font_Palette(_scorepal); 1609 int xprint = x + 20; 1610 int yprint = y + 25; 1611 do { 1612 #ifdef WIN32 1613 /* 1614 ** If we have just received input focus again after running in the background then 1615 ** we need to redraw. 1616 */ 1617 if (AllSurfaces.SurfacesRestored) { 1618 AllSurfaces.SurfacesRestored = false; 1619 Hide_Mouse(); 1620 seen_buff_save.Blit(VisiblePage); 1621 display = true; 1622 Show_Mouse(); 1623 } 1624 #endif 1625 char bufprint[2]; 1626 bufprint[1]=0; 1627 bufprint[0] = buffer[bufindex]; 1628 if (bufprint[0] == '\r' || bufprint[0] == '@') { 1629 xprint = x + 20; 1630 yprint += FontHeight + FontYSpacing; 1631 1632 } else { 1633 if (bufprint[0] != 20) { 1634 SeenPage.Print(bufprint, xprint, yprint, TBLACK, TBLACK); 1635 #ifdef WIN32 1636 seen_buff_save.Print(bufprint, xprint, yprint, TBLACK, TBLACK); 1637 #endif 1638 xprint += Char_Pixel_Width(bufprint[0]); 1639 } 1640 } 1641 if (bufprint[0] == '\r' || bufprint[0] == '@') { 1642 #ifdef WIN32 1643 Play_Sample(briefsnd, 255, Options.Normalize_Volume(135)); 1644 #else 1645 Play_Sample(briefsnd, 255, Options.Normalize_Volume(45)); 1646 #endif 1647 CDTimerClass<SystemTimerClass> cd; 1648 cd = 5; 1649 do { 1650 Call_Back(); 1651 } while(!Keyboard->Check() && cd); 1652 } 1653 } while (buffer[++bufindex]); 1654 1655 Show_Mouse(); 1656 Keyboard->Clear(); 1657 1658 if (buttonlist) { 1659 process = true; 1660 pressed = false; 1661 while (process) { 1662 #ifdef WIN32 1663 /* 1664 ** If we have just received input focus again after running in the background then 1665 ** we need to redraw. 1666 */ 1667 if (AllSurfaces.SurfacesRestored) { 1668 AllSurfaces.SurfacesRestored = false; 1669 Hide_Mouse(); 1670 seen_buff_save.Blit(VisiblePage); 1671 display = true; 1672 Show_Mouse(); 1673 } 1674 #endif 1675 1676 if (display) { 1677 display = false; 1678 1679 Hide_Mouse(); 1680 1681 /* 1682 ** Redraw the buttons. 1683 */ 1684 if (buttonlist) { 1685 buttonlist->Draw_All(); 1686 } 1687 Show_Mouse(); 1688 } 1689 1690 /* 1691 ** Invoke game callback. 1692 */ 1693 Call_Back(); 1694 1695 /* 1696 ** Fetch and process input. 1697 */ 1698 input = buttonlist->Input(); 1699 switch (input) { 1700 case (BUTTON_1|BUTTON_FLAG): 1701 selection = realval[0]; 1702 pressed = true; 1703 break; 1704 1705 case (KN_ESC): 1706 if (numbuttons > 2) { 1707 selection = realval[1]; 1708 pressed = true; 1709 } else { 1710 selection = realval[2]; 1711 pressed = true; 1712 } 1713 break; 1714 1715 case (BUTTON_2|BUTTON_FLAG): 1716 selection = BUTTON_2; 1717 pressed = true; 1718 break; 1719 1720 case (BUTTON_3|BUTTON_FLAG): 1721 selection = realval[1]; 1722 pressed = true; 1723 break; 1724 1725 case (KN_LEFT): 1726 if (numbuttons > 1) { 1727 buttons[curbutton]->Turn_Off(); 1728 buttons[curbutton]->Flag_To_Redraw(); 1729 1730 curbutton--; 1731 if (curbutton < 0) { 1732 curbutton = numbuttons - 1; 1733 } 1734 1735 buttons[curbutton]->Turn_On(); 1736 buttons[curbutton]->Flag_To_Redraw(); 1737 } 1738 break; 1739 1740 case (KN_RIGHT): 1741 if (numbuttons > 1) { 1742 buttons[curbutton]->Turn_Off(); 1743 buttons[curbutton]->Flag_To_Redraw(); 1744 1745 curbutton++; 1746 if (curbutton > (numbuttons - 1) ) { 1747 curbutton = 0; 1748 } 1749 1750 buttons[curbutton]->Turn_On(); 1751 buttons[curbutton]->Flag_To_Redraw(); 1752 } 1753 break; 1754 1755 case (KN_RETURN): 1756 selection = curbutton + BUTTON_1; 1757 pressed = true; 1758 break; 1759 1760 /* 1761 ** Check 'input' to see if it's the 1st char of button text 1762 */ 1763 default: 1764 if (b1char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { 1765 selection = BUTTON_1; 1766 pressed = true; 1767 } else if (b2txt!=NULL && 1768 b2char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { 1769 selection = BUTTON_2; 1770 pressed = true; 1771 } else if (b3txt!=NULL && 1772 b3char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { 1773 selection = BUTTON_3; 1774 pressed = true; 1775 } 1776 break; 1777 } 1778 1779 if (pressed) { 1780 switch (selection) { 1781 case (BUTTON_1): 1782 retval = 1; 1783 process = false; 1784 break; 1785 1786 case (BUTTON_2): 1787 retval = 0; 1788 process = false; 1789 break; 1790 1791 case BUTTON_3: 1792 retval = 2; 1793 process = false; 1794 break; 1795 } 1796 1797 pressed = false; 1798 } 1799 } 1800 1801 } else { 1802 1803 Keyboard->Clear(); 1804 } 1805 1806 if (retval == (morebutton-1) && strlen(msg) > BUFFSIZE-1) { 1807 retval = BGMessageBox(msg + buffend + 1, btn1, btn2); 1808 } 1809 /* 1810 ** Restore the screen. 1811 */ 1812 Hide_Mouse(); 1813 /* 1814 ** Now set the palette, depending on if we're going to show the video or 1815 ** go back to the main menu. 1816 */ 1817 switch (retval) { 1818 case 0: 1819 // BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); 1820 // SeenPage.Clear(); 1821 //// CCPalette.Set(); 1822 // break; 1823 case 1: 1824 BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); 1825 SeenPage.Clear(); 1826 break; 1827 default: 1828 break; 1829 } 1830 Show_Mouse(); 1831 1832 GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); 1833 1834 return(retval); 1835 } 1836 1837 1838 /*********************************************************************************************** 1839 * Set_Scenario_Name -- Creates the INI scenario name string. * 1840 * * 1841 * This routine is used by the scenario loading and saving code. It generates the scenario * 1842 * INI root file name for the specified scenario parameters. * 1843 * * 1844 * INPUT: * 1845 * buf buffer to store filename in; must be long enough for root.ext * 1846 * scenario scenario number * 1847 * player player type for this game (GDI, NOD, multi-player, ...) * 1848 * dir directional parameter for this game (East/West) * 1849 * var variation of this game (Lose, A/B/C/D, etc) * 1850 * * 1851 * OUTPUT: none. * 1852 * * 1853 * WARNINGS: none. * 1854 * * 1855 * HISTORY: * 1856 * 05/28/1994 JLB : Created. * 1857 * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * 1858 *=============================================================================================*/ 1859 void ScenarioClass::Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) 1860 { 1861 Scenario = scenario; 1862 // ScenPlayer = player; 1863 // ScenDir = dir; 1864 // ScenVar = var; 1865 1866 char c_player; // character representing player type 1867 char c_dir; // character representing direction type 1868 char c_var; // character representing variation type 1869 ScenarioVarType i; 1870 char fname[_MAX_FNAME+_MAX_EXT]; 1871 1872 /* 1873 ** Set the player-type value. 1874 */ 1875 switch (player) { 1876 case SCEN_PLAYER_SPAIN: 1877 c_player = HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix; 1878 break; 1879 1880 case SCEN_PLAYER_GREECE: 1881 c_player = HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix; 1882 break; 1883 1884 case SCEN_PLAYER_USSR: 1885 c_player = HouseTypeClass::As_Reference(HOUSE_USSR).Prefix; 1886 break; 1887 1888 case SCEN_PLAYER_JP: 1889 c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; 1890 break; 1891 1892 /* 1893 ** Multi player scenario. 1894 */ 1895 default: 1896 c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; 1897 break; 1898 } 1899 1900 /* 1901 ** Set the directional character value. 1902 ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' 1903 */ 1904 switch (dir) { 1905 case SCEN_DIR_EAST: 1906 c_dir = 'E'; 1907 break; 1908 1909 case SCEN_DIR_WEST: 1910 c_dir = 'W'; 1911 break; 1912 1913 default: 1914 case SCEN_DIR_NONE: 1915 c_dir = Percent_Chance(50) ? 'W' : 'E'; 1916 break; 1917 } 1918 1919 /* 1920 ** Set the variation value. 1921 */ 1922 if (var == SCEN_VAR_NONE) { 1923 1924 /* 1925 ** Find which variations are available for this scenario 1926 */ 1927 for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { 1928 sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); 1929 if (!CCFileClass(fname).Is_Available()) { 1930 break; 1931 } 1932 } 1933 1934 if (i==SCEN_VAR_FIRST) { 1935 c_var = 'X'; // indicates an error 1936 } else { 1937 c_var = 'A' + Random_Pick(0, i-1); 1938 // ScenVar = (ScenarioVarType)i; 1939 } 1940 } else { 1941 switch (var) { 1942 case SCEN_VAR_A: 1943 c_var = 'A'; 1944 break; 1945 1946 case SCEN_VAR_B: 1947 c_var = 'B'; 1948 break; 1949 1950 case SCEN_VAR_C: 1951 c_var = 'C'; 1952 break; 1953 1954 case SCEN_VAR_D: 1955 c_var = 'D'; 1956 break; 1957 1958 default: 1959 c_var = 'L'; 1960 break; 1961 1962 } 1963 } 1964 1965 /* 1966 ** generate the filename 1967 */ 1968 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1969 //Mono_Printf("In set_scenario_name, scenario # = %d\n",scenario);Keyboard->Get();Keyboard->Get(); 1970 if (scenario < 100) { 1971 sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); 1972 } else { 1973 char first = (scenario / 36) + 'A'; 1974 char second = scenario % 36; 1975 1976 if (second < 10) { 1977 second += '0'; 1978 } else { 1979 second = (second - 10) + 'A'; 1980 } 1981 1982 sprintf(ScenarioName, "SC%c%c%c%c%c.INI", c_player, first, second, c_dir, c_var); 1983 } 1984 #else 1985 sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); 1986 #endif 1987 } 1988 1989 1990 void ScenarioClass::Set_Scenario_Name(char const * name) 1991 { 1992 if (name != NULL) { 1993 strncpy(ScenarioName, name, sizeof(ScenarioName)); 1994 ScenarioName[ARRAY_SIZE(ScenarioName)-1] = '\0'; 1995 1996 char buf[3]; 1997 memcpy(buf, &ScenarioName[3], 2); 1998 buf[2] = '\0'; 1999 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2000 if (buf[0] > '9' || buf[1] > '9') { 2001 char first = buf[0]; 2002 char second = buf[1]; 2003 if (first <= '9') { 2004 first -= '0'; 2005 } else { 2006 first -= 'A'; 2007 } 2008 2009 if (second <= '9') { 2010 second -= '0'; 2011 } else { 2012 second = (second - 'A') + 10; 2013 } 2014 2015 Scenario = (36 * first) + second; 2016 } else { 2017 Scenario = atoi(buf); 2018 } 2019 #else 2020 Scenario = atoi(buf); 2021 #endif 2022 } 2023 } 2024 2025 2026 2027 /*********************************************************************************************** 2028 * Read_Scenario_INI -- Read specified scenario INI file. * 2029 * * 2030 * Read in the scenario INI file. This routine only sets the game * 2031 * globals with that data that is explicitly defined in the INI file. * 2032 * The remaining necessary interpolated data is generated elsewhere. * 2033 * * 2034 * INPUT: * 2035 * root root filename for scenario file to read * 2036 * * 2037 * fresh true = should the current scenario be cleared? * 2038 * * 2039 * OUTPUT: bool; Was the scenario read successful? * 2040 * * 2041 * WARNINGS: none * 2042 * * 2043 * HISTORY: * 2044 * 10/07/1992 JLB : Created. V.Grippi added CS check 2/5/97 * 2045 *=============================================================================================*/ 2046 bool Read_Scenario_INI(char * fname, bool ) 2047 { 2048 // char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename 2049 2050 2051 ScenarioInit++; 2052 2053 Clear_Scenario(); 2054 #ifdef OBSOLETE 2055 /* 2056 ** If we are not dealing with scenario 1, or a multi player scenario 2057 ** then make sure the correct disk is in the drive. 2058 */ 2059 if (RequiredCD != -2) { 2060 RequiredCD = -1; 2061 } 2062 #endif 2063 2064 /* 2065 ** Only force a CD check if this is a single player game or if its 2066 ** a multiplayer game on an official scenario. If its non-official 2067 ** (a user scenario) then we dont care which CD is in because the 2068 ** scenario is stored locally on the hard drive. In this case, we 2069 ** have already verified its existance. ST 3/1/97 4:52PM. 2070 */ 2071 #ifdef FIXIT_VERSION_3 // Avoid CD check if official scenario was downloaded. 2072 if( ( Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial ) && _stricmp( Scen.ScenarioName, "download.tmp" ) ){ 2073 #else 2074 if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial){ 2075 #endif 2076 2077 /* 2078 ** If this is scenario 1 then it should be on all CDs unless its an ant scenario 2079 */ 2080 if (Scen.Scenario == 1 && Scen.ScenarioName[2] != 'A') { 2081 RequiredCD = -1; 2082 } else { 2083 // Mono_Printf("Read_SCen_INI scenario is: %s\n", Scen.ScenarioName); 2084 /* 2085 ** If this is a multiplayer scenario we need to find out if its a counterstrike 2086 ** scenario. If so then we need CD 2. The original multiplayer scenarios are on 2087 ** all CDs. 2088 */ 2089 if (Session.Type != GAME_NORMAL) { 2090 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2091 RequiredCD = -1; // default that any CD will do. 2092 // If it's a counterstrike mission, require the counterstrike CD, unless the 2093 // Aftermath CD is already in the drive, in which case, leave it there. 2094 // Note, this works because this section only tests for multiplayer scenarios. 2095 if (Is_Mission_Counterstrike(Scen.ScenarioName)) { 2096 RequiredCD = 2; 2097 if( Is_Aftermath_Installed() || Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) == 3 ) 2098 { 2099 RequiredCD = 3; 2100 } 2101 } 2102 if(Is_Mission_Aftermath(Scen.ScenarioName)) { 2103 RequiredCD = 3; 2104 } 2105 #else 2106 if (Scen.Scenario > 24) { 2107 RequiredCD = 2; 2108 } else { 2109 RequiredCD = -1; 2110 } 2111 #endif 2112 } else { 2113 2114 /* 2115 ** This is a solo game. If the scenario number is >= 20 or its an ant mission 2116 ** then we need the counterstrike CD (2) 2117 */ 2118 if (Scen.Scenario >= 20 || Scen.ScenarioName[2] == 'A') { 2119 RequiredCD = 2; 2120 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2121 if (Scen.Scenario >= 36 && Scen.ScenarioName[2] != 'A') { 2122 RequiredCD = 3; 2123 #ifdef BOGUSCD 2124 RequiredCD = -1; 2125 #endif 2126 } 2127 #endif 2128 } else { 2129 2130 /* 2131 ** This is a solo mission from the original Red Alert. Choose the Soviet or 2132 ** allied CD depending on the scenario name. 2133 */ 2134 if (Scen.ScenarioName[2] == 'U') { 2135 RequiredCD = 1; 2136 } else { 2137 if (Scen.ScenarioName[2] == 'G') { 2138 // Mono_Printf("We are setting REquiredCD to 0"); 2139 RequiredCD = 0; 2140 2141 } 2142 } 2143 } 2144 } 2145 } 2146 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2147 // If we're asking for a CD swap, check to see if we need to set the palette 2148 // to avoid a black screen. If this is a normal RA game, and the CD being 2149 // requested is an RA CD, then don't set the palette, leave the map screen up. 2150 2151 #ifdef FIXIT_VERSION_3 2152 int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); 2153 if( !( Using_DVD() && cd_index == 5 ) && cd_index != RequiredCD ) { 2154 #else 2155 if (Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != RequiredCD) { 2156 #endif 2157 if ((RequiredCD == 0 || RequiredCD == 1) && Session.Type == GAME_NORMAL) { 2158 SeenPage.Clear(); 2159 } 2160 GamePalette.Set(FADE_PALETTE_FAST, Call_Back); 2161 } 2162 #endif 2163 if (!Force_CD_Available(RequiredCD)) { 2164 Prog_End("Read_Scenario_INI Force_CD_Available failed", true); 2165 if (!RunningAsDLL) { //PG 2166 Emergency_Exit(EXIT_FAILURE); 2167 } 2168 } 2169 } else { 2170 /* 2171 ** This is a user scenario so any old CD will do. 2172 */ 2173 RequiredCD = -1; 2174 } 2175 2176 2177 /* 2178 ** Create scenario filename and read the file. 2179 */ 2180 // sprintf(fname, "%s.INI", root); 2181 CCINIClass ini; 2182 CCFileClass file(fname); 2183 // file.Cache(); 2184 2185 int result = ini.Load(file, true); 2186 if (result == 0) { 2187 // Mono_Printf("ini.Load failed"); 2188 GlyphX_Debug_Print("Failed to load scenario file"); 2189 GlyphX_Debug_Print(fname); 2190 return(false); 2191 } 2192 2193 GlyphX_Debug_Print("Loaded scenario file"); 2194 GlyphX_Debug_Print(file); 2195 2196 /* 2197 ** If the scenario digest is wrong then the return code will be a 2. 2198 */ 2199 if (result == 2) { 2200 // if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { 2201 /* 2202 ** Make a special exception so that multiplayer maps from 1 through 2203 ** 24 will not care if the message digest is in error. All other 2204 ** maps will abort the scenario load. 2205 */ 2206 if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) { 2207 #if (1) 2208 GlyphX_Debug_Print("Scenario digest is wrong"); 2209 #else 2210 GamePalette.Set(); 2211 WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); 2212 #endif 2213 #ifdef RELEASE_VERSION 2214 return(false); 2215 #endif 2216 } 2217 // } 2218 } 2219 2220 /* 2221 ** Reset the rules values to their initial settings. 2222 */ 2223 #ifdef FIXIT_NAME_OVERRIDE 2224 for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { 2225 if (NameOverride[index] != NULL) free((void*)NameOverride[index]); 2226 NameOverride[index] = NULL; 2227 NameIDOverride[index] = 0; 2228 } 2229 if (Session.Type == GAME_NORMAL) { 2230 Special.IsShadowGrow = false; 2231 } 2232 #endif 2233 2234 #ifdef FIXIT_ANTS 2235 Session.Messages.Reset(); 2236 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2237 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2238 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2239 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2240 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2241 // Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); 2242 WeaponTypeClass::As_Pointer(WEAPON_FLAMER)->Sound = VOC_NONE; 2243 InfantryTypeClass::As_Reference(INFANTRY_THIEF).IsDoubleOwned = false; 2244 InfantryTypeClass::As_Reference(INFANTRY_E4).IsDoubleOwned = false; 2245 InfantryTypeClass::As_Reference(INFANTRY_SPY).PrimaryWeapon = NULL; 2246 InfantryTypeClass::As_Reference(INFANTRY_SPY).SecondaryWeapon = NULL; 2247 InfantryTypeClass::As_Reference(INFANTRY_GENERAL).IsBomber = false; 2248 UnitTypeClass::As_Reference(UNIT_HARVESTER).IsExploding = false; 2249 UnitTypeClass::As_Reference(UNIT_ANT1).Level = -1; 2250 UnitTypeClass::As_Reference(UNIT_ANT2).Level = -1; 2251 UnitTypeClass::As_Reference(UNIT_ANT3).Level = -1; 2252 BuildingTypeClass::As_Reference(STRUCT_QUEEN).Level = -1; 2253 BuildingTypeClass::As_Reference(STRUCT_LARVA1).Level = -1; 2254 BuildingTypeClass::As_Reference(STRUCT_LARVA2).Level = -1; 2255 #endif 2256 2257 2258 Rule.General(RuleINI); 2259 Rule.Recharge(RuleINI); 2260 Rule.AI(RuleINI); 2261 Rule.Powerups(RuleINI); 2262 Rule.Land_Types(RuleINI); 2263 Rule.Themes(RuleINI); 2264 Rule.IQ(RuleINI); 2265 Rule.Objects(RuleINI); 2266 Rule.Difficulty(RuleINI); 2267 #ifdef FIXIT_CSII // checked - ajw 9/28/98 - Except does this _change_ any rules, or just add to them? - Just adds. 2268 Rule.General(AftermathINI); 2269 Rule.Recharge(AftermathINI); 2270 Rule.AI(AftermathINI); 2271 Rule.Powerups(AftermathINI); 2272 Rule.Land_Types(AftermathINI); 2273 Rule.Themes(AftermathINI); 2274 Rule.IQ(AftermathINI); 2275 Rule.Objects(AftermathINI); 2276 Rule.Difficulty(AftermathINI); 2277 #endif 2278 2279 /* 2280 ** For civilians, remove the graphics name override from the base rules (can still be overridden in scenario-specific INI). 2281 */ 2282 for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { 2283 InfantryTypeClass* itype = InfantryTypes.Ptr(iindex); 2284 if (itype->IsCivilian) { 2285 itype->GraphicName[0] = '\0'; 2286 } 2287 } 2288 2289 /* 2290 ** Override any rules values specified in this 2291 ** particular scenario file. 2292 */ 2293 Rule.General(ini); 2294 Rule.Recharge(ini); 2295 Rule.AI(ini); 2296 Rule.Powerups(ini); 2297 Rule.Land_Types(ini); 2298 Rule.Themes(ini); 2299 Rule.IQ(ini); 2300 Rule.Objects(ini); 2301 Rule.Difficulty(ini); 2302 2303 /* 2304 ** - Fix a legacy bug with England and France country bonuses. 2305 ** - Use ore growth and spread values from the special settings. 2306 */ 2307 if (Session.Type != GAME_NORMAL) { 2308 HouseTypeClass::As_Reference(HOUSE_ENGLAND).ArmorBias = fixed(9, 10); 2309 HouseTypeClass::As_Reference(HOUSE_FRANCE).ROFBias = fixed(9, 10); 2310 2311 Rule.IsTGrowth = Special.IsTGrowth; 2312 Rule.IsTSpread = Special.IsTSpread; 2313 } 2314 2315 /* 2316 ** Init the Scenario CRC value 2317 */ 2318 ScenarioCRC = 0; 2319 #ifdef TOFIX 2320 len = strlen(buffer); 2321 for (int i = 0; i < len; i++) { 2322 val = (unsigned char)buffer[i]; 2323 Add_CRC(&ScenarioCRC, (unsigned long)val); 2324 } 2325 #endif 2326 2327 /* 2328 ** Fetch the appropriate movie names from the INI file. 2329 */ 2330 const char * const BASIC = "Basic"; 2331 ini.Get_String(BASIC, "Name", "<none>", Scen.Description, sizeof(Scen.Description)); 2332 Scen.IntroMovie = ini.Get_VQType(BASIC, "Intro", Scen.IntroMovie); 2333 Scen.BriefMovie = ini.Get_VQType(BASIC, "Brief", Scen.BriefMovie); 2334 Scen.WinMovie = ini.Get_VQType(BASIC, "Win", Scen.WinMovie); 2335 Scen.WinMovie2 = ini.Get_VQType(BASIC, "Win2", Scen.WinMovie2); 2336 Scen.WinMovie3 = ini.Get_VQType(BASIC, "Win3", Scen.WinMovie3); 2337 Scen.WinMovie4 = ini.Get_VQType(BASIC, "Win4", Scen.WinMovie4); 2338 Scen.LoseMovie = ini.Get_VQType(BASIC, "Lose", Scen.LoseMovie); 2339 Scen.ActionMovie = ini.Get_VQType(BASIC, "Action", Scen.ActionMovie); 2340 Scen.IsToCarryOver = ini.Get_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); 2341 Scen.IsToInherit = ini.Get_Bool(BASIC, "ToInherit", Scen.IsToInherit); 2342 Scen.IsInheritTimer = ini.Get_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); 2343 Scen.IsEndOfGame = ini.Get_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); 2344 Scen.IsTanyaEvac = ini.Get_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); 2345 Scen.TransitTheme = ini.Get_ThemeType(BASIC, "Theme", THEME_NONE); 2346 NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); 2347 Scen.CarryOverPercent = ini.Get_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); 2348 Scen.CarryOverPercent = Saturate(Scen.CarryOverPercent, 1); 2349 Scen.CarryOverCap = ini.Get_Int(BASIC, "CarryOverCap", Scen.CarryOverCap); 2350 Scen.IsNoSpyPlane = ini.Get_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); 2351 Scen.IsSkipScore = ini.Get_Bool(BASIC, "SkipScore", Scen.IsSkipScore); 2352 Scen.IsOneTimeOnly = ini.Get_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); 2353 Scen.IsNoMapSel = ini.Get_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); 2354 Scen.IsTruckCrate = ini.Get_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); 2355 Scen.IsMoneyTiberium = ini.Get_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); 2356 Scen.Percent = ini.Get_Int(BASIC, "Percent", Scen.Percent); 2357 2358 /* 2359 ** Read in the specific information for each of the house types. This creates 2360 ** the houses of different types. 2361 */ 2362 HouseClass::Read_INI(ini); 2363 Call_Back(); 2364 2365 /* 2366 ** Read in the team-type data. The team types must be created before any 2367 ** triggers can be created. 2368 */ 2369 TeamTypeClass::Read_INI(ini); 2370 Call_Back(); 2371 2372 /* 2373 ** Read in the trigger data. The triggers must be created before any other 2374 ** objects can be initialized. 2375 */ 2376 TriggerTypeClass::Read_INI(ini); 2377 Call_Back(); 2378 2379 2380 /* 2381 ** Read in the map control values. This includes dimensions 2382 ** as well as theater information. 2383 */ 2384 Map.Read_INI(ini); 2385 Call_Back(); 2386 2387 /* 2388 ** Assign PlayerPtr by reading the player's house from the INI; 2389 ** Must be done before any TechnoClass objects are created. 2390 */ 2391 if (Session.Type == GAME_NORMAL) { 2392 PlayerPtr = HouseClass::As_Pointer(ini.Get_HousesType(BASIC, "Player", HOUSE_GREECE)); 2393 PlayerPtr->Assign_Handicap(Scen.Difficulty); 2394 int carryover; 2395 if (Scen.CarryOverCap != -1) { 2396 carryover = min(Scen.CarryOverMoney * Scen.CarryOverPercent, Scen.CarryOverCap); 2397 } else { 2398 carryover = Scen.CarryOverMoney * Scen.CarryOverPercent; 2399 } 2400 PlayerPtr->Credits += carryover; 2401 PlayerPtr->Control.InitialCredits += carryover; 2402 } else { 2403 //Call the new Assign_Houses function. ST - 8/8/2019 12:35PM 2404 //Assign_Houses(); 2405 GlyphX_Assign_Houses(); 2406 } 2407 PlayerPtr->IsHuman = true; 2408 PlayerPtr->IsPlayerControl = true; 2409 2410 // if (NewINIFormat < 2 || !ini.Is_Present("MapPack")) { 2411 // Map.Read_Binary(root, &ScenarioCRC); 2412 // } 2413 2414 2415 /* 2416 ** Read in and place the 3D terrain objects. 2417 */ 2418 TerrainClass::Read_INI(ini); 2419 Call_Back(); 2420 /* 2421 ** Read in and place the units (all sides). 2422 */ 2423 UnitClass::Read_INI(ini); 2424 Call_Back(); 2425 2426 AircraftClass::Read_INI(ini); 2427 Call_Back(); 2428 2429 VesselClass::Read_INI(ini); 2430 Call_Back(); 2431 2432 /* 2433 ** Read in and place the infantry units (all sides). 2434 */ 2435 InfantryClass::Read_INI(ini); 2436 Call_Back(); 2437 2438 2439 2440 /* 2441 ** Read in and place all the buildings on the map. 2442 */ 2443 BuildingClass::Read_INI(ini); 2444 Call_Back(); 2445 2446 /* 2447 ** Read in the AI's base information. 2448 */ 2449 Base.Read_INI(ini); 2450 Call_Back(); 2451 2452 /* 2453 ** Read in any normal overlay objects. 2454 */ 2455 OverlayClass::Read_INI(ini); 2456 Call_Back(); 2457 2458 /* 2459 ** Read in any smudge overlays. 2460 */ 2461 SmudgeClass::Read_INI(ini); 2462 Call_Back(); 2463 2464 /* Moved above ini.Get_TextBlock(...) so Xlat mission.ini could be loaded 2465 ** If the briefing text could not be found in the INI file, then search 2466 ** the mission.ini file. VG 10/17/96 2467 */ 2468 INIClass mini; 2469 mini.Load(CCFileClass("MISSION.INI")); 2470 mini.Get_TextBlock(fname, Scen.BriefingText, sizeof(Scen.BriefingText)); 2471 2472 /* 2473 ** Read in any briefing text. 2474 */ 2475 if (Scen.BriefingText[0] == '\0') { 2476 ini.Get_TextBlock("Briefing", Scen.BriefingText, sizeof(Scen.BriefingText)); 2477 } 2478 /* 2479 ** Perform a final overpass of the map. This handles smoothing of certain 2480 ** types of terrain (tiberium). 2481 */ 2482 Map.Overpass(); 2483 Call_Back(); 2484 2485 /* 2486 ** Special cases: 2487 ** Gold Rush multiplayer map cell 9033 - LAND_ROCK 2488 ** The Lake District multiplayer map cell 8482 - LAND_ROCK 2489 ** Blue Lakes multiplayer map cell 11937 - LAND_RIVER 2490 ** USSR mission 13 - fixup trigger action 2491 ** Allied mission 5B - fail mission if spy re-boards the transport at mission start 2492 ** Allied mission 8A - Germany is allied with Greece and itself 2493 ** Allied mission 9A - fail mission if tech center is destroyed before being spied 2494 ** Aftermath: Brother in Arms - have transports move to separate waypoints 2495 ** Aftermath: Let's Make a Steal - Make the pillboxes un-capturable 2496 ** Counterstrike: Soviet Soldier Volkov and Chitzkoi / Deus Ex Machina - Sniper burst fix 2497 */ 2498 if (_stricmp(Scen.ScenarioName, "scmh8ea.ini") == 0) { 2499 Map[(CELL)9033].Override_Land_Type(LAND_ROCK); 2500 } 2501 2502 if (_stricmp(Scen.ScenarioName, "scm93ea.ini") == 0) { 2503 Map[(CELL)8482].Override_Land_Type(LAND_ROCK); 2504 } 2505 2506 if (_stricmp(Scen.ScenarioName, "scmh4ea.ini") == 0) { 2507 Map[(CELL)11937].Override_Land_Type(LAND_RIVER); 2508 } 2509 2510 if (_stricmp(Scen.ScenarioName, "scu13ea.ini") == 0) { 2511 TriggerTypeClass* trigger = TriggerTypes.Ptr(11); 2512 trigger->Action1.Trigger.Set_Raw(39); 2513 } 2514 2515 if (_stricmp(Scen.ScenarioName, "scg05eb.ini") == 0) { 2516 TeamTypeClass* spy1_team = TeamTypeClass::From_Name("spy1"); 2517 assert(spy1_team != NULL); 2518 spy1_team->MissionList[spy1_team->MissionCount].Mission = TMISSION_SET_GLOBAL; 2519 spy1_team->MissionList[spy1_team->MissionCount].Data.Value = 16; 2520 spy1_team->MissionCount++; 2521 2522 TriggerTypeClass* los3_trigger = new TriggerTypeClass(); 2523 los3_trigger->IsPersistant = TriggerTypeClass::VOLATILE; 2524 los3_trigger->House = HOUSE_GREECE; 2525 los3_trigger->EventControl = MULTI_AND; 2526 los3_trigger->ActionControl = MULTI_AND; 2527 los3_trigger->Event1.Event = TEVENT_GLOBAL_SET; 2528 los3_trigger->Event1.Data.Value = 16; 2529 los3_trigger->Event2.Event = TEVENT_ALL_DESTROYED; 2530 los3_trigger->Event2.Data.House = HOUSE_GREECE; 2531 los3_trigger->Action1.Action = TACTION_LOSE; 2532 los3_trigger->Action1.Data.Value = -255; 2533 los3_trigger->Action2.Action = TACTION_NONE; 2534 los3_trigger->Action2.Data.Value = -1; 2535 2536 TriggerTypeClass* frc1_trigger = TriggerTypeClass::From_Name("frc1"); 2537 assert(frc1_trigger != NULL); 2538 frc1_trigger->Action1.Trigger = los3_trigger; 2539 } 2540 2541 if (_stricmp(Scen.ScenarioName, "scg08ea.ini") == 0) { 2542 for (int house = HOUSE_FIRST; house < HOUSE_COUNT; ++house) { 2543 HouseClass* ptr = Houses.Ptr(house); 2544 if (ptr != NULL && ptr->IsActive) { 2545 if (ptr->Class->House == HOUSE_GREECE || ptr->Class->House == HOUSE_GERMANY) { 2546 ptr->Make_Ally(HOUSE_GERMANY); 2547 } 2548 } 2549 } 2550 } 2551 2552 if (_stricmp(Scen.ScenarioName, "scg09ea.ini") == 0) { 2553 TriggerTypeClass* spyd_trigger = TriggerTypeClass::From_Name("Spyd"); 2554 assert(spyd_trigger != NULL); 2555 2556 TriggerTypeClass* kos_trigger = new TriggerTypeClass(); 2557 kos_trigger->IsPersistant = spyd_trigger->IsPersistant; 2558 kos_trigger->House = spyd_trigger->House; 2559 kos_trigger->EventControl = MULTI_LINKED; 2560 kos_trigger->ActionControl = MULTI_ONLY; 2561 kos_trigger->Event1.Event = spyd_trigger->Event1.Event; 2562 kos_trigger->Event1.Data = spyd_trigger->Event1.Data; 2563 kos_trigger->Event2.Event = TEVENT_DESTROYED; 2564 kos_trigger->Event2.Data.Value = 0; 2565 kos_trigger->Action1.Action = TACTION_SET_GLOBAL; 2566 kos_trigger->Action1.Data.Value = 22; 2567 kos_trigger->Action2.Action = TACTION_SET_GLOBAL; 2568 kos_trigger->Action2.Data.Value = 23; 2569 2570 spyd_trigger->Event1.Event = TEVENT_GLOBAL_SET; 2571 spyd_trigger->Event1.Data.Value = 22; 2572 2573 for (int index = 0; index < Buildings.Count(); index++) { 2574 BuildingClass* building = Buildings.Ptr(index); 2575 if (building->Trigger.Is_Valid() && (building->Trigger->Class == spyd_trigger)) { 2576 building->Attach_Trigger(Find_Or_Make(kos_trigger)); 2577 } 2578 } 2579 2580 TriggerTypeClass* los3_trigger = new TriggerTypeClass(); 2581 los3_trigger->IsPersistant = spyd_trigger->IsPersistant; 2582 los3_trigger->House = spyd_trigger->House; 2583 los3_trigger->EventControl = MULTI_AND; 2584 los3_trigger->ActionControl = MULTI_AND; 2585 los3_trigger->Event1.Event = TEVENT_GLOBAL_SET; 2586 los3_trigger->Event1.Data.Value = 23; 2587 los3_trigger->Event2.Event = TEVENT_GLOBAL_CLEAR; 2588 los3_trigger->Event2.Data.Value = 22; 2589 los3_trigger->Action1.Action = TACTION_LOSE; 2590 los3_trigger->Action1.Data.Value = -255; 2591 los3_trigger->Action2.Action = TACTION_TEXT_TRIGGER; 2592 los3_trigger->Action2.Data.Value = 54; 2593 } 2594 2595 if (_stricmp(Scen.ScenarioName, "scu46ea.ini") == 0) { 2596 Scen.Waypoint[20] = 9915; 2597 Scen.Waypoint[21] = 9919; 2598 Map[Scen.Waypoint[20]].IsWaypoint = 1; 2599 Map[Scen.Waypoint[21]].IsWaypoint = 1; 2600 2601 TeamTypeClass* rnf1_team = TeamTypeClass::From_Name("rnf1"); 2602 assert(rnf1_team != NULL); 2603 rnf1_team->MissionList[0].Data.Value = 20; 2604 2605 TeamTypeClass* rnf2_team = TeamTypeClass::From_Name("rnf2"); 2606 assert(rnf2_team != NULL); 2607 rnf2_team->MissionList[0].Data.Value = 21; 2608 } 2609 2610 if (_stricmp(Scen.ScenarioName, "scu42ea.ini") == 0) { 2611 BuildingTypeClass::As_Reference(STRUCT_PILLBOX).IsCaptureable = false; 2612 } 2613 2614 if ((_stricmp(Scen.ScenarioName, "scu35ea.ini") == 0) || (_stricmp(Scen.ScenarioName, "scu47ea.ini") == 0)) { 2615 WeaponTypeClass::As_Pointer(Weapon_From_Name("Sniper"))->Burst = 2; 2616 } 2617 2618 /* 2619 ** Multi-player last-minute fixups: 2620 ** - If computer players are disabled, remove all computer-owned houses 2621 ** - If bases are disabled, create the scenario dynamically 2622 ** - Remove any flag spot overlays lying around 2623 ** - If capture-the-flag is enabled, assign flags to cells. 2624 */ 2625 if (Session.Type != GAME_NORMAL /*|| Scen.ScenPlayer == SCEN_PLAYER_2PLAYER || Scen.ScenPlayer == SCEN_PLAYER_MPLAYER*/) { 2626 2627 /* 2628 ** If Ghosts are disabled and we're not editing, remove computer players 2629 ** (Must be done after all objects are read in from the INI) 2630 */ 2631 if ( (Session.Options.AIPlayers + Session.Players.Count() < Rule.MaxPlayers) && !Debug_Map) { 2632 Remove_AI_Players(); 2633 } 2634 2635 /* 2636 ** Units must be created for each house. If bases are ON, this routine 2637 ** will create an MCV along with the units; otherwise, it will just create 2638 ** a whole bunch of units. Session.Options.UnitCount is the total # of units 2639 ** to create. 2640 */ 2641 if (!Debug_Map) { 2642 int save_init = ScenarioInit; // turn ScenarioInit off 2643 ScenarioInit = 0; 2644 Create_Units(ini.Get_Bool("Basic", "Official", false)); 2645 ScenarioInit = save_init; // turn ScenarioInit back on 2646 } 2647 2648 /* 2649 ** Place crates if random crates are enabled for 2650 ** this scenario. 2651 */ 2652 if (Session.Options.Goodies) { 2653 int count = max(Rule.CrateMinimum, Session.NumPlayers); 2654 count = min(count, Rule.CrateMaximum); 2655 for (int index = 0; index < count; index++) { 2656 Map.Place_Random_Crate(); 2657 } 2658 } 2659 } 2660 2661 Call_Back(); 2662 2663 /* 2664 ** Return with flag saying that the scenario file was read. 2665 */ 2666 #ifdef FIXIT_CSII // checked - ajw 9/28/98 - Added runtime check. 2667 if( Is_Aftermath_Installed() ) 2668 { 2669 if (Session.Type == GAME_SKIRMISH || Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2670 bAftermathMultiplayer = NewUnitsEnabled = OverrideNewUnitsEnabled; 2671 } 2672 } 2673 #endif 2674 ScenarioInit--; 2675 return(true); 2676 } 2677 2678 2679 /*********************************************************************************************** 2680 * Write_Scenario_INI -- Write the scenario INI file. * 2681 * * 2682 * INPUT: * 2683 * root root filename for the scenario * 2684 * * 2685 * OUTPUT: * 2686 * none. * 2687 * * 2688 * WARNINGS: * 2689 * none. * 2690 * * 2691 * HISTORY: * 2692 * 10/07/1992 JLB : Created. * 2693 * 05/11/1995 JLB : Updates movie data. * 2694 *=============================================================================================*/ 2695 void Write_Scenario_INI(char * fname) 2696 { 2697 #ifndef CHEAT_KEYS 2698 fname = fname; 2699 #else 2700 // CCFileClass file(fname); 2701 2702 CCINIClass ini; 2703 2704 /* 2705 ** Preload the old scenario if it is present because there may 2706 ** be some fields in the INI that are processed but not written 2707 ** out. Preloading the scenario will preserve these manually 2708 ** maintained entries. 2709 */ 2710 if (CCFileClass(fname).Is_Available()) { 2711 ini.Load(CCFileClass(fname), true); 2712 } 2713 2714 static char const * const BASIC = "Basic"; 2715 ini.Clear(BASIC); 2716 ini.Put_String(BASIC, "Name", Scen.Description); 2717 ini.Put_VQType(BASIC, "Intro", Scen.IntroMovie); 2718 ini.Put_VQType(BASIC, "Brief", Scen.BriefMovie); 2719 ini.Put_VQType(BASIC, "Win", Scen.WinMovie); 2720 ini.Put_VQType(BASIC, "Lose", Scen.LoseMovie); 2721 ini.Put_VQType(BASIC, "Action", Scen.ActionMovie); 2722 ini.Put_HousesType(BASIC, "Player", PlayerPtr->Class->House); 2723 ini.Put_ThemeType(BASIC, "Theme", Scen.TransitTheme); 2724 ini.Put_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); 2725 ini.Put_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); 2726 ini.Put_Bool(BASIC, "ToInherit", Scen.IsToInherit); 2727 ini.Put_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); 2728 ini.Put_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); 2729 ini.Put_Int(BASIC, "NewINIFormat", 3); 2730 ini.Put_Int(BASIC, "CarryOverCap", Scen.CarryOverCap/100); 2731 ini.Put_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); 2732 ini.Put_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); 2733 ini.Put_Bool(BASIC, "SkipScore", Scen.IsSkipScore); 2734 ini.Put_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); 2735 ini.Put_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); 2736 ini.Put_Bool(BASIC, "Official", true); 2737 ini.Put_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); 2738 ini.Put_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); 2739 ini.Put_Int(BASIC, "Percent", Scen.Percent); 2740 2741 HouseClass::Write_INI(ini); 2742 TeamTypeClass::Write_INI(ini); 2743 TriggerTypeClass::Write_INI(ini); 2744 Map.Write_INI(ini); 2745 TerrainClass::Write_INI(ini); 2746 UnitClass::Write_INI(ini); 2747 VesselClass::Write_INI(ini); 2748 InfantryClass::Write_INI(ini); 2749 BuildingClass::Write_INI(ini); 2750 Base.Write_INI(ini); 2751 OverlayClass::Write_INI(ini); 2752 SmudgeClass::Write_INI(ini); 2753 2754 if (strlen(Scen.BriefingText)) { 2755 ini.Put_TextBlock("Briefing", Scen.BriefingText); 2756 } 2757 // sprintf(fname, "%s.INI", root); 2758 RawFileClass rawfile(fname); 2759 ini.Save(rawfile, true); 2760 #endif 2761 } 2762 2763 2764 /*********************************************************************************************** 2765 * Assign_Houses -- Assigns multiplayer houses to various players * 2766 * * 2767 * This routine assigns all players to a multiplayer house slot; it forms network connections * 2768 * to each player. The Connection ID used is the value for that player's HousesType. * 2769 * * 2770 * PlayerPtr is also set here. * 2771 * * 2772 * INPUT: * 2773 * none. * 2774 * * 2775 * OUTPUT: * 2776 * none. * 2777 * * 2778 * WARNINGS: * 2779 * This routine assumes the 'Players' vector has been properly filled in with players' * 2780 * names, addresses, color, etc. * 2781 * Also, it's assumed that the HouseClass's have all been created & initialized. * 2782 * * 2783 * HISTORY: * 2784 * 06/09/1995 BRR : Created. * 2785 * 07/14/1995 JLB : Records name of player in house structure. * 2786 *=============================================================================================*/ 2787 void Assign_Houses(void) 2788 { 2789 int assigned[MAX_PLAYERS]; 2790 int color_used[8]; 2791 int i,j; 2792 HousesType house; 2793 HouseClass * housep; 2794 int lowest_color; 2795 int index; 2796 HousesType pref_house; 2797 int color; 2798 2799 //------------------------------------------------------------------------ 2800 // Initialize 2801 //------------------------------------------------------------------------ 2802 for (i = 0; i < MAX_PLAYERS; i++) { 2803 assigned[i] = 0; 2804 color_used[i] = 0; 2805 } 2806 2807 // debugprint( "Assign_Houses()\n" ); 2808 //------------------------------------------------------------------------ 2809 // Assign each player in 'Players' to a multiplayer house. Players will 2810 // be sorted by their chosen color value (this value must be unique among 2811 // all the players). 2812 //------------------------------------------------------------------------ 2813 for (i = 0; i < Session.Players.Count(); i++) { 2814 2815 //..................................................................... 2816 // Find the player with the lowest color index 2817 //..................................................................... 2818 index = 0; 2819 lowest_color = 255; 2820 for (j = 0; j < Session.Players.Count(); j++) { 2821 //.................................................................. 2822 // If we've already assigned this house, skip it. 2823 //.................................................................. 2824 if (assigned[j]) { 2825 continue; 2826 } 2827 if (Session.Players[j]->Player.Color < lowest_color) { 2828 lowest_color = Session.Players[j]->Player.Color; 2829 index = j; 2830 } 2831 } 2832 2833 //..................................................................... 2834 // Mark this player as having been assigned. 2835 //..................................................................... 2836 assigned[index] = 1; 2837 color_used[Session.Players[index]->Player.Color] = 1; 2838 2839 //..................................................................... 2840 // Assign the lowest-color'd player to the next available slot in the 2841 // HouseClass array. 2842 //..................................................................... 2843 house = (HousesType)(i + HOUSE_MULTI1); 2844 housep = HouseClass::As_Pointer(house); 2845 memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); 2846 strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); 2847 #ifdef WOLAPI_INTEGRATION 2848 // Make another copy of name, permanent throughout entire game. 2849 strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); 2850 #endif 2851 housep->IsHuman = true; 2852 housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), 2853 Session.Players[index]->Player.House, Session.Options.Credits); 2854 if (index == 0) { 2855 PlayerPtr = housep; 2856 } 2857 /* 2858 ** Convert the build level into an actual tech level to assign to the house. 2859 ** There isn't a one-to-one correspondence. 2860 */ 2861 housep->Control.TechLevel = _build_tech[BuildLevel]; 2862 2863 housep->Assign_Handicap(Scen.Difficulty); 2864 2865 //..................................................................... 2866 // Record where we placed this player 2867 //..................................................................... 2868 Session.Players[index]->Player.ID = house; 2869 2870 // debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); 2871 } 2872 2873 //------------------------------------------------------------------------ 2874 // Now assign computer players to the remaining houses. 2875 //------------------------------------------------------------------------ 2876 for (i = Session.Players.Count(); i < Session.Players.Count() + Session.Options.AIPlayers; i++) { 2877 house = (HousesType)(i + HOUSE_MULTI1); 2878 housep = HouseClass::As_Pointer(house); 2879 if (Percent_Chance(50)) { 2880 pref_house = HOUSE_GREECE; 2881 } else { 2882 pref_house = HOUSE_USSR; 2883 } 2884 2885 //..................................................................... 2886 // Pick a color for this house; keep looping until we find one. 2887 //..................................................................... 2888 while (1) { 2889 color = Random_Pick(0, 7); 2890 if (color_used[color] == false) { 2891 break; 2892 } 2893 } 2894 color_used[color] = true; 2895 2896 //..................................................................... 2897 // Set up the house 2898 //..................................................................... 2899 // housep->Control.MaxUnit = 80; 2900 // housep->Control.MaxInfantry = 60; 2901 // housep->Control.MaxBuilding = 60; 2902 // housep->Control.MaxVessel = 60; 2903 housep->IsHuman = false; 2904 housep->IsStarted = true; 2905 2906 strcpy(housep->IniName, Text_String(TXT_COMPUTER)); 2907 2908 if (Session.Type != GAME_NORMAL) { 2909 housep->IQ = Rule.MaxIQ; 2910 } 2911 2912 housep->Init_Data((PlayerColorType)color, pref_house, Session.Options.Credits); 2913 housep->Control.TechLevel = _build_tech[BuildLevel]; 2914 // housep->Control.TechLevel = BuildLevel; 2915 2916 DiffType difficulty = Scen.CDifficulty; 2917 2918 if (Session.Players.Count() > 1 && Rule.IsCompEasyBonus && difficulty > DIFF_EASY) { 2919 difficulty = (DiffType)(difficulty - 1); 2920 } 2921 housep->Assign_Handicap(difficulty); 2922 } 2923 2924 for (i = Session.Players.Count()+Session.Options.AIPlayers; i < Rule.MaxPlayers; i++) { 2925 house = (HousesType)(i + HOUSE_MULTI1); 2926 housep = HouseClass::As_Pointer(house); 2927 if (housep != NULL) { 2928 housep->IsDefeated = true; 2929 } 2930 } 2931 } 2932 2933 2934 /*********************************************************************************************** 2935 * Remove_AI_Players -- Removes the computer AI houses & their units * 2936 * * 2937 * INPUT: * 2938 * none. * 2939 * * 2940 * OUTPUT: * 2941 * none. * 2942 * * 2943 * WARNINGS: * 2944 * none. * 2945 * * 2946 * HISTORY: * 2947 * 06/09/1995 BRR : Created. * 2948 *=============================================================================================*/ 2949 static void Remove_AI_Players(void) 2950 { 2951 int i; 2952 int aicount = 0; 2953 HousesType house; 2954 HouseClass * housep; 2955 2956 #if (0) 2957 for (i = 0; i < MAX_PLAYERS; i++) { 2958 house = (HousesType)(i + (int)HOUSE_MULTI1); 2959 housep = HouseClass::As_Pointer (house); 2960 if (housep->IsHuman == false) { 2961 aicount++; 2962 if(aicount > Session.Options.AIPlayers) { 2963 housep->Clobber_All(); 2964 } 2965 } 2966 } 2967 #else 2968 /* 2969 ** AI players are set up like human players now. ST - 8/13/2019 1:32PM 2970 */ 2971 for (i = 0; i < MAX_PLAYERS; i++) { 2972 if (i >= Session.Players.Count()) { 2973 house = (HousesType)(i + (int)HOUSE_MULTI1); 2974 housep = HouseClass::As_Pointer (house); 2975 if (housep->IsHuman == false) { 2976 housep->Clobber_All(); 2977 } 2978 } 2979 } 2980 2981 #endif 2982 } 2983 2984 2985 #define USE_GLYPHX_START_LOCATIONS 1 2986 2987 2988 /*********************************************************************************************** 2989 * Create_Units -- Creates infantry & units, for non-base multiplayer * 2990 * * 2991 * This routine uses data tables to determine which units to create for either * 2992 * a GDI or NOD house, and how many of each. * 2993 * * 2994 * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * 2995 * as that house's "home" cell. * 2996 * * 2997 * INPUT: official -- Directs the placement logic to use the full set of waypoints rather * 2998 * than biasing toward the first four. * 2999 * * 3000 * OUTPUT: * 3001 * none. * 3002 * * 3003 * WARNINGS: * 3004 * none. * 3005 * * 3006 * HISTORY: * 3007 * 06/09/1995 BRR : Created. * 3008 *=============================================================================================*/ 3009 static int ReserveInfantryIndex = 0; 3010 static void Reserve_Infantry() 3011 { 3012 if (Infantry.Count() == Infantry.Length()) { 3013 delete Infantry.Ptr(ReserveInfantryIndex); 3014 ReserveInfantryIndex = (ReserveInfantryIndex + 1) % Infantry.Length(); 3015 } 3016 } 3017 3018 static int ReserveUnitIndex = 0; 3019 static void Reserve_Unit() 3020 { 3021 if (Units.Count() == Units.Length()) { 3022 delete Units.Ptr(ReserveUnitIndex); 3023 ReserveUnitIndex = (ReserveUnitIndex + 1) % Units.Length(); 3024 } 3025 } 3026 3027 static void Create_Units(bool official) 3028 { 3029 static struct { 3030 int MinLevel; 3031 UnitType AllyType[2]; 3032 UnitType SovietType[2]; 3033 } utable[] = { 3034 {4, {UNIT_MTANK2, UNIT_LTANK}, {UNIT_MTANK, UNIT_NONE}}, 3035 {5, {UNIT_APC, UNIT_NONE}, {UNIT_V2_LAUNCHER, UNIT_NONE}}, 3036 {8, {UNIT_ARTY, UNIT_JEEP}, {UNIT_MTANK, UNIT_NONE}}, 3037 {10, {UNIT_MTANK2, UNIT_MTANK2}, {UNIT_HTANK, UNIT_NONE}} 3038 }; 3039 static int num_units[ARRAY_SIZE(utable)]; // # of each type of unit to create 3040 int tot_units; // total # units to create 3041 3042 static struct { 3043 int MinLevel; 3044 int AllyCount; 3045 InfantryType AllyType; 3046 int SovietCount; 3047 InfantryType SovietType; 3048 } itable[] = { 3049 {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, 3050 {2, 1,INFANTRY_E3, 1,INFANTRY_E2}, 3051 {4, 1,INFANTRY_E3, 1,INFANTRY_E4}, 3052 3053 // removed because of bug B478 (inappropriate infantry given in a bases off scenario). 3054 // {5, 1,INFANTRY_RENOVATOR, 1,INFANTRY_RENOVATOR}, 3055 // {6, 1,INFANTRY_SPY, 1,INFANTRY_DOG}, 3056 // {10, 1,INFANTRY_THIEF, 1,INFANTRY_DOG}, 3057 // {12, 1,INFANTRY_MEDIC, 2,INFANTRY_DOG} 3058 }; 3059 static int num_infantry[ARRAY_SIZE(itable)];// # of each type of infantry to create 3060 int tot_infantry; // total # infantry to create 3061 3062 3063 CELL centroid; // centroid of this house's stuff 3064 CELL centerpt; // centroid for a category of objects, as a CELL 3065 3066 int u_limit=0; // last allowable index of units for this BuildLevel 3067 int i_limit=0; // last allowable index of infantry for this BuildLevel 3068 TechnoClass * obj; // newly-created object 3069 int i,j,k; // loop counters 3070 int scaleval; // value to scale # units or infantry 3071 3072 ReserveInfantryIndex = ReserveUnitIndex = 0; 3073 3074 /* 3075 ** For the current BuildLevel, find the max allowable index into the tables 3076 */ 3077 for (i = 0; i < ARRAY_SIZE(utable); i++) { 3078 if (PlayerPtr->Control.TechLevel >= utable[i].MinLevel) { 3079 u_limit = i+1; 3080 } 3081 } 3082 for (i = 0; i < ARRAY_SIZE(itable); i++) { 3083 if (PlayerPtr->Control.TechLevel >= itable[i].MinLevel) { 3084 i_limit = i+1; 3085 } 3086 } 3087 3088 /* 3089 ** Compute how many of each buildable category to create 3090 */ 3091 /* 3092 ** Compute allowed # units 3093 */ 3094 tot_units = (Session.Options.UnitCount * 2) / 3; 3095 if (u_limit == 0) tot_units = 0; 3096 3097 /* 3098 ** Init # of each category to 0 3099 */ 3100 for (i = 0; i < u_limit; i++) { 3101 num_units[i] = 0; 3102 } 3103 3104 /* 3105 ** Increment # of each category, until we've used up all units 3106 */ 3107 j = 0; 3108 for (i = 0; i < tot_units; i++) { 3109 num_units[j]++; 3110 j++; 3111 if (j >= u_limit) { 3112 j = 0; 3113 } 3114 } 3115 3116 /* 3117 ** Compute allowed # infantry 3118 */ 3119 tot_infantry = Session.Options.UnitCount - tot_units; 3120 3121 /* 3122 ** Init # of each category to 0 3123 */ 3124 for (i = 0; i < i_limit; i++) { 3125 num_infantry[i] = 0; 3126 } 3127 3128 /* 3129 ** Increment # of each category, until we've used up all infantry 3130 */ 3131 j = 0; 3132 for (i = 0; i < tot_infantry; i++) { 3133 num_infantry[j]++; 3134 j++; 3135 if (j >= i_limit) { 3136 j = 0; 3137 } 3138 } 3139 3140 /* 3141 ** Build a list of the valid waypoints. This normally shouldn't be 3142 ** necessary because the scenario level designer should have assigned 3143 ** valid locations to the first N waypoints, but just in case, this 3144 ** loop verifies that. 3145 */ 3146 3147 const unsigned int MAX_STORED_WAYPOINTS = 26; 3148 3149 bool taken[MAX_STORED_WAYPOINTS]; 3150 CELL waypts[MAX_STORED_WAYPOINTS]; 3151 assert(Rule.MaxPlayers < ARRAY_SIZE(waypts)); 3152 int num_waypts = 0; 3153 3154 /* 3155 ** Calculate the number of waypoints (as a minimum) that will be lifted from the 3156 ** mission file. Bias this number so that only the first 4 waypoints are used 3157 ** if there are 4 or fewer players. Unofficial maps will pick from all the 3158 ** available waypoints. 3159 */ 3160 #ifndef USE_GLYPHX_START_LOCATIONS 3161 int look_for = max(4, Session.Players.Count()+Session.Options.AIPlayers); 3162 if (!official) { 3163 look_for = 8; 3164 } 3165 #else 3166 /* 3167 ** We allow the users to choose from all available start positions, even on official maps. ST - 1/15/2020 9:19AM 3168 */ 3169 int look_for = Session.Players.Count(); 3170 #endif 3171 3172 for (int waycount = 0; waycount < 26; waycount++) { 3173 // for (int waycount = 0; waycount < max(4, Session.Players.Count()+Session.Options.AIPlayers); waycount++) { 3174 if (Scen.Waypoint[waycount] != -1) { 3175 waypts[num_waypts] = Scen.Waypoint[waycount]; 3176 taken[num_waypts] = false; 3177 num_waypts++; 3178 3179 if (num_waypts >= MAX_STORED_WAYPOINTS) 3180 { 3181 break; 3182 } 3183 } 3184 } 3185 3186 /* 3187 ** If there are insufficient waypoints to account for all players, then randomly assign 3188 ** starting points until there is enough. 3189 */ 3190 int deficiency = look_for - num_waypts; 3191 // int deficiency = (Session.Players.Count() + Session.Options.AIPlayers) - num_waypts; 3192 if (deficiency > 0) { 3193 for (int index = 0; index < deficiency; index++) { 3194 CELL trycell = XY_Cell(Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1), Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1)); 3195 3196 trycell = Map.Nearby_Location(trycell, SPEED_TRACK); 3197 waypts[num_waypts] = trycell; 3198 taken[num_waypts] = false; 3199 num_waypts++; 3200 } 3201 } 3202 3203 /* 3204 ** Loop through all houses. Computer-controlled houses, with Session.Options.Bases 3205 ** ON, are treated as though bases are OFF (since we have no base-building 3206 ** AI logic.) 3207 */ 3208 int numtaken = 0; 3209 for (HousesType house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { 3210 3211 /* 3212 ** Get a pointer to this house; if there is none, go to the next house 3213 */ 3214 HouseClass * hptr = HouseClass::As_Pointer(house); 3215 if (hptr == NULL) { 3216 continue; 3217 } 3218 3219 /* 3220 ** Pick the starting location for this house. The first house just picks 3221 ** one of the valid locations at random. The other houses pick the furthest 3222 ** wapoint from the existing houses. 3223 */ 3224 if (!UseGlyphXStartLocations) { 3225 3226 if (numtaken == 0) { 3227 int pick = Random_Pick(0, num_waypts-1); 3228 centroid = waypts[pick]; 3229 hptr->StartLocationOverride = pick; 3230 taken[pick] = true; 3231 numtaken++; 3232 } else { 3233 3234 /* 3235 ** Set all waypoints to have a score of zero in preparation for giving 3236 ** a distance score to all waypoints. 3237 */ 3238 int score[26]; 3239 memset(score, '\0', sizeof(score)); 3240 3241 /* 3242 ** Scan through all waypoints and give a score as a value of the sum 3243 ** of the distances from this waypoint to all taken waypoints. 3244 */ 3245 for (int index = 0; index < num_waypts; index++) { 3246 3247 /* 3248 ** If this waypoint has not already been taken, then accumulate the 3249 ** sum of the distance between this waypoint and all other taken 3250 ** waypoints. 3251 */ 3252 if (!taken[index]) { 3253 for (int trypoint = 0; trypoint < num_waypts; trypoint++) { 3254 3255 if (taken[trypoint]) { 3256 score[index] += Distance(Cell_Coord(waypts[index]), Cell_Coord(waypts[trypoint])); 3257 } 3258 } 3259 } 3260 } 3261 3262 /* 3263 ** Now find the waypoint with the largest score. This waypoint is the one 3264 ** that is furthest from all other taken waypoints. 3265 */ 3266 int best = 0; 3267 int bestvalue = 0; 3268 for (int searchindex = 0; searchindex < num_waypts; searchindex++) { 3269 if (score[searchindex] > bestvalue || bestvalue == 0) { 3270 bestvalue = score[searchindex]; 3271 best = searchindex; 3272 } 3273 } 3274 3275 /* 3276 ** Assign this best position to the house. 3277 */ 3278 centroid = waypts[best]; 3279 hptr->StartLocationOverride = best; 3280 taken[best] = true; 3281 numtaken++; 3282 } 3283 } else { 3284 3285 /* 3286 ** New code that respects the start locations passed in from GlyphX. 3287 ** 3288 ** ST - 1/8/2020 3:39PM 3289 */ 3290 centroid = waypts[hptr->StartLocationOverride]; 3291 } 3292 3293 /* 3294 ** Assign the center of this house to the waypoint location. 3295 */ 3296 hptr->Center = Cell_Coord(centroid); 3297 3298 /* 3299 ** If Bases are ON, human & computer houses are treated differently 3300 */ 3301 if (Session.Options.Bases) { 3302 3303 /* 3304 ** - For a human-controlled house: 3305 ** - Set 'scaleval' to 1 3306 ** - Create an MCV 3307 ** - Attach a flag to it for capture-the-flag mode 3308 */ 3309 scaleval = 1; 3310 Reserve_Unit(); 3311 obj = new UnitClass (UNIT_MCV, house); 3312 if (!obj->Unlimbo(Cell_Coord(centroid), DIR_N)) { 3313 if (!Scan_Place_Object(obj, centroid)) { 3314 delete obj; 3315 obj = NULL; 3316 } 3317 } 3318 if (obj != NULL) { 3319 hptr->FlagHome = 0; 3320 hptr->FlagLocation = 0; 3321 if (Special.IsCaptureTheFlag) { 3322 hptr->Flag_Attach((UnitClass *)obj, true); 3323 } 3324 } 3325 } else { 3326 3327 /* 3328 ** If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for 3329 ** capture-the-flag mode. 3330 */ 3331 scaleval = 1; 3332 #ifdef TOFIX 3333 if (Special.IsCaptureTheFlag) { 3334 obj = new UnitClass (UNIT_TRUCK, house); 3335 obj->Unlimbo(Cell_Coord(centroid), DIR_N); 3336 hptr->FlagHome = 0; // turn house's flag off 3337 hptr->FlagLocation = 0; 3338 } 3339 #endif 3340 } 3341 3342 /* 3343 ** Create units for this house 3344 */ 3345 for (i = 0; i < u_limit; i++) { 3346 3347 /* 3348 ** Find the center point for this category. 3349 */ 3350 centerpt = Clip_Scatter(centroid, 4); 3351 3352 /* 3353 ** Place objects; loop through all unit in this category 3354 */ 3355 for (j = 0; j < num_units[i] * scaleval; j++) { 3356 3357 /* 3358 ** Create an Ally unit 3359 */ 3360 if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { 3361 for (k = 0; k < 2; k++) if(utable[i].AllyType[k] != UNIT_NONE) { 3362 Reserve_Unit(); 3363 obj = new UnitClass (utable[i].AllyType[k], house); 3364 if (!Scan_Place_Object(obj, centerpt)) { 3365 delete obj; 3366 } else { 3367 if (!hptr->IsHuman) { 3368 obj->Set_Mission(MISSION_GUARD_AREA); 3369 } else { 3370 obj->Set_Mission(MISSION_GUARD); 3371 } 3372 } 3373 } 3374 } else { 3375 3376 /* 3377 ** Create a Soviet unit 3378 */ 3379 for (k = 0; k < 2; k++) if(utable[i].SovietType[k] != UNIT_NONE) { 3380 Reserve_Unit(); 3381 obj = new UnitClass (utable[i].SovietType[k], house); 3382 if (!Scan_Place_Object(obj, centerpt)) { 3383 delete obj; 3384 } else { 3385 if (!hptr->IsHuman) { 3386 obj->Set_Mission(MISSION_GUARD_AREA); 3387 } else { 3388 obj->Set_Mission(MISSION_GUARD); 3389 } 3390 } 3391 } 3392 } 3393 } 3394 } 3395 3396 /* 3397 ** Create infantry 3398 */ 3399 for (i = 0; i < i_limit; i++) { 3400 /* 3401 ** Find the center point for this category. 3402 */ 3403 centerpt = Clip_Scatter(centroid, 4); 3404 3405 /* 3406 ** Place objects; loop through all unit in this category 3407 */ 3408 for (j = 0; j < num_infantry[i] * scaleval; j++) { 3409 3410 /* 3411 ** Create Ally infantry (Note: Unlimbo calls Enter_Idle_Mode(), which 3412 ** assigns the infantry to HUNT; we must use Set_Mission() to override 3413 ** this state.) 3414 */ 3415 if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { 3416 for (k = 0; k < itable[i].AllyCount; k++) { 3417 Reserve_Infantry(); 3418 obj = new InfantryClass (itable[i].AllyType, house); 3419 if (!Scan_Place_Object(obj, centerpt)) { 3420 delete obj; 3421 } else { 3422 if (!hptr->IsHuman) { 3423 obj->Set_Mission(MISSION_GUARD_AREA); 3424 } else { 3425 obj->Set_Mission(MISSION_GUARD); 3426 } 3427 } 3428 } 3429 } else { 3430 3431 /* 3432 ** Create Soviet infantry 3433 */ 3434 for (k = 0; k < itable[i].SovietCount; k++) { 3435 Reserve_Infantry(); 3436 obj = new InfantryClass (itable[i].SovietType, house); 3437 if (!Scan_Place_Object(obj, centerpt)) { 3438 delete obj; 3439 } else { 3440 if (!hptr->IsHuman) { 3441 obj->Set_Mission(MISSION_GUARD_AREA); 3442 } else { 3443 obj->Set_Mission(MISSION_GUARD); 3444 } 3445 } 3446 } 3447 } 3448 } 3449 } 3450 } 3451 } 3452 3453 3454 /*********************************************************************************************** 3455 * Scan_Place_Object -- places an object >near< the given cell * 3456 * * 3457 * INPUT: * 3458 * obj ptr to object to Unlimbo * 3459 * cell center of search area * 3460 * * 3461 * OUTPUT: * 3462 * true = object was placed; false = it wasn't * 3463 * * 3464 * WARNINGS: * 3465 * none. * 3466 * * 3467 * HISTORY: * 3468 * 06/09/1995 BRR : Created. * 3469 *=============================================================================================*/ 3470 int Scan_Place_Object(ObjectClass * obj, CELL cell) 3471 { 3472 int dist; // for object placement 3473 FacingType rot; // for object placement 3474 FacingType fcounter; // for object placement 3475 int tryval; 3476 CELL newcell; 3477 TechnoClass * techno; 3478 int skipit; 3479 3480 /* 3481 ** First try to unlimbo the object in the given cell. 3482 */ 3483 if (Map.In_Radar(cell)) { 3484 techno = Map[cell].Cell_Techno(); 3485 if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && 3486 obj->What_Am_I()==RTTI_INFANTRY)) { 3487 if (obj->Unlimbo(Cell_Coord(cell), DIR_N)) { 3488 return(true); 3489 } 3490 } 3491 } 3492 3493 /* 3494 ** Loop through distances from the given center cell; skip the center cell. 3495 ** For each distance, try placing the object along each rotational direction; 3496 ** if none are available, try each direction with a random scatter value. 3497 ** If that fails, go to the next distance. 3498 ** This ensures that the closest coordinates are filled first. 3499 */ 3500 for (dist = 1; dist < 32; dist++) { 3501 3502 /* 3503 ** Pick a random starting direction 3504 */ 3505 rot = Random_Pick(FACING_N, FACING_NW); 3506 3507 /* 3508 ** Try all directions twice 3509 */ 3510 for (tryval = 0 ; tryval < 2; tryval++) { 3511 3512 /* 3513 ** Loop through all directions, at this distance. 3514 */ 3515 for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { 3516 3517 skipit = false; 3518 3519 /* 3520 ** Pick a coordinate along this directional axis 3521 */ 3522 newcell = Clip_Move(cell, rot, dist); 3523 3524 /* 3525 ** If this is our second try at this distance, add a random scatter 3526 ** to the desired cell, so our units aren't all aligned along spokes. 3527 */ 3528 if (tryval > 0) { 3529 newcell = Clip_Scatter (newcell, 1); 3530 } 3531 3532 /* 3533 ** If, by randomly scattering, we've chosen the exact center, skip 3534 ** it & try another direction. 3535 */ 3536 if (newcell==cell) { 3537 skipit = true; 3538 } 3539 3540 if (!skipit) { 3541 /* 3542 ** Only attempt to Unlimbo the object if: 3543 ** - there is no techno in the cell 3544 ** - the techno in the cell & the object are both infantry 3545 */ 3546 techno = Map[newcell].Cell_Techno(); 3547 if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && 3548 obj->What_Am_I()==RTTI_INFANTRY)) { 3549 if (obj->Unlimbo(Cell_Coord(newcell), DIR_N)) { 3550 return(true); 3551 } 3552 } 3553 } 3554 3555 rot++; 3556 if (rot > FACING_NW) { 3557 rot = FACING_N; 3558 } 3559 } 3560 } 3561 } 3562 3563 return(false); 3564 } 3565 3566 3567 /*********************************************************************************************** 3568 * Clip_Scatter -- randomly scatters from given cell; won't fall off map * 3569 * * 3570 * INPUT: * 3571 * cell cell to scatter from * 3572 * maxdist max distance to scatter * 3573 * * 3574 * OUTPUT: * 3575 * new cell number * 3576 * * 3577 * WARNINGS: * 3578 * none. * 3579 * * 3580 * HISTORY: * 3581 * 07/30/1995 BRR : Created. * 3582 *=============================================================================================*/ 3583 static CELL Clip_Scatter(CELL cell, int maxdist) 3584 { 3585 int x,y; 3586 int xdist; 3587 int ydist; 3588 int xmin,xmax; 3589 int ymin,ymax; 3590 3591 /* 3592 ** Get X & Y coords of given starting cell 3593 */ 3594 x = Cell_X(cell); 3595 y = Cell_Y(cell); 3596 3597 /* 3598 ** Compute our x & y limits 3599 */ 3600 xmin = Map.MapCellX; 3601 xmax = xmin + Map.MapCellWidth - 1; 3602 ymin = Map.MapCellY; 3603 ymax = ymin + Map.MapCellHeight - 1; 3604 3605 /* 3606 ** Adjust the x-coordinate 3607 */ 3608 xdist = Random_Pick(0, maxdist); 3609 if (Percent_Chance(50)) { 3610 x += xdist; 3611 if (x > xmax) { 3612 x = xmax; 3613 } 3614 } else { 3615 x -= xdist; 3616 if (x < xmin) { 3617 x = xmin; 3618 } 3619 } 3620 3621 /* 3622 ** Adjust the y-coordinate 3623 */ 3624 ydist = Random_Pick(0, maxdist); 3625 if (Percent_Chance(50)) { 3626 y += ydist; 3627 if (y > ymax) { 3628 y = ymax; 3629 } 3630 } else { 3631 y -= ydist; 3632 if (y < ymin) { 3633 y = ymin; 3634 } 3635 } 3636 3637 return (XY_Cell(x, y)); 3638 } 3639 3640 3641 /*********************************************************************************************** 3642 * Clip_Move -- moves in given direction from given cell; clips to map * 3643 * * 3644 * INPUT: * 3645 * cell cell to start from * 3646 * facing direction to move * 3647 * dist distance to move * 3648 * * 3649 * OUTPUT: * 3650 * new cell number * 3651 * * 3652 * WARNINGS: * 3653 * none. * 3654 * * 3655 * HISTORY: * 3656 * 07/30/1995 BRR : Created. * 3657 *=============================================================================================*/ 3658 static CELL Clip_Move(CELL cell, FacingType facing, int dist) 3659 { 3660 int x,y; 3661 int xmin,xmax; 3662 int ymin,ymax; 3663 3664 /* 3665 ** Get X & Y coords of given starting cell 3666 */ 3667 x = Cell_X(cell); 3668 y = Cell_Y(cell); 3669 3670 /* 3671 ** Compute our x & y limits 3672 */ 3673 xmin = Map.MapCellX; 3674 xmax = xmin + Map.MapCellWidth - 1; 3675 ymin = Map.MapCellY; 3676 ymax = ymin + Map.MapCellHeight - 1; 3677 3678 /* 3679 ** Adjust the x-coordinate 3680 */ 3681 switch (facing) { 3682 case FACING_N: 3683 y -= dist; 3684 break; 3685 3686 case FACING_NE: 3687 x += dist; 3688 y -= dist; 3689 break; 3690 3691 case FACING_E: 3692 x += dist; 3693 break; 3694 3695 case FACING_SE: 3696 x += dist; 3697 y += dist; 3698 break; 3699 3700 case FACING_S: 3701 y += dist; 3702 break; 3703 3704 case FACING_SW: 3705 x -= dist; 3706 y += dist; 3707 break; 3708 3709 case FACING_W: 3710 x -= dist; 3711 break; 3712 3713 case FACING_NW: 3714 x -= dist; 3715 y -= dist; 3716 break; 3717 } 3718 3719 /* 3720 ** Clip to the map 3721 */ 3722 if (x > xmax) 3723 x = xmax; 3724 if (x < xmin) 3725 x = xmin; 3726 3727 if (y > ymax) 3728 y = ymax; 3729 if (y < ymin) 3730 y = ymin; 3731 3732 return (XY_Cell(x, y)); 3733 } 3734 3735 3736 void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var) 3737 { 3738 if (name == NULL) return; 3739 3740 /* 3741 ** Fetch the scenario number. 3742 */ 3743 char buf[3]; 3744 memcpy(buf, &name[3], 2); 3745 buf[2] = '\0'; 3746 #ifdef FIXIT_CSII // checked - ajw 9/28/98 3747 char first = buf[0]; 3748 char second = buf[1]; 3749 if (first <= '9' && second <= '9') { 3750 scenario = atoi(buf); 3751 } else { 3752 if (first <= '9') { 3753 first -= '0'; 3754 } else { 3755 if (first >= 'a' && first <= 'z') { 3756 first -= 'a'; 3757 } else { 3758 first -= 'A'; 3759 } 3760 } 3761 if (second <= '9') { 3762 second -= '0'; 3763 } else { 3764 if (second >= 'a' && second <= 'z') { 3765 second = (second - 'a') + 10; 3766 } else { 3767 second = (second - 'A') + 10; 3768 } 3769 } 3770 scenario = (36 * first) + second; 3771 } 3772 #else 3773 scenario = atoi(buf); 3774 #endif 3775 3776 /* 3777 ** Fetch the scenario player (side). 3778 */ 3779 player = SCEN_PLAYER_GREECE; 3780 if (name[2] == HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix) { 3781 player = SCEN_PLAYER_SPAIN; 3782 } 3783 if (name[2] == HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix) { 3784 player = SCEN_PLAYER_GREECE; 3785 } 3786 if (name[2] == HouseTypeClass::As_Reference(HOUSE_USSR).Prefix) { 3787 player = SCEN_PLAYER_USSR; 3788 } 3789 3790 /* 3791 ** Fetch the direction. 3792 */ 3793 dir = SCEN_DIR_EAST; 3794 if (name[5] == 'E') { 3795 dir = SCEN_DIR_EAST; 3796 } else { 3797 dir = SCEN_DIR_WEST; 3798 } 3799 3800 /* 3801 ** Fetch the variation. 3802 */ 3803 var = SCEN_VAR_A; 3804 var = ScenarioVarType((name[6] - 'A') + SCEN_VAR_A); 3805 }