CONQUER.CPP (187986B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: /CounterStrike/CONQUER.CPP 6 3/13/97 2:05p Steve_tall $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : CONQUER.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : April 3, 1991 * 28 * * 29 *---------------------------------------------------------------------------------------------* 30 * Functions: * 31 * CC_Draw_Shape -- Custom draw shape handler. * 32 * Call_Back -- Main game maintenance callback routine. * 33 * Color_Cycle -- Handle the general palette color cycling. * 34 * Crate_From_Name -- Given a crate name convert it to a crate type. * 35 * Disk_Space_Available -- returns bytes of free disk space * 36 * Do_Record_Playback -- handles saving/loading map pos & current object * 37 * Fading_Table_Name -- Builds a theater specific fading table name. * 38 * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * 39 * Force_CD_Available -- Ensures that specified CD is available. * 40 * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * 41 * Handle_Team -- Processes team selection command. * 42 * Handle_View -- Either records or restores the tactical view. * 43 * KN_To_Facing -- Converts a keyboard input number into a facing value. * 44 * Keyboard_Process -- Processes the tactical map input codes. * 45 * Language_Name -- Build filename for current language. * 46 * List_Copy -- Makes a copy of a cell offset list. * 47 * Main_Game -- Main game startup routine. * 48 * Main_Loop -- This is the main game loop (as a single loop). * 49 * Map_Edit_Loop -- a mini-main loop for map edit mode only * 50 * Message_Input -- allows inter-player message input processing * 51 * MixFileHandler -- Handles VQ file access. * 52 * Name_From_Source -- retrieves the name for the given SourceType * 53 * Owner_From_Name -- Convert an owner name into a bitfield. * 54 * Play_Movie -- Plays a VQ movie. * 55 * Shake_The_Screen -- Dispatcher that shakes the screen. * 56 * Shape_Dimensions -- Determine the minimum rectangle for the shape. * 57 * Source_From_Name -- Converts ASCII name into SourceType. * 58 * Sync_Delay -- Forces the game into a 15 FPS rate. * 59 * Theater_From_Name -- Converts ASCII name into a theater number. * 60 * Unselect_All -- Causes all selected objects to become unselected. * 61 * VQ_Call_Back -- Maintenance callback used for VQ movies. * 62 * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. 63 * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion. 64 * Is_Aftermath_Installed -- Function to determine the availability of the AM expansion. 65 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 66 67 68 #ifdef TESTCODE 69 class A { 70 public: 71 enum {VAR=1}; 72 }; 73 74 template<class T> 75 class B { 76 public: 77 enum {VAR2=T::VAR}; // this is the line in question. 78 }; 79 80 B<A> test; 81 #endif 82 83 84 85 #include "function.h" 86 #ifdef WIN32 87 #ifdef WINSOCK_IPX 88 #include "WSProto.h" 89 #else //WINSOCK_IPX 90 #include "tcpip.h" 91 #endif //WINSOCK_IPX 92 #else 93 #include "fakesock.h" 94 TcpipManagerClass Winsock; 95 #endif 96 #include <stdlib.h> 97 #include <stdio.h> 98 #include <string.h> 99 #include <direct.h> 100 #include <fcntl.h> 101 #include <io.h> 102 #include <dos.h> 103 #include <share.h> 104 #include "ccdde.h" 105 #include "vortex.h" 106 107 #ifdef WOLAPI_INTEGRATION 108 //#include "WolDebug.h" 109 #include "WolStrng.h" 110 #include "WolapiOb.h" 111 extern WolapiObject* pWolapi; 112 #define PAGE_RESPOND_KEY KN_RETURN //KN_COMMA 113 #endif 114 115 #ifdef MPEGMOVIE 116 #ifdef MCIMPEG 117 #include "mcimovie.h" 118 #endif 119 #include "movie.h" 120 MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user); 121 #endif 122 123 #define SHAPE_TRANS 0x40 124 125 void * Get_Shape_Header_Data(void * ptr); 126 extern bool Spawn_WChat(bool can_launch); 127 128 #ifdef FIXIT_CSII // checked - ajw 9/28/98 129 void Enable_Secret_Units(void); 130 #endif 131 132 extern bool Is_Mission_Aftermath (char *file_name); 133 extern bool Is_Mission_Counterstrike (char *file_name); 134 135 #ifdef FIXIT_VERSION_3 // Stalemate games. 136 extern void Do_Draw(void); 137 #endif 138 139 #ifdef CHEAT_KEYS 140 bool bNoMovies = false; 141 #endif 142 143 /**************************************** 144 ** Function prototypes for this module ** 145 *****************************************/ 146 bool Main_Loop(void); 147 void Keyboard_Process(KeyNumType & input); 148 static void Message_Input(KeyNumType &input); 149 void Color_Cycle(void); 150 bool Map_Edit_Loop(void); 151 152 extern "C" { 153 bool UseOldShapeDraw = false; 154 } 155 156 #ifdef CHEAT_KEYS 157 void Dump_Heap_Pointers( void ); 158 void Error_In_Heap_Pointers( char * string ); 159 #endif 160 static void Do_Record_Playback(void); 161 162 void Toggle_Formation(void); 163 164 extern "C" { 165 extern char * __nheapbeg; 166 } 167 168 // 169 // Special module globals for recording and playback 170 // 171 char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type 172 char TeamNumber = 0; // which team was selected? (1-9) 173 char FormationEvent = 0; // 0 = no event, 1 = formation was toggled 174 175 176 /* -----------------10/14/96 7:29PM------------------ 177 178 --------------------------------------------------*/ 179 180 #if(TEN) 181 void TEN_Call_Back(void); 182 #endif // TEN 183 184 #if(MPATH) 185 void MPATH_Call_Back(void); 186 #endif // MPATH 187 188 /*********************************************************************************************** 189 * Main_Game -- Main game startup routine. * 190 * * 191 * This is the first official routine of the game. It handles game initialization and * 192 * the main game loop control. * 193 * * 194 * Initialization: * 195 * - Init_Game handles one-time-only inits * 196 * - Select_Game is responsible for initializations required for each new game played * 197 * (these may be different depending on whether a multiplayer game is selected, and * 198 * other parameters) * 199 * - This routine performs any un-inits required, both for each game played, and one-time * 200 * * 201 * INPUT: argc -- Number of command line arguments (including program name itself). * 202 * * 203 * argv -- Array of command line argument pointers. * 204 * * 205 * OUTPUT: none * 206 * * 207 * WARNINGS: none * 208 * * 209 * HISTORY: * 210 * 10/01/1994 JLB : Created. * 211 *=============================================================================================*/ 212 void Main_Game(int argc, char * argv[]) 213 { 214 static bool fade = true; 215 216 /* 217 ** Perform one-time-only initializations 218 */ 219 if (!Init_Game(argc, argv)) { 220 return; 221 } 222 223 /* 224 ** Game processing loop: 225 ** 1) Select which game to play, or whether to exit (don't fade the palette 226 ** on the first game selection, but fade it in on subsequent calls) 227 ** 2) Invoke either the main-loop routine, or the editor-loop routine, 228 ** until they indicate that the user wants to exit the scenario. 229 */ 230 while (Select_Game(fade)) { 231 232 // ST 5/14/2019 233 if (RunningAsDLL) { 234 return; 235 } 236 237 fade = false; 238 ScenarioInit = 0; // Kludge. 239 240 fade = true; 241 242 /* 243 ** Initialise the color lookup tables for the chronal vortex 244 */ 245 ChronalVortex.Stop(); 246 ChronalVortex.Setup_Remap_Tables(Scen.Theater); 247 248 /* 249 ** Make the game screen visible, clear the keyboard buffer of spurious 250 ** values, and then show the mouse. This PRESUMES that Select_Game() has 251 ** told the map to draw itself. 252 */ 253 GamePalette.Set(FADE_PALETTE_MEDIUM); 254 Keyboard->Clear(); 255 /* 256 ** Only show the mouse if we're not playing back a recording. 257 */ 258 if (Session.Play) { 259 Hide_Mouse(); 260 TeamEvent = 0; 261 TeamNumber = 0; 262 FormationEvent = 0; 263 } else { 264 Show_Mouse(); 265 } 266 267 #ifdef WIN32 268 if (Session.Type == GAME_INTERNET) { 269 Register_Game_Start_Time(); 270 GameStatisticsPacketSent = false; 271 PacketLater = NULL; 272 ConnectionLost = false; 273 } else { 274 #ifndef WOLAPI_INTEGRATION 275 DDEServer.Disable(); 276 #endif // !WOLAPI_INTEGRATION 277 } 278 #endif //WIN32 279 280 #ifdef SCENARIO_EDITOR 281 /* 282 ** Scenario-editor version of main-loop processing 283 */ 284 for (;;) { 285 /* 286 ** Non-scenario-editor-mode: call the game's main loop 287 */ 288 if (!Debug_Map) { 289 #ifdef FIXIT_CSII // checked - ajw 9/28/98 290 TimeQuake = PendingTimeQuake; 291 PendingTimeQuake = false; 292 #else 293 TimeQuake = false; 294 #endif 295 if (Main_Loop()) { 296 break; 297 } 298 299 if (SpecialDialog != SDLG_NONE) { 300 switch (SpecialDialog) { 301 case SDLG_SPECIAL: 302 Map.Help_Text(TXT_NONE); 303 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 304 Special_Dialog(); 305 Map.Revert_Mouse_Shape(); 306 SpecialDialog = SDLG_NONE; 307 break; 308 309 case SDLG_OPTIONS: 310 Map.Help_Text(TXT_NONE); 311 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 312 Options.Process(); 313 Map.Revert_Mouse_Shape(); 314 SpecialDialog = SDLG_NONE; 315 break; 316 317 case SDLG_SURRENDER: 318 Map.Help_Text(TXT_NONE); 319 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 320 if (Surrender_Dialog(TXT_SURRENDER)) { 321 PlayerPtr->Flag_To_Lose(); 322 } 323 SpecialDialog = SDLG_NONE; 324 Map.Revert_Mouse_Shape(); 325 break; 326 327 default: 328 break; 329 } 330 } 331 } else { 332 333 /* 334 ** Scenario-editor-mode: call the editor's main loop 335 */ 336 if (Map_Edit_Loop()) { 337 break; 338 } 339 } 340 } 341 #else 342 /* 343 ** Non-editor version of main-loop processing 344 */ 345 for (;;) { 346 #ifdef FIXIT_CSII // checked - ajw 9/28/98 347 TimeQuake = PendingTimeQuake; 348 PendingTimeQuake = false; 349 #else 350 TimeQuake = false; 351 #endif 352 /* 353 ** Call the game's main loop 354 */ 355 if (Main_Loop()) { 356 break; 357 } 358 359 /* 360 ** If the SpecialDialog flag is set, invoke the given special dialog. 361 ** This must be done outside the main loop, since the dialog will call 362 ** Main_Loop(), allowing the game to run in the background. 363 */ 364 if (SpecialDialog != SDLG_NONE) { 365 switch (SpecialDialog) { 366 case SDLG_SPECIAL: 367 Map.Help_Text(TXT_NONE); 368 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 369 Special_Dialog(); 370 Map.Revert_Mouse_Shape(); 371 SpecialDialog = SDLG_NONE; 372 break; 373 374 case SDLG_OPTIONS: 375 Map.Help_Text(TXT_NONE); 376 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 377 Options.Process(); 378 Map.Revert_Mouse_Shape(); 379 SpecialDialog = SDLG_NONE; 380 break; 381 382 case SDLG_SURRENDER: 383 Map.Help_Text(TXT_NONE); 384 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 385 if (Surrender_Dialog(TXT_SURRENDER)) { 386 OutList.Add(EventClass(EventClass::DESTRUCT)); 387 } 388 SpecialDialog = SDLG_NONE; 389 Map.Revert_Mouse_Shape(); 390 break; 391 392 /*ifdef FIXIT_VERSION_3 // Stalemate games. 393 case SDLG_PROPOSE_DRAW: 394 Map.Help_Text(TXT_NONE); 395 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 396 if (Surrender_Dialog(TXT_WOL_PROPOSE_DRAW)) { 397 OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); 398 } 399 SpecialDialog = SDLG_NONE; 400 Map.Revert_Mouse_Shape(); 401 break; 402 403 case SDLG_ACCEPT_DRAW: 404 Map.Help_Text(TXT_NONE); 405 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 406 if (Surrender_Dialog(TXT_WOL_ACCEPT_DRAW)) { 407 OutList.Add(EventClass(EventClass::ACCEPT_DRAW)); 408 } 409 SpecialDialog = SDLG_NONE; 410 Map.Revert_Mouse_Shape(); 411 break; 412 #endif 413 */ 414 default: 415 break; 416 } 417 } 418 } 419 #endif 420 421 422 #ifdef WIN32 423 /* 424 ** Send the game stats to WChat if we haven't already done so 425 */ 426 if (!GameStatisticsPacketSent && PacketLater) { 427 Send_Statistics_Packet(); // After game sending if PacketLater set. 428 } 429 #endif //WIN32 430 431 /* 432 ** Scenario is done; fade palette to black 433 */ 434 BlackPalette.Set(FADE_PALETTE_SLOW); 435 VisiblePage.Clear(); 436 437 /* 438 ** Un-initialize whatever needs it, for each game played. 439 ** 440 ** Shut down either the modem or network; they'll get re-initialized if 441 ** the user selections those options again in Select_Game(). This 442 ** "re-boots" the modem & network code, which I currently feel is safer 443 ** than just letting it hang around. 444 ** (Skip this step if we're in playback mode; the modem or net won't have 445 ** been initialized in that case.) 446 */ 447 if (Session.Record || Session.Play) { 448 Session.RecordFile.Close(); 449 } 450 451 if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) { 452 if (!Session.Play) { 453 //PG Modem_Signoff(); 454 } 455 } else { 456 if (Session.Type == GAME_IPX) { 457 if (!Session.Play) { 458 //PG Shutdown_Network(); 459 } 460 } 461 } 462 463 #if(TEN) 464 465 if (Session.Type == GAME_TEN) { 466 Shutdown_TEN(); 467 //Prog_End(); 468 Emergency_Exit(0); 469 } 470 #endif // TEN 471 472 #if(MPATH) 473 if (Session.Type == GAME_MPATH) { 474 Shutdown_MPATH(); 475 //Prog_End(); 476 Emergency_Exit(0); 477 } 478 #endif // MPATH 479 480 /* 481 ** If we're playing back, the mouse will be hidden; show it. 482 ** Also, set all variables back to normal, to return to the main menu. 483 */ 484 if (Session.Play) { 485 Show_Mouse(); 486 Session.Type = GAME_NORMAL; 487 Session.Play = 0; 488 } 489 #ifndef WOLAPI_INTEGRATION 490 #ifdef WIN32 491 if (Special.IsFromWChat) { 492 //PG Shutdown_Network(); // Clear up the pseudo IPX stuff 493 #ifndef WINSOCK_IPX 494 Winsock.Close(); 495 #endif //WINSOCK_IPX 496 Special.IsFromWChat = false; 497 SpawnedFromWChat = false; 498 DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice 499 Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu 500 Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us 501 } 502 #endif //WIN32 503 #endif // !WOLAPI_INTEGRATION 504 } 505 506 /* 507 ** Free the scenario description buffers 508 */ 509 Session.Free_Scenario_Descriptions(); 510 } 511 512 513 /*********************************************************************************************** 514 * Keyboard_Process -- Processes the tactical map input codes. * 515 * * 516 * This routine is used to process the input codes while the player * 517 * has the tactical map displayed. It handles all the keys that * 518 * are appropriate to that mode. * 519 * * 520 * INPUT: input -- Input code as returned from Input_Num(). * 521 * * 522 * OUTPUT: none * 523 * * 524 * WARNINGS: none * 525 * * 526 * HISTORY: * 527 * 01/21/1992 JLB : Created. * 528 * 07/04/1995 JLB : Handles team and map control hotkeys. * 529 *=============================================================================================*/ 530 void Keyboard_Process(KeyNumType & input) 531 { 532 ObjectClass * obj; 533 int index; 534 535 /* 536 ** Don't do anything if there is not keyboard event. 537 */ 538 if (input == KN_NONE) { 539 return; 540 } 541 /* 542 ** For network & modem, process user input for inter-player messages. 543 */ 544 Message_Input(input); 545 546 #ifdef WIN32 547 /* 548 ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to 549 ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed. 550 */ 551 552 KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT)); 553 KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT); 554 555 556 #else 557 KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT)); 558 KeyNumType key = plain; 559 #endif 560 561 #ifdef CHEAT_KEYS 562 563 if (Debug_Flag) { 564 HousesType h; 565 566 switch (int(input)) { 567 case int(int(KN_M) | int(KN_SHIFT_BIT)): 568 case int(int(KN_M) | int(KN_ALT_BIT)): 569 case int(int(KN_M) | int(KN_CTRL_BIT)): 570 for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 571 Houses.Ptr(h)->Refund_Money(10000); 572 } 573 break; 574 575 default: 576 break; 577 } 578 } 579 #endif 580 581 #ifdef VIRGIN_CHEAT_KEYS 582 if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { 583 PlayerPtr->Blockage = false; 584 PlayerPtr->Flag_To_Win(); 585 } 586 #endif 587 588 #ifdef CHEAT_KEYS 589 #ifdef WIN32 590 if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) { 591 #else 592 if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { 593 #endif 594 PlayerPtr->Blockage = false; 595 PlayerPtr->Flag_To_Win(); 596 } 597 598 if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) { 599 if (Session.Type == GAME_NORMAL) { 600 Debug_Unshroud = (Debug_Unshroud == false); 601 Map.Flag_To_Redraw(true); 602 } 603 } 604 605 if (Debug_Flag && input == KN_SLASH) { 606 if (Session.Type != GAME_NORMAL) { 607 SpecialDialog = SDLG_SPECIAL; 608 input = KN_NONE; 609 } else { 610 Special_Dialog(); 611 } 612 } 613 #endif 614 615 /* 616 ** Process prerecorded team selection. This will be an additive select 617 ** if the SHIFT key is held down. It will create the team if the 618 ** CTRL or ALT key is held down. 619 */ 620 int action = 0; 621 #ifdef WIN32 622 if (input & WWKEY_SHIFT_BIT) action = 1; 623 if (input & WWKEY_ALT_BIT) action = 3; 624 if (input & WWKEY_CTRL_BIT) action = 2; 625 #else 626 if (input & KN_SHIFT_BIT) action = 1; 627 if (input & KN_ALT_BIT) action = 3; 628 if (input & KN_CTRL_BIT) action = 2; 629 #endif 630 631 /* 632 ** If the "N" key is pressed, then select the next object. 633 */ 634 if (key != 0 && key == Options.KeyNext) { 635 if (action) { 636 obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 637 } else { 638 obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 639 } 640 if (obj != NULL) { 641 Unselect_All(); 642 obj->Select(); 643 Map.Center_Map(); 644 Map.Flag_To_Redraw(true); 645 } 646 input = KN_NONE; 647 } 648 if (key != 0 && key == Options.KeyPrevious) { 649 if (action) { 650 obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 651 } else { 652 obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 653 } 654 if (obj != NULL) { 655 Unselect_All(); 656 obj->Select(); 657 Map.Center_Map(); 658 Map.Flag_To_Redraw(true); 659 } 660 input = KN_NONE; 661 } 662 663 664 /* 665 ** All selected units will go into idle mode. 666 */ 667 if (key != 0 && key == Options.KeyStop) { 668 if (CurrentObject.Count()) { 669 for (index = 0; index < CurrentObject.Count(); index++) { 670 ObjectClass const * tech = CurrentObject[index]; 671 672 if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { 673 OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); 674 } 675 } 676 } 677 input = KN_NONE; 678 } 679 680 /* 681 ** All selected units will attempt to go into guard area mode. 682 */ 683 if (key != 0 && key == Options.KeyGuard) { 684 if (CurrentObject.Count()) { 685 for (index = 0; index < CurrentObject.Count(); index++) { 686 ObjectClass const * tech = CurrentObject[index]; 687 688 if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { 689 OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); 690 } 691 } 692 } 693 input = KN_NONE; 694 } 695 696 /* 697 ** All selected units will attempt to scatter. 698 */ 699 if (key != 0 && key == Options.KeyScatter) { 700 if (CurrentObject.Count()) { 701 for (index = 0; index < CurrentObject.Count(); index++) { 702 ObjectClass const * tech = CurrentObject[index]; 703 704 if (tech != NULL && tech->Can_Player_Move()) { 705 OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); 706 } 707 } 708 } 709 input = KN_NONE; 710 } 711 712 /* 713 ** Center the map around the currently selected objects. If no 714 ** objects are selected, then fall into the home case. 715 */ 716 if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) { 717 if (CurrentObject.Count()) { 718 Map.Center_Map(); 719 #ifdef WIN32 720 Map.Flag_To_Redraw(true); 721 #endif 722 input = KN_NONE; 723 } else { 724 input = Options.KeyBase; 725 } 726 } 727 728 /* 729 ** Center the map about the construction yard or construction vehicle 730 ** if one is present. 731 */ 732 if (key != 0 && key == Options.KeyBase) { 733 Unselect_All(); 734 if (PlayerPtr->CurBuildings) { 735 for (index = 0; index < Buildings.Count(); index++) { 736 BuildingClass * building = Buildings.Ptr(index); 737 738 if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { 739 Unselect_All(); 740 building->Select(); 741 if (building->IsLeader) break; 742 } 743 } 744 } 745 if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) { 746 for (index = 0; index < Units.Count(); index++) { 747 UnitClass * unit = Units.Ptr(index); 748 749 if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { 750 Unselect_All(); 751 unit->Select(); 752 break; 753 } 754 } 755 } 756 if (CurrentObject.Count()) { 757 Map.Center_Map(); 758 } else { 759 if (PlayerPtr->Center != 0) { 760 Map.Center_Map(PlayerPtr->Center); 761 } 762 } 763 Map.Flag_To_Redraw(true); 764 input = KN_NONE; 765 } 766 767 /* 768 ** Toggle the status of formation for the current team 769 */ 770 if (key != 0 && key == Options.KeyFormation) { 771 Toggle_Formation(); 772 input = KN_NONE; 773 } 774 775 #ifdef TOFIX 776 /* 777 ** For multiplayer, 'R' pops up the surrender dialog. 778 */ 779 if (input != 0 && input == Options.KeyResign) { 780 if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { 781 SpecialDialog = SDLG_SURRENDER; 782 input = KN_NONE; 783 } 784 input = KN_NONE; 785 } 786 #endif 787 788 /* 789 ** Handle making and breaking alliances. 790 */ 791 if (key != 0 && key == Options.KeyAlliance) { 792 if (Session.Type != GAME_NORMAL || Debug_Flag) { 793 if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { 794 if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { 795 OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); 796 } 797 } 798 } 799 input = KN_NONE; 800 } 801 802 /* 803 ** Select all the units on the current display. This is equivalent to 804 ** drag selecting the whole view. 805 */ 806 if (key != 0 && key == Options.KeySelectView) { 807 Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight)); 808 input = KN_NONE; 809 } 810 811 /* 812 ** Toggles the repair state similarly to pressing the repair button. 813 */ 814 if (key != 0 && key == Options.KeyRepair) { 815 Map.Repair_Mode_Control(-1); 816 input = KN_NONE; 817 } 818 819 /* 820 ** Toggles the sell state similarly to pressing the sell button. 821 */ 822 if (key != 0 && key == Options.KeySell) { 823 Map.Sell_Mode_Control(-1); 824 input = KN_NONE; 825 } 826 827 /* 828 ** Toggles the map zoom mode similarly to pressing the map button. 829 */ 830 if (key != 0 && key == Options.KeyMap) { 831 Map.Zoom_Mode_Control(); 832 input = KN_NONE; 833 } 834 835 /* 836 ** Scrolls the sidebar up one slot. 837 */ 838 if (key != 0 && key == Options.KeySidebarUp) { 839 Map.SidebarClass::Scroll(true, -1); 840 input = KN_NONE; 841 } 842 843 /* 844 ** Scrolls the sidebar down one slot. 845 */ 846 if (key != 0 && key == Options.KeySidebarDown) { 847 Map.SidebarClass::Scroll(false, -1); 848 input = KN_NONE; 849 } 850 851 /* 852 ** Brings up the options dialog box. 853 */ 854 if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) { 855 Map.Help_Text(TXT_NONE); // Turns off help text. 856 Queue_Options(); 857 input = KN_NONE; 858 } 859 860 /* 861 ** Scrolls the tactical map in the direction specified. 862 */ 863 int distance = CELL_LEPTON_W; 864 if (key != 0 && key == Options.KeyScrollLeft) { 865 Map.Scroll_Map(DIR_W, distance, true); 866 input = KN_NONE; 867 } 868 if (key != 0 && key == Options.KeyScrollRight) { 869 Map.Scroll_Map(DIR_E, distance, true); 870 input = KN_NONE; 871 } 872 if (key != 0 && key == Options.KeyScrollUp) { 873 Map.Scroll_Map(DIR_N, distance, true); 874 input = KN_NONE; 875 } 876 if (key != 0 && key == Options.KeyScrollDown) { 877 Map.Scroll_Map(DIR_S, distance, true); 878 input = KN_NONE; 879 } 880 881 /* 882 ** Teams are handled by the 10 special team keys. The manual comparison 883 ** to the KN numbers is because the Windows keyboard driver can vary 884 ** the base code number for the key depending on the shift or alt key 885 ** state! 886 */ 887 if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) { 888 Handle_Team(0, action); 889 input = KN_NONE; 890 } 891 if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) { 892 Handle_Team(1, action); 893 input = KN_NONE; 894 } 895 if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) { 896 Handle_Team(2, action); 897 input = KN_NONE; 898 } 899 if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) { 900 Handle_Team(3, action); 901 input = KN_NONE; 902 } 903 if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) { 904 Handle_Team(4, action); 905 input = KN_NONE; 906 } 907 if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) { 908 Handle_Team(5, action); 909 input = KN_NONE; 910 } 911 if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) { 912 Handle_Team(6, action); 913 input = KN_NONE; 914 } 915 if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) { 916 Handle_Team(7, action); 917 input = KN_NONE; 918 } 919 if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) { 920 Handle_Team(8, action); 921 input = KN_NONE; 922 } 923 if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) { 924 Handle_Team(9, action); 925 input = KN_NONE; 926 } 927 928 /* 929 ** Handle the bookmark hotkeys. 930 */ 931 if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) { 932 Handle_View(0, action); 933 input = KN_NONE; 934 } 935 if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) { 936 Handle_View(1, action); 937 input = KN_NONE; 938 } 939 if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) { 940 Handle_View(2, action); 941 input = KN_NONE; 942 } 943 if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) { 944 Handle_View(3, action); 945 input = KN_NONE; 946 } 947 948 #ifdef CHEAT_KEYS 949 if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { 950 Debug_Key(input); 951 } 952 #endif 953 } 954 955 956 void Toggle_Formation(void) { 957 958 // MBL 03.23.2020: this has been copied to DLLExportClass::Team_Units_Formation_Toggle_On(), and modified as needed 959 #if 0 960 int team = -1; 961 long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; 962 long maxx = 0, maxy = 0; 963 int index; 964 bool setform = 0; 965 966 // 967 // Recording support 968 // 969 if (Session.Record) { 970 FormationEvent = 1; 971 } 972 973 /* 974 ** Find the first selected object that is a member of a team, and 975 ** register his group as the team we're using. Once we find the team 976 ** number, update the 'setform' flag to know whether we should be setting 977 ** the formation's offsets, or clearing them. If they currently have 978 ** illegal offsets (as in 0x80000000), then we're setting. 979 */ 980 for (index = 0; index < Units.Count(); index++) { 981 UnitClass * obj = Units.Ptr(index); 982 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { 983 team = obj->Group; 984 if (team != -1) { 985 setform = obj->XFormOffset == (int)0x80000000; 986 TeamSpeed[team] = SPEED_WHEEL; 987 TeamMaxSpeed[team] = MPH_LIGHT_SPEED; 988 break; 989 } 990 } 991 } 992 if (team == -1) { 993 for (index = 0; index < Infantry.Count(); index++) { 994 InfantryClass * obj = Infantry.Ptr(index); 995 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { 996 team = obj->Group; 997 if (team != -1) { 998 setform = obj->XFormOffset == (int)0x80000000; 999 TeamSpeed[team] = SPEED_WHEEL; 1000 TeamMaxSpeed[team] = MPH_LIGHT_SPEED; 1001 break; 1002 } 1003 } 1004 } 1005 } 1006 1007 if (team == -1) { 1008 for (index = 0; index < Vessels.Count(); index++) { 1009 VesselClass * obj = Vessels.Ptr(index); 1010 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { 1011 team = obj->Group; 1012 if (team != -1) { 1013 setform = obj->XFormOffset == 0x80000000UL; 1014 TeamSpeed[team] = SPEED_WHEEL; 1015 TeamMaxSpeed[team] = MPH_LIGHT_SPEED; 1016 break; 1017 } 1018 } 1019 } 1020 } 1021 1022 if (team == -1) return; 1023 /* 1024 ** Now that we have a team, let's go set (or clear) the formation offsets. 1025 */ 1026 for (index = 0; index < Units.Count(); index++) { 1027 UnitClass * obj = Units.Ptr(index); 1028 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { 1029 obj->Mark(MARK_CHANGE); 1030 if (setform) { 1031 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1032 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1033 if (xc < minx) minx = xc; 1034 if (xc > maxx) maxx = xc; 1035 if (yc < miny) miny = yc; 1036 if (yc > maxy) maxy = yc; 1037 if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { 1038 TeamMaxSpeed[team] = obj->Class->MaxSpeed; 1039 TeamSpeed[team] = obj->Class->Speed; 1040 } 1041 } else { 1042 obj->XFormOffset = obj->YFormOffset = (int)0x80000000; 1043 } 1044 } 1045 } 1046 1047 for (index = 0; index < Infantry.Count(); index++) { 1048 InfantryClass * obj = Infantry.Ptr(index); 1049 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { 1050 obj->Mark(MARK_CHANGE); 1051 if (setform) { 1052 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1053 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1054 if (xc < minx) minx = xc; 1055 if (xc > maxx) maxx = xc; 1056 if (yc < miny) miny = yc; 1057 if (yc > maxy) maxy = yc; 1058 if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { 1059 TeamMaxSpeed[team] = obj->Class->MaxSpeed; 1060 } 1061 } else { 1062 obj->XFormOffset = obj->YFormOffset = (int)0x80000000; 1063 } 1064 } 1065 } 1066 1067 for (index = 0; index < Vessels.Count(); index++) { 1068 VesselClass * obj = Vessels.Ptr(index); 1069 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { 1070 obj->Mark(MARK_CHANGE); 1071 if (setform) { 1072 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1073 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1074 if (xc < minx) minx = xc; 1075 if (xc > maxx) maxx = xc; 1076 if (yc < miny) miny = yc; 1077 if (yc > maxy) maxy = yc; 1078 if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { 1079 TeamMaxSpeed[team] = obj->Class->MaxSpeed; 1080 } 1081 } else { 1082 obj->XFormOffset = obj->YFormOffset = 0x80000000UL; 1083 } 1084 } 1085 } 1086 1087 /* 1088 ** All the units have been counted to find the bounding rectangle and 1089 ** center of the formation, or to clear their offsets. Now, if we're to 1090 ** set them into formation, proceed to do so. Otherwise, bail. 1091 */ 1092 if (setform) { 1093 int centerx = (int)((maxx - minx)/2)+minx; 1094 int centery = (int)((maxy - miny)/2)+miny; 1095 1096 for (index = 0; index < Units.Count(); index++) { 1097 UnitClass * obj = Units.Ptr(index); 1098 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { 1099 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1100 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1101 1102 obj->XFormOffset = xc - centerx; 1103 obj->YFormOffset = yc - centery; 1104 } 1105 } 1106 1107 for (index = 0; index < Infantry.Count(); index++) { 1108 InfantryClass * obj = Infantry.Ptr(index); 1109 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { 1110 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1111 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1112 1113 obj->XFormOffset = xc - centerx; 1114 obj->YFormOffset = yc - centery; 1115 } 1116 } 1117 1118 for (index = 0; index < Vessels.Count(); index++) { 1119 VesselClass * obj = Vessels.Ptr(index); 1120 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { 1121 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 1122 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 1123 1124 obj->XFormOffset = xc - centerx; 1125 obj->YFormOffset = yc - centery; 1126 } 1127 } 1128 } 1129 #endif 1130 } 1131 1132 1133 /*********************************************************************************************** 1134 * Message_Input -- allows inter-player message input processing * 1135 * * 1136 * INPUT: * 1137 * input key value * 1138 * * 1139 * OUTPUT: * 1140 * none. * 1141 * * 1142 * WARNINGS: * 1143 * none. * 1144 * * 1145 * HISTORY: * 1146 * 05/22/1995 BRR : Created. * 1147 *=============================================================================================*/ 1148 //#pragma off (unreferenced) 1149 static void Message_Input(KeyNumType &input) 1150 { 1151 int rc; 1152 char txt[MAX_MESSAGE_LENGTH+32]; 1153 int id; 1154 // SerialPacketType * serial_packet; 1155 //int i; 1156 KeyNumType copy_input; 1157 //char *msg; 1158 1159 /* 1160 ** Check keyboard input for a request to send a message. 1161 ** The 'to' argument for Add_Edit is prefixed to the message buffer; the 1162 ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. 1163 ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the 1164 ** 'to' portion. At the other end, the buffer allocated to display the 1165 ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". 1166 */ 1167 #ifdef WOLAPI_INTEGRATION 1168 if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && 1169 ( ( input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) ) || input == PAGE_RESPOND_KEY ) && 1170 !Session.Messages.Is_Edit()) { 1171 #else 1172 if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) { 1173 #endif 1174 memset (txt, 0, 40); 1175 1176 /* 1177 ** For a serial game, send a message on F1 or F4; set 'txt' to the 1178 ** "Message:" string & add an editable message to the list. 1179 */ 1180 if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { 1181 if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) { 1182 1183 strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:" 1184 1185 Session.Messages.Add_Edit (Session.ColorIdx, 1186 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1187 1188 Map.Flag_To_Redraw(false); 1189 } 1190 } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) { 1191 /* 1192 ** For a network game: 1193 ** F1-F7 = "To <name> (house):" (only allowed if we're not in ObiWan mode) 1194 ** F8 = "To All:" 1195 */ 1196 if (input==(KN_F1 + Session.MaxPlayers - 1)) { 1197 1198 Session.MessageAddress = IPXAddressClass(); // set to broadcast 1199 strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:" 1200 1201 Session.Messages.Add_Edit(Session.ColorIdx, 1202 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1203 1204 Map.Flag_To_Redraw(false); 1205 1206 #ifdef WOLAPI_INTEGRATION 1207 } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan && input != PAGE_RESPOND_KEY ) { 1208 #else 1209 } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) { 1210 #endif 1211 id = Ipx.Connection_ID(input - KN_F1); 1212 Session.MessageAddress = (*(Ipx.Connection_Address (id))); 1213 sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id)); 1214 1215 Session.Messages.Add_Edit(Session.ColorIdx, 1216 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1217 1218 Map.Flag_To_Redraw(false); 1219 } 1220 #ifdef WOLAPI_INTEGRATION 1221 else if( Session.Type == GAME_INTERNET && pWolapi && !pWolapi->bConnectionDown && input == PAGE_RESPOND_KEY ) 1222 { 1223 if( *pWolapi->szExternalPager ) 1224 { 1225 // Respond to a page from external ww online user that paged me. 1226 // Set MessageAddress to all zeroes, as a flag to ourselves later on. 1227 NetNumType blip; 1228 NetNodeType blop; 1229 memset( blip, 0, 4 ); 1230 memset( blop, 0, 6 ); 1231 Session.MessageAddress = IPXAddressClass( blip, blop ); 1232 1233 // Tell pWolapi not to reset szExternalPager for the time being. 1234 pWolapi->bFreezeExternalPager = true; 1235 1236 sprintf( txt, Text_String( TXT_TO ), pWolapi->szExternalPager ); 1237 1238 Session.Messages.Add_Edit(Session.ColorIdx, 1239 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1240 1241 Map.Flag_To_Redraw(false); 1242 1243 Keyboard->Clear(); 1244 } 1245 else 1246 { 1247 Session.Messages.Add_Message( NULL, 0, TXT_WOL_NOTPAGED, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); 1248 Sound_Effect( VOC_SYS_ERROR ); 1249 } 1250 } 1251 #endif 1252 } 1253 #if(TEN) 1254 /* 1255 ** For a TEN game: 1256 ** F1-F7 = "To <name> (house):" (only allowed if we're not in ObiWan mode) 1257 ** F8 = "To All:" 1258 */ 1259 else if (Session.Type == GAME_TEN && !Session.Messages.Is_Edit()) { 1260 if (input==(KN_F1 + Session.MaxPlayers - 1)) { 1261 1262 Session.TenMessageAddress = -1; // set to broadcast 1263 strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" 1264 1265 Session.Messages.Add_Edit(Session.ColorIdx, 1266 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1267 1268 Map.Flag_To_Redraw(false); 1269 1270 } else if ((input - KN_F1) < Ten->Num_Connections() && !Session.ObiWan) { 1271 1272 id = Ten->Connection_ID(input - KN_F1); 1273 Session.TenMessageAddress = Ten->Connection_Address(id); 1274 sprintf(txt,Text_String(TXT_TO),Ten->Connection_Name(id)); 1275 1276 Session.Messages.Add_Edit(Session.ColorIdx, 1277 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1278 1279 Map.Flag_To_Redraw(false); 1280 } 1281 } 1282 #endif // TEN 1283 #if(MPATH) 1284 /* 1285 ** For a MPATH game: 1286 ** F1-F7 = "To <name> (house):" (only allowed if we're not in ObiWan mode) 1287 ** F8 = "To All:" 1288 */ 1289 else if (Session.Type == GAME_MPATH && !Session.Messages.Is_Edit()) { 1290 if (input==(KN_F1 + Session.MaxPlayers - 1)) { 1291 1292 Session.MPathMessageAddress = 0; // set to broadcast 1293 strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" 1294 1295 Session.Messages.Add_Edit(Session.ColorIdx, 1296 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1297 1298 Map.Flag_To_Redraw(false); 1299 1300 } else if ((input - KN_F1) < MPath->Num_Connections() && !Session.ObiWan) { 1301 1302 id = MPath->Connection_ID(input - KN_F1); 1303 Session.MPathMessageAddress = MPath->Connection_Address(id); 1304 sprintf(txt,Text_String(TXT_TO),MPath->Connection_Name(id)); 1305 1306 Session.Messages.Add_Edit(Session.ColorIdx, 1307 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); 1308 1309 Map.Flag_To_Redraw(false); 1310 } 1311 } 1312 #endif // MPATH 1313 } 1314 1315 /* 1316 ** Process message-system input; send the message out if RETURN is hit. 1317 */ 1318 copy_input = input; 1319 rc = Session.Messages.Input(input); 1320 1321 /* 1322 ** If a single character has been added to an edit buffer, update the display. 1323 */ 1324 if (rc == 1 && Session.Type != GAME_NORMAL) { 1325 Map.Flag_To_Redraw(false); 1326 } 1327 1328 /* 1329 ** If backspace was hit, redraw the map. If the edit message was removed, 1330 ** the map must be force-drawn, since it won't be able to compute the 1331 ** cells to redraw; otherwise, let the map compute the cells to redraw, 1332 ** by not force-drawing it, but just setting the IsToRedraw bit. 1333 */ 1334 if (rc==2 && Session.Type != GAME_NORMAL) { 1335 if (copy_input==KN_ESC) { 1336 Map.Flag_To_Redraw(true); 1337 #ifdef WOLAPI_INTEGRATION 1338 if( pWolapi ) 1339 // Just in case user was responding to a page from outside the game, and we had frozen the "szExternalPager". 1340 pWolapi->bFreezeExternalPager = false; 1341 #endif 1342 } else { 1343 Map.Flag_To_Redraw(false); 1344 } 1345 Map.DisplayClass::IsToRedraw = true; 1346 } 1347 1348 /* 1349 ** Send a message 1350 */ 1351 if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) { 1352 #if (0) 1353 /* 1354 ** Serial game: fill in a SerialPacketType & send it. 1355 ** (Note: The size of the SerialPacketType.Command must be the same as 1356 ** the EventClass.Type!) 1357 */ 1358 if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { 1359 serial_packet = (SerialPacketType *)NullModem.BuildBuf; 1360 1361 serial_packet->Command = SERIAL_MESSAGE; 1362 strcpy (serial_packet->Name, Session.Players[0]->Name); 1363 serial_packet->ID = Session.ColorIdx; 1364 1365 if (rc==3) { 1366 strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf()); 1367 } else { 1368 strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf()); 1369 Session.Messages.Clear_Overflow_Buf(); 1370 } 1371 1372 /* 1373 ** Send the message, and store this message in our LastMessage 1374 ** buffer; the computer may send us a version of it later. 1375 */ 1376 NullModem.Send_Message(NullModem.BuildBuf, 1377 sizeof(SerialPacketType), 1); 1378 1379 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1380 char *ptr = &serial_packet->Message.Message[0]; 1381 if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { 1382 Enable_Secret_Units(); 1383 } 1384 #endif 1385 strcpy(Session.LastMessage, serial_packet->Message.Message); 1386 } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { 1387 #ifdef WOLAPI_INTEGRATION 1388 NetNumType blip; 1389 NetNodeType blop; 1390 Session.MessageAddress.Get_Address( blip, blop ); 1391 if( blip[0] + blip[1] + blip[2] + blip[3] + blop[0] + blop[1] + blop[2] + blop[3] + blop[4] + blop[5] == 0 ) 1392 { 1393 // This message is a response to the last person that paged me. 1394 if( pWolapi && !pWolapi->bConnectionDown ) // (As connection may have gone down.) 1395 { 1396 pWolapi->Page( pWolapi->szExternalPager, Session.Messages.Get_Edit_Buf(), false ); 1397 pWolapi->bFreezeExternalPager = false; 1398 } 1399 } 1400 else 1401 #endif 1402 { 1403 1404 /* 1405 ** Network game: fill in a GlobalPacketType & send it. 1406 */ 1407 Session.GPacket.Command = NET_MESSAGE; 1408 strcpy (Session.GPacket.Name, Session.Players[0]->Name); 1409 Session.GPacket.Message.Color = Session.ColorIdx; 1410 Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); 1411 1412 if (rc==3) { 1413 strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); 1414 } else { 1415 strcpy (Session.GPacket.Message.Buf, 1416 Session.Messages.Get_Overflow_Buf()); 1417 Session.Messages.Clear_Overflow_Buf(); 1418 } 1419 1420 /* 1421 ** If 'F4' was hit, MessageAddress will be a broadcast address; send 1422 ** the message to every player we have a connection with. 1423 */ 1424 if (Session.MessageAddress.Is_Broadcast()) { 1425 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1426 char *ptr = &Session.GPacket.Message.Buf[0]; 1427 if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { 1428 *ptr = 'X'; // force it to an odd hack so we know it was broadcast. 1429 Enable_Secret_Units(); 1430 } 1431 #endif 1432 for (i = 0; i < Ipx.Num_Connections(); i++) { 1433 Ipx.Send_Global_Message(&Session.GPacket, 1434 sizeof(GlobalPacketType), 1, 1435 Ipx.Connection_Address(Ipx.Connection_ID(i))); 1436 Ipx.Service(); 1437 } 1438 } else { 1439 1440 /* 1441 ** Otherwise, MessageAddress contains the exact address to send to. 1442 ** Send to that address only. 1443 */ 1444 Ipx.Send_Global_Message(&Session.GPacket, 1445 sizeof(GlobalPacketType), 1, 1446 &Session.MessageAddress); 1447 Ipx.Service(); 1448 1449 } 1450 1451 /* 1452 ** Store this message in our LastMessage buffer; the computer may send 1453 ** us a version of it later. 1454 */ 1455 strcpy(Session.LastMessage, Session.GPacket.Message.Buf); 1456 } 1457 } 1458 1459 #if(TEN) 1460 /* 1461 ** TEN game: fill in a GlobalPacketType & send it. 1462 */ 1463 else if (Session.Type == GAME_TEN) { 1464 Session.GPacket.Command = NET_MESSAGE; 1465 strcpy (Session.GPacket.Name, Session.Players[0]->Name); 1466 Session.GPacket.Message.Color = Session.ColorIdx; 1467 Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); 1468 1469 if (rc==3) { 1470 strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); 1471 } else { 1472 strcpy (Session.GPacket.Message.Buf, 1473 Session.Messages.Get_Overflow_Buf()); 1474 Session.Messages.Clear_Overflow_Buf(); 1475 } 1476 1477 Ten->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), 1478 1, Session.TenMessageAddress); 1479 } 1480 #endif // TEN 1481 1482 #if(MPATH) 1483 /* 1484 ** MPATH game: fill in a GlobalPacketType & send it. 1485 */ 1486 else if (Session.Type == GAME_MPATH) { 1487 Session.GPacket.Command = NET_MESSAGE; 1488 strcpy (Session.GPacket.Name, Session.Players[0]->Name); 1489 Session.GPacket.Message.Color = Session.ColorIdx; 1490 Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); 1491 1492 if (rc==3) { 1493 strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); 1494 } else { 1495 strcpy (Session.GPacket.Message.Buf, 1496 Session.Messages.Get_Overflow_Buf()); 1497 Session.Messages.Clear_Overflow_Buf(); 1498 } 1499 1500 MPath->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), 1501 1, Session.MPathMessageAddress); 1502 } 1503 #endif // MPATH 1504 1505 /* 1506 ** Tell the map to completely update itself, since a message is now missing. 1507 */ 1508 Map.Flag_To_Redraw(true); 1509 #endif 1510 } 1511 } 1512 //#pragma on (unreferenced) 1513 1514 1515 /*********************************************************************************************** 1516 * Color_Cycle -- Handle the general palette color cycling. * 1517 * * 1518 * This is a maintenance routine that handles the color cycling. It should be called as * 1519 * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * 1520 * * 1521 * INPUT: none * 1522 * * 1523 * OUTPUT: none * 1524 * * 1525 * WARNINGS: none * 1526 * * 1527 * HISTORY: * 1528 * 05/31/1994 JLB : Created. * 1529 * 06/10/1994 JLB : Uses new cycle color values. * 1530 * 12/21/1994 JLB : Handles text fade color. * 1531 * 07/16/1996 JLB : Faster pulsing of white color. * 1532 *=============================================================================================*/ 1533 void Color_Cycle(void) 1534 { 1535 static CDTimerClass<SystemTimerClass> _timer; 1536 static CDTimerClass<SystemTimerClass> _ftimer; 1537 static bool _up = false; 1538 static int val = 255; 1539 bool changed = false; 1540 1541 if (Options.IsPaletteScroll) { 1542 /* 1543 ** Process the fading white color. It is used for the radar box and other glowing 1544 ** game interface elements. 1545 */ 1546 if (!_ftimer) { 1547 _ftimer = TIMER_SECOND/6; 1548 1549 #define STEP_RATE 20 1550 if (_up) { 1551 val += STEP_RATE; 1552 if (val > 150) { 1553 val = 150; 1554 _up = false; 1555 } 1556 } else { 1557 val -= STEP_RATE; 1558 if (val < 0x20) { 1559 val = 0x20; 1560 _up = true; 1561 } 1562 } 1563 1564 /* 1565 ** Set the pulse color as the proportional value between white and the 1566 ** minimum value for pulsing. 1567 */ 1568 InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE]; 1569 InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor); 1570 1571 /* 1572 ** Pulse the glowing embers between medium and dark red. 1573 */ 1574 InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80); 1575 InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor); 1576 1577 changed = true; 1578 } 1579 1580 /* 1581 ** Process the color cycling effects -- water. 1582 */ 1583 if (!_timer) { 1584 _timer = TIMER_SECOND/4; 1585 1586 RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1]; 1587 for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) { 1588 InGamePalette[index] = InGamePalette[index-1]; 1589 } 1590 InGamePalette[CYCLE_COLOR_START] = first; 1591 1592 changed = true; 1593 } 1594 1595 /* 1596 ** If any of the processing functions changed the palette, then this palette must be 1597 ** passed to the system. 1598 */ 1599 if (changed) { 1600 BStart(BENCH_PALETTE); 1601 InGamePalette.Set(); 1602 // Set_Palette(InGamePalette); 1603 BEnd(BENCH_PALETTE); 1604 } 1605 } 1606 } 1607 1608 1609 /*********************************************************************************************** 1610 * Call_Back -- Main game maintenance callback routine. * 1611 * * 1612 * This routine handles all the "real time" processing that needs to * 1613 * occur. This includes palette fading and sound updating. It needs * 1614 * to be called as often as possible. * 1615 * * 1616 * INPUT: none * 1617 * * 1618 * OUTPUT: none * 1619 * * 1620 * WARNINGS: none * 1621 * * 1622 * HISTORY: * 1623 * 10/07/1992 JLB : Created. * 1624 *=============================================================================================*/ 1625 void Call_Back(void) 1626 { 1627 /* 1628 ** Music and speech maintenance 1629 */ 1630 if (SampleType) { 1631 Sound_Callback(); 1632 Theme.AI(); 1633 Speak_AI(); 1634 } 1635 1636 /* 1637 ** Network maintenance. 1638 */ 1639 if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { 1640 IPX_Call_Back(); 1641 } 1642 1643 /* 1644 ** Serial game maintenance. 1645 */ 1646 if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) { 1647 //NullModem.Service(); ST - 5/7/2019 1648 } 1649 1650 #ifdef WOLAPI_INTEGRATION 1651 // Wolapi maintenance. 1652 if( pWolapi ) 1653 { 1654 if( pWolapi->bInGame ) 1655 { 1656 if( !pWolapi->bConnectionDown && ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) 1657 { 1658 pWolapi->pChat->PumpMessages(); 1659 pWolapi->pNetUtil->PumpMessages(); 1660 pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT + 700; // Slower pump during games. 1661 if( pWolapi->bConnectionDown ) 1662 { 1663 // Connection to server lost. 1664 Session.Messages.Add_Message( NULL, 0, TXT_WOL_WOLAPIGONE, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); 1665 Sound_Effect( WOLSOUND_LOGOUT ); 1666 // ajw (Wolapi object is now left around, so we can try to send game results.) 1667 // // Kill wolapi. 1668 // pWolapi->UnsetupCOMStuff(); 1669 // delete pWolapi; 1670 // pWolapi = NULL; 1671 } 1672 } 1673 } 1674 else 1675 { 1676 // When showing a modal dialog during chat, this pumping is turned on. It's turned off immediately following. 1677 if( pWolapi->bPump_In_Call_Back && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) 1678 { 1679 pWolapi->pChat->PumpMessages(); 1680 pWolapi->pNetUtil->PumpMessages(); 1681 pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; 1682 } 1683 } 1684 } 1685 #endif 1686 1687 #if(TEN) 1688 if (Session.Type == GAME_TEN) { 1689 TEN_Call_Back(); 1690 } 1691 #endif // TEN 1692 1693 #if(MPATH) 1694 if (Session.Type == GAME_MPATH) { 1695 MPATH_Call_Back(); 1696 } 1697 #endif // MPATH 1698 } 1699 1700 1701 void IPX_Call_Back(void) 1702 { 1703 #if (0)//PG 1704 Ipx.Service(); 1705 1706 /* 1707 ** Read packets only if the game is "closed", so we don't steal global 1708 ** messages from the connection dialogs. 1709 */ 1710 if (!Session.NetOpen) { 1711 if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) { 1712 1713 if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { 1714 1715 /* 1716 ** If this is another player signing off, remove the connection & 1717 ** mark that player's house as non-human, so the computer will take 1718 ** it over. 1719 */ 1720 if (Session.GPacket.Command == NET_SIGN_OFF) { 1721 for (int i = 0; i < Ipx.Num_Connections(); i++) { 1722 1723 int id = Ipx.Connection_ID(i); 1724 1725 if (Session.GAddress == (*Ipx.Connection_Address(id))) { 1726 Destroy_Connection(id, 0); 1727 } 1728 } 1729 } else { 1730 1731 /* 1732 ** Process a message from another user. 1733 */ 1734 1735 if (Session.GPacket.Command == NET_MESSAGE) { 1736 bool msg_ok = false; 1737 1738 /* 1739 ** If NetProtect is set, make sure this message came from within 1740 ** this game. 1741 */ 1742 if (!Session.NetProtect) { 1743 msg_ok = true; 1744 } else { 1745 if (Session.GPacket.Message.NameCRC == 1746 Compute_Name_CRC(Session.GameName)) { 1747 msg_ok = true; 1748 } else { 1749 msg_ok = false; 1750 } 1751 } 1752 1753 if (msg_ok) { 1754 if (!Session.Messages.Concat_Message(Session.GPacket.Name, 1755 Session.GPacket.Message.Color, 1756 Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { 1757 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1758 if (NewUnitsEnabled && !strncmp(Session.GPacket.Message.Buf,"XECRET UNITS ON ",15)) { 1759 Session.GPacket.Message.Buf[0]='S'; 1760 Enable_Secret_Units(); 1761 } 1762 #endif 1763 Session.Messages.Add_Message (Session.GPacket.Name, 1764 Session.GPacket.Message.Color, 1765 Session.GPacket.Message.Buf, 1766 Session.GPacket.Message.Color, 1767 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | 1768 TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 1769 1770 Sound_Effect(VOC_INCOMING_MESSAGE); 1771 } 1772 1773 /* 1774 ** Tell the map to do a partial update (just to force the messages 1775 ** to redraw). 1776 */ 1777 Map.Flag_To_Redraw(true); 1778 1779 /* 1780 ** Save this message in our last-message buffer 1781 */ 1782 strcpy(Session.LastMessage, Session.GPacket.Message.Buf); 1783 } 1784 } else { 1785 Process_Global_Packet(&Session.GPacket, &Session.GAddress); 1786 } 1787 } 1788 } 1789 } 1790 } 1791 #endif 1792 } 1793 1794 1795 #if(TEN) 1796 void TEN_Call_Back(void) 1797 { 1798 int id; 1799 1800 Ten->Service(); 1801 1802 if (Ten->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, 1803 &Session.TenAddress)) { 1804 1805 // 1806 // If this is another player signing off, remove the connection & 1807 // mark that player's house as non-human, so the computer will take 1808 // it over. 1809 // 1810 if (Session.GPacket.Command == NET_SIGN_OFF) { 1811 for (int i = 0; i < Ten->Num_Connections(); i++) { 1812 1813 id = Ten->Connection_ID(i); 1814 1815 if (Session.TenAddress == Ten->Connection_Address(id)) { 1816 Destroy_TEN_Connection(id, 0); 1817 } 1818 } 1819 } 1820 1821 // 1822 // Process a message from another user. 1823 // 1824 else if (Session.GPacket.Command == NET_MESSAGE) { 1825 if (!Session.Messages.Concat_Message(Session.GPacket.Name, 1826 Session.GPacket.Message.Color, 1827 Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { 1828 1829 Session.Messages.Add_Message (Session.GPacket.Name, 1830 Session.GPacket.Message.Color, 1831 Session.GPacket.Message.Buf, 1832 Session.GPacket.Message.Color, 1833 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | 1834 TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 1835 1836 Sound_Effect(VOC_INCOMING_MESSAGE); 1837 1838 /* 1839 ** Tell the map to do a partial update (just to force the messages 1840 ** to redraw). 1841 */ 1842 Map.Flag_To_Redraw(true); 1843 1844 /* 1845 ** Save this message in our last-message buffer 1846 */ 1847 strcpy(Session.LastMessage, Session.GPacket.Message.Buf); 1848 } 1849 } 1850 } 1851 } 1852 #endif // TEN 1853 1854 1855 #if(MPATH) 1856 void MPATH_Call_Back(void) 1857 { 1858 int id; 1859 1860 MPath->Service(); 1861 1862 if (MPath->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, 1863 &Session.MPathAddress)) { 1864 1865 // 1866 // If this is another player signing off, remove the connection & 1867 // mark that player's house as non-human, so the computer will take 1868 // it over. 1869 // 1870 if (Session.GPacket.Command == NET_SIGN_OFF) { 1871 for (int i = 0; i < MPath->Num_Connections(); i++) { 1872 1873 id = MPath->Connection_ID(i); 1874 1875 if (Session.MPathAddress == MPath->Connection_Address(id)) { 1876 Destroy_MPATH_Connection(id, 0); 1877 } 1878 } 1879 } 1880 1881 // 1882 // Process a message from another user. 1883 // 1884 else if (Session.GPacket.Command == NET_MESSAGE) { 1885 if (!Session.Messages.Concat_Message(Session.GPacket.Name, 1886 Session.GPacket.Message.Color, 1887 Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { 1888 1889 Session.Messages.Add_Message (Session.GPacket.Name, 1890 Session.GPacket.Message.Color, 1891 Session.GPacket.Message.Buf, 1892 Session.GPacket.Message.Color, 1893 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | 1894 TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); 1895 1896 Sound_Effect(VOC_INCOMING_MESSAGE); 1897 1898 /* 1899 ** Tell the map to do a partial update (just to force the messages 1900 ** to redraw). 1901 */ 1902 Map.Flag_To_Redraw(true); 1903 1904 /* 1905 ** Save this message in our last-message buffer 1906 */ 1907 strcpy(Session.LastMessage, Session.GPacket.Message.Buf); 1908 } 1909 } 1910 } 1911 } 1912 #endif // MPATH 1913 1914 1915 /*********************************************************************************************** 1916 * Language_Name -- Build filename for current language. * 1917 * * 1918 * This routine attaches a language specific suffix to the base * 1919 * filename provided. Typical use of this is when loading language * 1920 * specific files at game initialization time. * 1921 * * 1922 * INPUT: basename -- Base name to append language specific * 1923 * extension to. * 1924 * * 1925 * OUTPUT: Returns with pointer to completed filename. * 1926 * * 1927 * WARNINGS: The return pointer value is valid only until the next time * 1928 * this routine is called. * 1929 * * 1930 * HISTORY: * 1931 * 10/07/1992 JLB : Created. * 1932 *=============================================================================================*/ 1933 char const * Language_Name(char const * basename) 1934 { 1935 static char _fullname[_MAX_FNAME+_MAX_EXT]; 1936 1937 if (!basename) return(NULL); 1938 1939 sprintf(_fullname, "%s.ENG", basename); 1940 return(_fullname); 1941 } 1942 1943 1944 /*********************************************************************************************** 1945 * Source_From_Name -- Converts ASCII name into SourceType. * 1946 * * 1947 * This routine is used to convert an ASCII name representing a * 1948 * SourceType into the actual SourceType value. Typically, this is * 1949 * used when processing the scenario INI file. * 1950 * * 1951 * INPUT: name -- The ASCII source name to process. * 1952 * * 1953 * OUTPUT: Returns with the SourceType represented by the name * 1954 * specified. * 1955 * * 1956 * WARNINGS: none * 1957 * * 1958 * HISTORY: * 1959 * 04/17/1994 JLB : Created. * 1960 *=============================================================================================*/ 1961 SourceType Source_From_Name(char const * name) 1962 { 1963 if (name) { 1964 for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { 1965 if (stricmp(SourceName[source], name) == 0) { 1966 return(source); 1967 } 1968 } 1969 } 1970 return(SOURCE_NONE); 1971 } 1972 1973 1974 /*********************************************************************************************** 1975 * Name_From_Source -- retrieves the name for the given SourceType * 1976 * * 1977 * INPUT: * 1978 * source SourceType to get the name for * 1979 * * 1980 * OUTPUT: * 1981 * name of SourceType * 1982 * * 1983 * WARNINGS: * 1984 * none. * 1985 * * 1986 * HISTORY: * 1987 * 11/15/1994 BR : Created. * 1988 *=============================================================================================*/ 1989 char const * Name_From_Source(SourceType source) 1990 { 1991 if ((unsigned)source < SOURCE_COUNT) { 1992 return(SourceName[source]); 1993 } 1994 return("None"); 1995 } 1996 1997 1998 /*********************************************************************************************** 1999 * Theater_From_Name -- Converts ASCII name into a theater number. * 2000 * * 2001 * This routine converts an ASCII representation of a theater and converts it into a * 2002 * matching theater number. If no match was found, then THEATER_NONE is returned. * 2003 * * 2004 * INPUT: name -- Pointer to ASCII name to convert. * 2005 * * 2006 * OUTPUT: Returns with the name converted into a theater number. * 2007 * * 2008 * WARNINGS: none * 2009 * * 2010 * HISTORY: * 2011 * 10/01/1994 JLB : Created. * 2012 *=============================================================================================*/ 2013 TheaterType Theater_From_Name(char const * name) 2014 { 2015 TheaterType index; 2016 2017 if (name) { 2018 for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { 2019 if (stricmp(name, Theaters[index].Name) == 0) { 2020 return(index); 2021 } 2022 } 2023 } 2024 return(THEATER_NONE); 2025 } 2026 2027 2028 /*********************************************************************************************** 2029 * KN_To_Facing -- Converts a keyboard input number into a facing value. * 2030 * * 2031 * This routine determine which compass direction is represented by the keyboard value * 2032 * provided. It is used for map scrolling and other directional control operations from * 2033 * the keyboard. * 2034 * * 2035 * INPUT: input -- The KN number to convert. * 2036 * * 2037 * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * 2038 * not be translated, then FACING_NONE is returned. * 2039 * * 2040 * WARNINGS: none * 2041 * * 2042 * HISTORY: * 2043 * 05/28/1994 JLB : Created. * 2044 *=============================================================================================*/ 2045 FacingType KN_To_Facing(int input) 2046 { 2047 input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); 2048 switch (input) { 2049 case KN_LEFT: 2050 return(FACING_W); 2051 2052 case KN_RIGHT: 2053 return(FACING_E); 2054 2055 case KN_UP: 2056 return(FACING_N); 2057 2058 case KN_DOWN: 2059 return(FACING_S); 2060 2061 case KN_UPLEFT: 2062 return(FACING_NW); 2063 2064 case KN_UPRIGHT: 2065 return(FACING_NE); 2066 2067 case KN_DOWNLEFT: 2068 return(FACING_SW); 2069 2070 case KN_DOWNRIGHT: 2071 return(FACING_SE); 2072 2073 default: 2074 break; 2075 } 2076 return(FACING_NONE); 2077 } 2078 2079 2080 /*********************************************************************************************** 2081 * Sync_Delay -- Forces the game into a 15 FPS rate. * 2082 * * 2083 * This routine will wait until the timer for the current frame has expired before * 2084 * returning. It is called at the end of every game loop in order to force the game loop * 2085 * to run at a fixed rate. * 2086 * * 2087 * INPUT: none * 2088 * * 2089 * OUTPUT: none * 2090 * * 2091 * WARNINGS: This routine will delay an amount of time according to the game speed setting. * 2092 * * 2093 * HISTORY: * 2094 * 01/04/1995 JLB : Created. * 2095 * 03/06/1995 JLB : Fixed. * 2096 *=============================================================================================*/ 2097 static void Sync_Delay(void) 2098 { 2099 /* 2100 ** Accumulate the number of 'spare' ticks that are frittered away here. 2101 */ 2102 SpareTicks += FrameTimer; 2103 2104 /* 2105 ** Delay until the frame timer expires. This forces the game loop to be regulated to a 2106 ** speed controlled by the game options slider. 2107 */ 2108 while (FrameTimer) { 2109 Color_Cycle(); 2110 Call_Back(); 2111 2112 if (SpecialDialog == SDLG_NONE) { 2113 #ifdef WIN32 2114 WWMouse->Erase_Mouse(&HidPage, TRUE); 2115 #endif //WIN32 2116 KeyNumType input = KN_NONE; 2117 int x, y; 2118 Map.Input(input, x, y); 2119 if (input) { 2120 Keyboard_Process(input); 2121 } 2122 Map.Render(); 2123 } 2124 } 2125 Color_Cycle(); 2126 Call_Back(); 2127 } 2128 2129 2130 /*********************************************************************************************** 2131 * Main_Loop -- This is the main game loop (as a single loop). * 2132 * * 2133 * This function will perform one game loop. * 2134 * * 2135 * INPUT: none * 2136 * * 2137 * OUTPUT: bool; Should the game end? * 2138 * * 2139 * WARNINGS: none * 2140 * * 2141 * HISTORY: * 2142 * 10/01/1994 JLB : Created. * 2143 *=============================================================================================*/ 2144 #ifdef WIN32 2145 extern void Check_For_Focus_Loss(void); 2146 void Reallocate_Big_Shape_Buffer(void); 2147 #endif //WIN32 2148 2149 2150 bool Main_Loop() 2151 { 2152 KeyNumType input; // Player input. 2153 int x; 2154 int y; 2155 int framedelay; 2156 2157 Mono_Set_Cursor(0,0); 2158 2159 if (!GameActive) return(!GameActive); 2160 2161 #ifdef WIN32 2162 /* 2163 ** Call the focus loss handler 2164 */ 2165 Check_For_Focus_Loss(); 2166 2167 /* 2168 ** Allocate extra memory for uncompressed shapes as needed 2169 */ 2170 Reallocate_Big_Shape_Buffer(); 2171 #endif 2172 2173 /* 2174 ** Sync-bug trapping code 2175 */ 2176 if (Frame >= Session.TrapFrame) { 2177 Session.Trap_Object(); 2178 } 2179 2180 // 2181 // Initialize our AI processing timer 2182 // 2183 Session.ProcessTimer = TickCount; 2184 2185 #if 1 2186 if (Session.TrapCheckHeap) { 2187 Debug_Trap_Check_Heap = true; 2188 } 2189 #endif 2190 2191 #ifdef CHEAT_KEYS 2192 2193 /* 2194 ** Update the running status debug display. 2195 */ 2196 Self_Regulate(); 2197 #endif 2198 2199 BStart(BENCH_GAME_FRAME); 2200 2201 /* 2202 ** If there is no theme playing, but it looks like one is required, then start one 2203 ** playing. This is usually the symptom of there being no transition score. 2204 */ 2205 if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { 2206 Theme.Queue_Song(THEME_PICK_ANOTHER); 2207 } 2208 2209 /* 2210 ** Setup the timer so that the Main_Loop function processes at the correct rate. 2211 */ 2212 if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && 2213 Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { 2214 2215 // 2216 // In playback mode, run as fast as possible. 2217 // 2218 if (Session.Play) { 2219 FrameTimer = 0; 2220 } else { 2221 #ifdef FIXIT_VERSION_3 2222 if( !Session.DesiredFrameRate ) 2223 Session.DesiredFrameRate = 60; // A division by zero was happening (very rare). 2224 #endif 2225 framedelay = TIMER_SECOND / Session.DesiredFrameRate; 2226 FrameTimer = framedelay; 2227 } 2228 } else { 2229 if (Options.GameSpeed != 0) { 2230 FrameTimer = Options.GameSpeed + 2231 (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) - 2232 (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0); 2233 } else { 2234 FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0); 2235 } 2236 } 2237 2238 /* 2239 ** Update the display, unless we're inside a dialog. 2240 */ 2241 if (!Session.Play) { 2242 #ifdef WIN32 2243 if (SpecialDialog == SDLG_NONE && GameInFocus) { 2244 WWMouse->Erase_Mouse(&HidPage, TRUE); 2245 #else 2246 if (SpecialDialog == SDLG_NONE) { 2247 #endif 2248 Map.Input(input, x, y); 2249 if (input) { 2250 Keyboard_Process(input); 2251 } 2252 Map.Render(); 2253 } 2254 } 2255 2256 /* 2257 ** Save map's position & selected objects, if we're recording the game. 2258 */ 2259 if (Session.Record || Session.Play) { 2260 Do_Record_Playback(); 2261 } 2262 2263 #ifndef SORTDRAW 2264 /* 2265 ** Sort the map's ground layer by y-coordinate value. This is done 2266 ** outside the IsToRedraw check, for the purposes of game sync'ing 2267 ** between machines; this way, all machines will sort the Map's 2268 ** layer in the same way, and any processing done that's based on 2269 ** the order of this layer will remain in sync. 2270 */ 2271 DisplayClass::Layer[LAYER_GROUND].Sort(); 2272 #endif 2273 2274 /* 2275 ** AI logic operations are performed here. 2276 */ 2277 Logic.AI(); 2278 TimeQuake = false; 2279 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2280 if (!PendingTimeQuake) { 2281 TimeQuakeCenter = 0; 2282 } 2283 #endif 2284 2285 /* 2286 ** Manage the inter-player message list. If Manage() returns true, it means 2287 ** a message has expired & been removed, and the entire map must be updated. 2288 */ 2289 if (Session.Messages.Manage()) { 2290 #ifdef WIN32 2291 HiddenPage.Clear(); 2292 #else //WIN32 2293 HidPage.Clear(); 2294 #endif //WIN32 2295 Map.Flag_To_Redraw(true); 2296 } 2297 2298 // 2299 // Measure how long it took to process the AI 2300 // 2301 Session.ProcessTicks += (TickCount - Session.ProcessTimer); 2302 Session.ProcessFrames++; 2303 2304 /* 2305 ** Process all commands that are ready to be processed. 2306 */ 2307 Queue_AI(); 2308 2309 /* 2310 ** Keep track of elapsed time in the game. 2311 */ 2312 Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; 2313 2314 Call_Back(); 2315 2316 /* 2317 ** Check for player wins or loses according to global event flag. 2318 */ 2319 if (PlayerWins) { 2320 2321 #ifdef WIN32 2322 2323 /* 2324 ** Send the game statistics to WChat. 2325 */ 2326 if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { 2327 Register_Game_End_Time(); 2328 Send_Statistics_Packet(); // Player just won. 2329 } 2330 2331 WWMouse->Erase_Mouse(&HidPage, TRUE); 2332 #endif //WIN32 2333 PlayerLoses = false; 2334 PlayerWins = false; 2335 PlayerRestarts = false; 2336 Map.Help_Text(TXT_NONE); 2337 Do_Win(); 2338 return(!GameActive); 2339 } 2340 if (PlayerLoses) { 2341 #ifdef WIN32 2342 /* 2343 ** Send the game statistics to WChat. 2344 */ 2345 if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { 2346 Register_Game_End_Time(); 2347 Send_Statistics_Packet(); // Player just lost. 2348 } 2349 2350 WWMouse->Erase_Mouse(&HidPage, TRUE); 2351 #endif //WIN32 2352 PlayerWins = false; 2353 PlayerLoses = false; 2354 PlayerRestarts = false; 2355 Map.Help_Text(TXT_NONE); 2356 Do_Lose(); 2357 return(!GameActive); 2358 } 2359 if (PlayerRestarts) { 2360 #ifdef WIN32 2361 WWMouse->Erase_Mouse(&HidPage, TRUE); 2362 #endif //WIN32 2363 PlayerWins = false; 2364 PlayerLoses = false; 2365 PlayerRestarts = false; 2366 Map.Help_Text(TXT_NONE); 2367 Do_Restart(); 2368 return(!GameActive); 2369 } 2370 2371 #ifdef FIXIT_VERSION_3 // Stalemate games. 2372 if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 && 2373 Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) 2374 { 2375 // End game in a draw. 2376 if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { 2377 Register_Game_End_Time(); 2378 Send_Statistics_Packet(); 2379 } 2380 WWMouse->Erase_Mouse(&HidPage, TRUE); 2381 Map.Help_Text(TXT_NONE); 2382 Do_Draw(); 2383 return(!GameActive); 2384 } 2385 #endif 2386 2387 /* 2388 ** The frame logic has been completed. Increment the frame 2389 ** counter. 2390 */ 2391 Frame++; 2392 2393 /* 2394 ** Is there a memory trasher altering the map?? 2395 */ 2396 if (Debug_Check_Map) { 2397 if (!Map.Validate()) { 2398 if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) { 2399 GameActive = 0; 2400 } 2401 Map.Validate(); // give debugger a chance to catch it 2402 } 2403 } 2404 2405 2406 #if (0) //ST - 5/8/2019 2407 #ifdef WIN32 2408 if (Debug_MotionCapture) { 2409 static void ** _array = 0; 2410 static int _sequence = 0; 2411 static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE; 2412 2413 if (_array == NULL) { 2414 _array = new void * [_seqsize]; 2415 memset(_array, '\0', _seqsize * sizeof(void*)); 2416 } 2417 2418 if (_array == NULL) { 2419 Debug_MotionCapture = false; 2420 } 2421 2422 static GraphicBufferClass temp_page( SeenBuff.Get_Width(), 2423 SeenBuff.Get_Height(), 2424 NULL, 2425 SeenBuff.Get_Width() * SeenBuff.Get_Height()); 2426 2427 int size = SeenBuff.Get_Width() * SeenBuff.Get_Height(); 2428 2429 if (_sequence < _seqsize) { 2430 if (_array[_sequence] == NULL) { 2431 _array[_sequence] = new char[size]; 2432 } 2433 2434 if (_array[_sequence] != NULL) { 2435 SeenBuff.Blit(temp_page); 2436 memmove(_array[_sequence], temp_page.Get_Buffer(), size); 2437 } 2438 _sequence++; 2439 2440 } else { 2441 Debug_MotionCapture = false; 2442 2443 CDFileClass file; 2444 file.Cache(200000); 2445 char filename[30]; 2446 2447 for (int index = 0; index < _sequence; index++) { 2448 memmove(temp_page.Get_Buffer(), _array[index], size); 2449 sprintf(filename, "cap%04d.pcx", index); 2450 file.Set_Name(filename); 2451 2452 Write_PCX_File(file, temp_page, & GamePalette); 2453 } 2454 2455 _sequence = 0; 2456 } 2457 } 2458 #endif 2459 #endif 2460 BEnd(BENCH_GAME_FRAME); 2461 2462 Sync_Delay(); 2463 return(!GameActive); 2464 } 2465 2466 2467 #ifdef SCENARIO_EDITOR 2468 /*************************************************************************** 2469 * Map_Edit_Loop -- a mini-main loop for map edit mode only * 2470 * * 2471 * INPUT: * 2472 * * 2473 * OUTPUT: * 2474 * * 2475 * WARNINGS: * 2476 * * 2477 * HISTORY: * 2478 * 10/19/1994 BR : Created. * 2479 *=========================================================================*/ 2480 bool Map_Edit_Loop(void) 2481 { 2482 /* 2483 ** Redraw the map. 2484 */ 2485 Map.Render(); 2486 2487 /* 2488 ** Get user input (keys, mouse clicks). 2489 */ 2490 KeyNumType input; 2491 2492 #ifdef WIN32 2493 WWMouse->Erase_Mouse(&HidPage, TRUE); 2494 #endif //WIN32 2495 2496 int x; 2497 int y; 2498 Map.Input(input, x, y); 2499 2500 /* 2501 ** Process keypress. 2502 */ 2503 if (input) { 2504 Keyboard_Process(input); 2505 } 2506 2507 Call_Back(); // maintains Theme.AI() for music 2508 Color_Cycle(); 2509 2510 return(!GameActive); 2511 } 2512 2513 2514 /*************************************************************************** 2515 * Go_Editor -- Enables/disables the map editor * 2516 * * 2517 * INPUT: * 2518 * flag true = go into editor mode; false = go into game mode * 2519 * * 2520 * OUTPUT: * 2521 * none. * 2522 * * 2523 * WARNINGS: * 2524 * none. * 2525 * * 2526 * HISTORY: * 2527 * 10/19/1994 BR : Created. * 2528 *=========================================================================*/ 2529 void Go_Editor(bool flag) 2530 { 2531 /* 2532 ** Go into Scenario Editor mode 2533 */ 2534 if (flag) { 2535 Debug_Map = true; 2536 Debug_Unshroud = true; 2537 2538 /* 2539 ** Un-select any selected objects 2540 */ 2541 Unselect_All(); 2542 2543 /* 2544 ** Turn off the sidebar if it's on 2545 */ 2546 Map.Activate(0); 2547 2548 /* 2549 ** Reset the map's Button list for the new mode 2550 */ 2551 Map.Init_IO(); 2552 2553 /* 2554 ** Force a complete redraw of the screen 2555 */ 2556 #ifdef WIN32 2557 HiddenPage.Clear(); 2558 #else 2559 HidPage.Clear(); 2560 #endif 2561 Map.Flag_To_Redraw(true); 2562 Map.Render(); 2563 2564 } else { 2565 2566 /* 2567 ** Go into normal game mode 2568 */ 2569 Debug_Map = false; 2570 Debug_Unshroud = false; 2571 2572 /* 2573 ** Un-select any selected objects 2574 */ 2575 Unselect_All(); 2576 2577 /* 2578 ** Reset the map's Button list for the new mode 2579 */ 2580 Map.Init_IO(); 2581 2582 /* 2583 ** Force a complete redraw of the screen 2584 */ 2585 HidPage.Clear(); 2586 Map.Flag_To_Redraw(true); 2587 Map.Render(); 2588 } 2589 } 2590 2591 #endif 2592 2593 #if (0) 2594 /*********************************************************************************************** 2595 * MixFileHandler -- Handles VQ file access. * 2596 * * 2597 * This routine is called from the VQ player when it needs to access the source file. By * 2598 * using this routine it is possible to virtualize the file system. * 2599 * * 2600 * INPUT: vqa -- Pointer to the VQA handle for this animation. * 2601 * * 2602 * action-- The requested action to perform. * 2603 * * 2604 * buffer-- Optional buffer pointer as needed by the type of action. * 2605 * * 2606 * nbytes-- The number of bytes (if needed) for this operation. * 2607 * * 2608 * OUTPUT: Returns a value consistent with the action requested. * 2609 * * 2610 * WARNINGS: none * 2611 * * 2612 * HISTORY: * 2613 * 07/04/1995 JLB : Created. * 2614 *=============================================================================================*/ 2615 long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes) 2616 { 2617 CCFileClass * file; 2618 long error; 2619 2620 file = (CCFileClass *)vqa->VQAio; 2621 2622 /* 2623 ** Perform the action specified by the stream command. 2624 */ 2625 switch (action) { 2626 2627 /* 2628 ** VQACMD_READ means read NBytes from the stream and place it in the 2629 ** memory pointed to by Buffer. 2630 ** 2631 ** Any error code returned will be remapped by VQA library into 2632 ** VQAERR_READ. 2633 */ 2634 case VQACMD_READ: 2635 error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); 2636 break; 2637 2638 /* 2639 ** VQACMD_WRITE is analogous to VQACMD_READ. 2640 ** 2641 ** Writing is not allowed to the VQA file, VQA library will remap the 2642 ** error into VQAERR_WRITE. 2643 */ 2644 case VQACMD_WRITE: 2645 error = 1; 2646 break; 2647 2648 /* 2649 ** VQACMD_SEEK asks that you perform a seek relative to the current 2650 ** position. NBytes is a signed number, indicating seek direction 2651 ** (positive for forward, negative for backward). Buffer has no meaning 2652 ** here. 2653 ** 2654 ** Any error code returned will be remapped by VQA library into 2655 ** VQAERR_SEEK. 2656 */ 2657 case VQACMD_SEEK: 2658 error = (file->Seek(nbytes, SEEK_CUR) == -1); 2659 break; 2660 2661 /* 2662 ** VQACMD_OPEN asks that you open your stream for access. 2663 */ 2664 case VQACMD_OPEN: 2665 file = new CCFileClass((char *)buffer); 2666 2667 if (file != NULL && file->Is_Available()) { 2668 error = file->Open((char *)buffer, READ); 2669 2670 if (error != -1) { 2671 vqa->VQAio = (unsigned long)file; 2672 error = 0; 2673 } else { 2674 delete file; 2675 file = 0; 2676 error = 1; 2677 } 2678 } else { 2679 error = 1; 2680 } 2681 break; 2682 2683 case VQACMD_CLOSE: 2684 file->Close(); 2685 delete file; 2686 file = 0; 2687 vqa->VQAio = 0; 2688 error = 0; 2689 break; 2690 2691 /* 2692 ** VQACMD_INIT means to prepare your stream for reading. This is used for 2693 ** certain streams that can't be read immediately upon opening, and need 2694 ** further preparation. This operation is allowed to fail; the error code 2695 ** will be returned directly to the client. 2696 */ 2697 case VQACMD_INIT: 2698 2699 /* 2700 ** IFFCMD_CLEANUP means to terminate the transaction with the associated 2701 ** stream. This is used for streams that can't simply be closed. This 2702 ** operation is not allowed to fail; any error returned will be ignored. 2703 */ 2704 case VQACMD_CLEANUP: 2705 error = 0; 2706 break; 2707 2708 default: 2709 error = 0; 2710 break; 2711 } 2712 2713 return(error); 2714 } 2715 #endif 2716 2717 void Rebuild_Interpolated_Palette(unsigned char * interpal) 2718 { 2719 for (int y=0; y<255; y++) { 2720 for (int x=y+1; x<256; x++) { 2721 *(interpal + (y*256+x)) = *(interpal + (x*256+y)); 2722 } 2723 } 2724 } 2725 2726 2727 unsigned char * InterpolatedPalettes[100]; 2728 BOOL PalettesRead; 2729 unsigned PaletteCounter; 2730 2731 2732 /*********************************************************************************************** 2733 * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs * 2734 * * 2735 * * 2736 * * 2737 * INPUT: Name of palette file * 2738 * * 2739 * OUTPUT: Number of palettes loaded * 2740 * * 2741 * WARNINGS: None * 2742 * * 2743 * HISTORY: * 2744 * 5/7/96 9:49AM ST : Created * 2745 *=============================================================================================*/ 2746 int Load_Interpolated_Palettes(char const * filename, BOOL add) 2747 { 2748 int num_palettes=0; 2749 int i; 2750 int start_palette; 2751 2752 PalettesRead = FALSE; 2753 CCFileClass file(filename); 2754 2755 if (!add) { 2756 for (i=0; i < ARRAY_SIZE(InterpolatedPalettes); i++) { 2757 InterpolatedPalettes[i]=NULL; 2758 } 2759 start_palette=0; 2760 } else { 2761 for (start_palette = 0; start_palette < ARRAY_SIZE(InterpolatedPalettes); start_palette++) { 2762 if (!InterpolatedPalettes[start_palette]) break; 2763 } 2764 } 2765 2766 /* 2767 ** Hack another interpolated palette if the requested one is 2768 ** not present. 2769 */ 2770 if (!file.Is_Available()) { 2771 file.Set_Name("AAGUN.VQP"); 2772 } 2773 2774 if (file.Is_Available()) { 2775 2776 file.Open(READ); 2777 file.Read(&num_palettes , 4); 2778 2779 for (i=0; i < num_palettes; i++) { 2780 InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); 2781 memset (InterpolatedPalettes[i+start_palette], 0, 65536); 2782 for (int y = 0; y < 256; y++) { 2783 file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); 2784 } 2785 2786 Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); 2787 } 2788 2789 PalettesRead = TRUE; 2790 file.Close(); 2791 } 2792 PaletteCounter = 0; 2793 return (num_palettes); 2794 } 2795 2796 2797 void Free_Interpolated_Palettes(void) 2798 { 2799 for (int i = 0; i < ARRAY_SIZE(InterpolatedPalettes) ;i++) { 2800 if (InterpolatedPalettes[i]) { 2801 free(InterpolatedPalettes[i]); 2802 InterpolatedPalettes[i]=NULL; 2803 } 2804 } 2805 } 2806 2807 2808 /*********************************************************************************************** 2809 * Play_Movie -- Plays a VQ movie. * 2810 * * 2811 * Use this routine to play a VQ movie. It will dispatch the specified movie to the * 2812 * VQ player. The routine will not return until the movie has finished playing. * 2813 * * 2814 * INPUT: name -- The name of the movie file (sans ".VQA"). * 2815 * * 2816 * theme -- The identifier for an optional theme that should be played in the * 2817 * background while this VQ plays. * 2818 * * 2819 * clrscrn -- 'true' if to clear the screen when the movie is over * 2820 * * 2821 * OUTPUT: none * 2822 * * 2823 * WARNINGS: none * 2824 * * 2825 * HISTORY: * 2826 * 12/19/1994 JLB : Created. * 2827 *=============================================================================================*/ 2828 #ifdef WIN32 2829 extern void Suspend_Audio_Thread(void); 2830 extern void Resume_Audio_Thread(void); 2831 2832 #ifdef MOVIE640 2833 extern GraphicBufferClass VQ640; 2834 #endif 2835 #endif 2836 2837 extern void Play_Movie_GlyphX(const char * movie_name, ThemeType theme, bool immediate); 2838 2839 void Play_Movie(char const * name, ThemeType theme, bool clrscrn, bool immediate) 2840 { 2841 #if (1) 2842 if (strcmp(name, "x") == 0 || strcmp(name, "X") == 0) { 2843 return; 2844 } 2845 2846 Play_Movie_GlyphX(name, theme, immediate); 2847 return; 2848 #else 2849 #ifdef MPEGMOVIE 2850 //theme = theme; 2851 //clrscrn = clrscrn; 2852 if( Using_DVD() ) 2853 { 2854 if (PlayMpegMovie(name)) 2855 return; 2856 } 2857 #endif 2858 2859 #ifdef CHEAT_KEYS 2860 // Mono_Printf("Movie: %s\n", name); 2861 #endif //CHEAT_KEYS 2862 /* 2863 ** Don't play movies in editor mode 2864 */ 2865 if (Debug_Map) { 2866 return; 2867 } 2868 #ifdef CHEAT_KEYS 2869 // Mono_Printf("A\n"); 2870 #endif //CHEAT_KEYS 2871 /* 2872 ** Don't play movies in multiplayer mode 2873 */ 2874 if (Session.Type != GAME_NORMAL) { 2875 return; 2876 } 2877 #ifdef CHEAT_KEYS 2878 //Mono_Printf("b\n"); 2879 #endif //CHEAT_KEYS 2880 2881 if (name) { 2882 char fullname[_MAX_FNAME+_MAX_EXT]; 2883 _makepath(fullname, NULL, NULL, name, ".VQA"); 2884 #ifdef WIN32 2885 char palname [_MAX_FNAME+_MAX_EXT]; 2886 _makepath(palname , NULL, NULL, name, ".VQP"); 2887 #endif //WIN32 2888 #ifdef CHEAT_KEYS 2889 // Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); 2890 #endif 2891 2892 if (!CCFileClass(fullname).Is_Available()){ 2893 #ifdef CHEAT_KEYS 2894 // Mono_Printf("fullname: %s\n", fullname); 2895 #endif //CHEAT_KEYS 2896 return; 2897 } 2898 /* 2899 ** Reset the anim control structure. 2900 */ 2901 Anim_Init(); 2902 2903 /* 2904 ** Prepare to play a movie. First hide the mouse and stop any score that is playing. 2905 ** While the score (if any) is fading to silence, fade the palette to black as well. 2906 ** When the palette has finished fading, wait until the score has finished fading 2907 ** before launching the movie. 2908 */ 2909 Hide_Mouse(); 2910 Theme.Queue_Song(theme); 2911 if (PreserveVQAScreen == 0 && !clrscrn) { 2912 BlackPalette.Set(FADE_PALETTE_MEDIUM); 2913 VisiblePage.Clear(); 2914 BlackPalette.Adjust(0x08, WhitePalette); 2915 BlackPalette.Set(); 2916 BlackPalette.Adjust(0xFF); 2917 BlackPalette.Set(); 2918 } 2919 PreserveVQAScreen = 0; 2920 Keyboard->Clear(); 2921 2922 VQAHandle * vqa = NULL; 2923 2924 2925 #ifdef WIN32 2926 #ifdef MOVIE640 2927 if(IsVQ640) { 2928 AnimControl.ImageWidth = 640; 2929 AnimControl.ImageHeight = 400; 2930 AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); 2931 } else { 2932 AnimControl.ImageWidth = 320; 2933 AnimControl.ImageHeight = 200; 2934 AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); 2935 } 2936 #endif 2937 #endif 2938 2939 if (!Debug_Quiet && Get_Digi_Handle() != -1) { 2940 AnimControl.OptionFlags |= VQAOPTF_AUDIO; 2941 } else { 2942 AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; 2943 } 2944 2945 if ((vqa = VQA_Alloc()) != NULL) { 2946 VQA_Init(vqa, MixFileHandler); 2947 2948 if (VQA_Open(vqa, fullname, &AnimControl) == 0) { 2949 Brokeout = false; 2950 #ifdef WIN32 2951 //Suspend_Audio_Thread(); 2952 #ifdef MOVIE640 2953 if(!IsVQ640) { 2954 Load_Interpolated_Palettes(palname); 2955 } 2956 #else 2957 Load_Interpolated_Palettes(palname); 2958 #endif 2959 //Set_Palette(BlackPalette); 2960 SysMemPage.Clear(); 2961 InMovie = true; 2962 #endif //WIN32 2963 VQA_Play(vqa, VQAMODE_RUN); 2964 VQA_Close(vqa); 2965 #ifdef WIN32 2966 //Resume_Audio_Thread(); 2967 InMovie = FALSE; 2968 #ifdef MOVIE640 2969 if(!IsVQ640) { 2970 Free_Interpolated_Palettes(); 2971 } 2972 #else 2973 Free_Interpolated_Palettes(); 2974 #endif 2975 IsVQ640 = false; 2976 Set_Primary_Buffer_Format(); 2977 #endif //WIN32 2978 2979 /* 2980 ** Any movie that ends prematurely must have the screen 2981 ** cleared to avoid any unexpected palette glitches. 2982 */ 2983 if (Brokeout) { 2984 clrscrn = true; 2985 VisiblePage.Clear(); 2986 Brokeout = false; 2987 } 2988 } else { 2989 #ifndef NDEBUG 2990 bool error = true; 2991 assert(error); 2992 #endif 2993 } 2994 2995 VQA_Free(vqa); 2996 } else { 2997 assert(vqa != NULL); 2998 } 2999 #ifdef CHEAT_KEYS 3000 //Mono_Printf("d"); 3001 #endif //CHEAT_KEYS 3002 /* 3003 ** Presume that the screen is left in a garbage state as well as the palette 3004 ** being in an unknown condition. Recover from this by clearing the screen and 3005 ** forcing the palette to black. 3006 */ 3007 if (clrscrn) { 3008 VisiblePage.Clear(); 3009 BlackPalette.Adjust(0x08, WhitePalette); 3010 BlackPalette.Set(); 3011 BlackPalette.Adjust(0xFF); 3012 BlackPalette.Set(); 3013 } 3014 Show_Mouse(); 3015 } 3016 #endif 3017 } 3018 3019 3020 void Play_Movie(VQType name, ThemeType theme, bool clrscrn, bool immediate) 3021 { 3022 if (name != VQ_NONE) { 3023 if (name == VQ_REDINTRO) { 3024 IsVQ640 = true; 3025 } 3026 Play_Movie(VQName[name], theme, clrscrn, immediate); 3027 IsVQ640 = false; 3028 } 3029 } 3030 3031 3032 // Denzil 5/18/98 - Mpeg movie playback 3033 #ifdef MPEGMOVIE 3034 extern LPDIRECTDRAWPALETTE PalettePtr; 3035 3036 bool PlayMpegMovie(const char* name) 3037 { 3038 char path[MAX_PATH]; 3039 CCFileClass file; 3040 const char* filename; 3041 3042 #ifdef CHEAT_KEYS 3043 if( bNoMovies ) 3044 return true; 3045 #endif 3046 3047 sprintf(path, "movies\\%.8s.%.3s", name, "mpg"); 3048 filename = file.Set_Name(path); 3049 3050 if (!file.Is_Available()) 3051 { 3052 #if(1) 3053 VisiblePage.Clear(); 3054 GamePalette.Set(); 3055 Show_Mouse(); 3056 sprintf(path, "Couldn't find %s\n", filename); 3057 WWMessageBox().Process(path); 3058 #endif 3059 return false; 3060 } 3061 3062 // Stop theme music 3063 if (Misc_Focus_Loss_Function) 3064 Misc_Focus_Loss_Function(); 3065 3066 // Release primary surface 3067 VisiblePage.Un_Init(); 3068 3069 #ifdef MCIMPEG 3070 if (MciMovie && MpgSettings && (MpgSettings->GetDeviceName() != NULL)) 3071 { 3072 DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_NORMAL); 3073 3074 if (!MciMovie->Open(filename, MpgSettings->GetDeviceName())) 3075 { 3076 WWMessageBox().Process("Couldn't open movie.\n"); 3077 } 3078 else if (!MciMovie->Play(MainWindow)) 3079 { 3080 WWMessageBox().Process("Couldn't play movie.\n"); 3081 } 3082 3083 DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); 3084 } 3085 else 3086 #endif 3087 { 3088 DDSURFACEDESC ddsd; 3089 IDirectDrawSurface* primary = NULL; 3090 bool modeChange = false; 3091 RECT rect; 3092 3093 if (FAILED(DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 16))) 3094 { 3095 WWMessageBox().Process("Couldn't change display mode.\n"); 3096 } 3097 else 3098 { 3099 // Create primary surface reference 3100 memset(&ddsd, 0, sizeof(ddsd)); 3101 ddsd.dwSize = sizeof(ddsd); 3102 ddsd.dwFlags = DDSD_CAPS; 3103 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; 3104 3105 if (FAILED(DirectDrawObject->CreateSurface(&ddsd, &primary, NULL))) 3106 { 3107 WWMessageBox().Process("Couldn't create primary movie surface.\n"); 3108 } 3109 else 3110 { 3111 rect.top = rect.left = 0; 3112 rect.bottom = ScreenHeight; 3113 rect.right = ScreenWidth; 3114 3115 MpgSetCallback(MpegCallback, NULL); 3116 MpgPlay(filename, DirectDrawObject, primary, &rect); 3117 3118 if (primary) 3119 primary->Release(); 3120 3121 } 3122 3123 DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 8); 3124 } 3125 } 3126 3127 // Restore surfaces 3128 VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); 3129 PaletteSurface->SetPalette(PalettePtr); 3130 AllSurfaces.Set_Surface_Focus(true); 3131 AllSurfaces.Restore_Surfaces(); 3132 return true; 3133 } 3134 3135 3136 MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user) 3137 { 3138 static IDirectDrawPalette* _palette = NULL; 3139 3140 user = user; 3141 3142 switch (cmd) 3143 { 3144 case MPGCMD_ERROR: 3145 WWMessageBox().Process((char const *)data); 3146 break; 3147 3148 case MPGCMD_INIT: 3149 VisiblePage.Clear(); 3150 break; 3151 3152 case MPGCMD_CLEANUP: 3153 VisiblePage.Clear(); 3154 3155 if (_palette != NULL) 3156 { 3157 PaletteSurface->SetPalette(_palette); 3158 _palette->Release(); 3159 _palette = NULL; 3160 } 3161 break; 3162 3163 case MPGCMD_PALETTE: 3164 if (FAILED(PaletteSurface->GetPalette(&_palette))) 3165 { 3166 WWMessageBox().Process("Couldn't get primary palette.\n"); 3167 } 3168 else 3169 { 3170 if (FAILED(PaletteSurface->SetPalette((IDirectDrawPalette*)data))) 3171 { 3172 WWMessageBox().Process("Couldn't set movie palette.\n"); 3173 } 3174 } 3175 break; 3176 3177 case MPGCMD_UPDATE: 3178 if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) 3179 { 3180 if (Keyboard->Get() == KN_ESC) 3181 { 3182 Keyboard->Clear(); 3183 return MPGRES_QUIT; 3184 } 3185 3186 Keyboard->Clear(); 3187 } 3188 3189 if (!GameInFocus) 3190 { 3191 MpgPause(); 3192 3193 while (!GameInFocus) 3194 { 3195 Check_For_Focus_Loss(); 3196 } 3197 3198 MpgResume(); 3199 return MPGRES_LOSTFOCUS; 3200 } 3201 break; 3202 3203 default: 3204 break; 3205 } 3206 3207 return MPGRES_CONTINUE; 3208 } 3209 #endif 3210 3211 /*********************************************************************************************** 3212 * Unselect_All -- Causes all selected objects to become unselected. * 3213 * * 3214 * This routine will unselect all objects that are currently selected. * 3215 * * 3216 * INPUT: none * 3217 * * 3218 * OUTPUT: none * 3219 * * 3220 * WARNINGS: none * 3221 * * 3222 * HISTORY: * 3223 * 01/19/1995 JLB : Created. * 3224 *=============================================================================================*/ 3225 void Unselect_All(void) 3226 { 3227 //while (CurrentObject.Count()) { 3228 // CurrentObject[0]->Unselect(); 3229 //} 3230 3231 //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 3232 while (CurrentObject.Count()) { 3233 3234 int count_before = CurrentObject.Count(); 3235 CurrentObject[0]->Unselect(); 3236 3237 if (count_before <= CurrentObject.Count()) { 3238 GlyphX_Debug_Print("Unselect_All failed to remove an object"); 3239 CurrentObject.Delete(CurrentObject[0]); 3240 } 3241 } 3242 //End of change - JAS 6/28/2019 3243 3244 } 3245 3246 3247 void Unselect_All_Except(ObjectClass* object) 3248 { 3249 int index = 0; 3250 while (index < CurrentObject.Count()) { 3251 3252 if (CurrentObject[index] == object) { 3253 index++; 3254 continue; 3255 } 3256 3257 int count_before = CurrentObject.Count(); 3258 CurrentObject[index]->Unselect(); 3259 3260 if (count_before <= CurrentObject.Count()) { 3261 GlyphX_Debug_Print("Unselect_All failed to remove an object"); 3262 CurrentObject.Delete(CurrentObject[index]); 3263 } 3264 } 3265 } 3266 3267 3268 /*********************************************************************************************** 3269 * Fading_Table_Name -- Builds a theater specific fading table name. * 3270 * * 3271 * This routine builds a standard fading table name. This name is dependant on the theater * 3272 * being played, since each theater has its own palette. * 3273 * * 3274 * INPUT: base -- The base name of this fading table. The base name can be no longer than * 3275 * seven characters. * 3276 * * 3277 * theater -- The theater that this fading table is specific to. * 3278 * * 3279 * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * 3280 * valid until this function is called again. * 3281 * * 3282 * WARNINGS: none * 3283 * * 3284 * HISTORY: * 3285 * 01/19/1995 JLB : Created. * 3286 *=============================================================================================*/ 3287 char const * Fading_Table_Name(char const * base, TheaterType theater) 3288 { 3289 static char _buffer[_MAX_FNAME+_MAX_EXT]; 3290 char root[_MAX_FNAME]; 3291 3292 sprintf(root, "%1.1s%s", Theaters[theater].Root, base); 3293 _makepath(_buffer, NULL, NULL, root, ".MRF"); 3294 return(_buffer); 3295 } 3296 3297 3298 /*********************************************************************************************** 3299 * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * 3300 * * 3301 * INPUT: void const * shapefile - pointer to a key framed shapefile * 3302 * int shapenum - shape to extract from shapefile * 3303 * * 3304 * OUTPUT: void const * - 3/3 icon set of shape from file * 3305 * * 3306 * HISTORY: * 3307 * 04/12/1995 PWG : Created. * 3308 * 05/10/1995 JLB : Handles a null shapefile pointer. * 3309 *=============================================================================================*/ 3310 void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) 3311 { 3312 static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; 3313 static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; 3314 int lp,framelp; 3315 char pixel; 3316 3317 char * retval = NULL; 3318 char * buffer = NULL; 3319 3320 /* 3321 ** If there is no shape file, then there can be no radar icon imagery. 3322 */ 3323 if (!shapefile) return(NULL); 3324 3325 #if (0) 3326 CCPalette.Set(); 3327 Set_Logic_Page(SeenBuff); 3328 CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL); 3329 #endif 3330 3331 /* 3332 ** Get the pixel width and height of the frame we built. This will 3333 ** be used to extract icons and build pixels. 3334 */ 3335 int pixel_width = Get_Build_Frame_Width( shapefile ); 3336 int pixel_height = Get_Build_Frame_Height( shapefile ); 3337 3338 /* 3339 ** Find the width and height in icons, adjust these by half an 3340 ** icon because the artists may be sloppy and miss the edge of an 3341 ** icon one way or the other. 3342 */ 3343 int icon_width = (pixel_width + 12) / 24; 3344 int icon_height = (pixel_height + 12) / 24; 3345 3346 /* 3347 ** If we have been told to build as many frames as possible, then 3348 ** find out how many frames there are to build. 3349 */ 3350 if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); 3351 3352 /* 3353 ** Allocate a position to store our icons. If the alloc fails then 3354 ** we don't add these icons to the set. 3355 **/ 3356 buffer = new char[(icon_width * icon_height * 9 * frames)+2]; 3357 if (!buffer) return(NULL); 3358 3359 /* 3360 ** Save off the return value so that we can return it to the calling 3361 ** function. 3362 */ 3363 retval = (char *)buffer; 3364 *buffer++ = (char)icon_width; 3365 *buffer++ = (char)icon_height; 3366 int val = 24/zoomfactor; 3367 3368 for (framelp = 0; framelp < frames; framelp ++) { 3369 /* 3370 ** Build the current frame. If the frame can not be built then we 3371 ** just need to skip past this set of icons and try to build the 3372 ** next frame. 3373 */ 3374 #ifdef WIN32 3375 void * ptr; 3376 if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { 3377 ptr = Get_Shape_Header_Data(ptr); 3378 #else //WIN#@ 3379 if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) { 3380 #endif //WIN32 3381 3382 /* 3383 ** Loop through the icon width and the icon height building icons 3384 ** into the buffer pointer. When the getx or gety falls outside of 3385 ** the width and height of the shape, just insert transparent pixels. 3386 */ 3387 for (int icony = 0; icony < icon_height; icony ++) { 3388 for (int iconx = 0; iconx < icon_width; iconx ++) { 3389 #ifdef WIN32 3390 3391 for (int y = 0; y < zoomfactor; y++) { 3392 for (int x = 0; x < zoomfactor; x++) { 3393 int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); 3394 int gety = (icony * 24) + (y * val) + (zoomfactor / 2); 3395 if ((getx < pixel_width) && (gety < pixel_height)) { 3396 for (lp = 0; lp < 9; lp ++) { 3397 pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); 3398 3399 #else //WIN32 3400 for (int y = 0; y < 3; y++) { 3401 for (int x = 0; x < 3; x++) { 3402 int getx = (iconx * 24) + (x << 3) + 4; 3403 int gety = (icony * 24) + (y << 3) + 4; 3404 if ((getx < pixel_width) && (gety < pixel_height)) { 3405 for (lp = 0; lp < 9; lp ++) { 3406 pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); 3407 #endif //WIN32 3408 if (pixel == LTGREEN) pixel = 0; 3409 if (pixel) { 3410 break; 3411 } 3412 } 3413 *buffer++ = pixel; 3414 } else { 3415 *buffer++ = 0; 3416 } 3417 } 3418 } 3419 } 3420 } 3421 } else { 3422 buffer += icon_width * icon_height * 9; 3423 } 3424 } 3425 return(retval); 3426 } 3427 3428 3429 extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner); 3430 extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); 3431 void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation); 3432 3433 void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale, int width, int height) 3434 { 3435 if (window == WINDOW_VIRTUAL) { 3436 if (width == 0) width = Get_Build_Frame_Width(shapefile); 3437 if (height == 0) height = Get_Build_Frame_Height(shapefile); 3438 DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, NULL, HOUSE_NONE); 3439 return; 3440 } 3441 3442 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); 3443 } 3444 3445 void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale, char override_owner) 3446 { 3447 if (window == WINDOW_VIRTUAL) { 3448 int width = Get_Build_Frame_Width(shapefile); 3449 int height = Get_Build_Frame_Height(shapefile); 3450 DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, shape_file_name, override_owner); 3451 return; 3452 } 3453 3454 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); 3455 } 3456 3457 3458 void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) 3459 { 3460 if (window == WINDOW_VIRTUAL) { 3461 DLL_Draw_Pip_Intercept(object, shapenum); 3462 return; 3463 } 3464 3465 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); 3466 } 3467 3468 3469 /*********************************************************************************************** 3470 * CC_Draw_Shape -- Custom draw shape handler. * 3471 * * 3472 * All draw shape calls will route through this function. It handles all draws for * 3473 * C&C. Such draws always occur to the logical page and assume certain things about * 3474 * the parameters passed. * 3475 * * 3476 * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * 3477 * embedded shapes. * 3478 * * 3479 * shapenum -- The shape number within the shapefile that will be drawn. * 3480 * * 3481 * x,y -- The pixel coordinates to draw the shape. * 3482 * * 3483 * window -- The clipping window to use. * 3484 * * 3485 * flags -- The custom draw shape flags. This controls how the parameters * 3486 * are used (if any). * 3487 * * 3488 * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * 3489 * data table. * 3490 * * 3491 * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * 3492 * table. * 3493 * * 3494 * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). * 3495 * * 3496 * scale -- 24.8 fixed point scale factor. * 3497 * * 3498 * OUTPUT: none * 3499 * * 3500 * WARNINGS: none * 3501 * * 3502 * HISTORY: * 3503 * 02/21/1995 JLB : Created. * 3504 *=============================================================================================*/ 3505 void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) 3506 { 3507 int predoffset; 3508 #ifdef WIN32 3509 unsigned long shape_pointer; 3510 #endif //WIN32 3511 3512 3513 /* 3514 ** Special kludge for E3 to prevent crashes 3515 */ 3516 if ((flags & SHAPE_GHOST) && (!ghostdata)) { 3517 ghostdata = DisplayClass::SpecialGhost; 3518 } 3519 if ((flags & SHAPE_FADING) && (!fadingdata)) { 3520 fadingdata = DisplayClass::FadingShade; 3521 } 3522 3523 static unsigned char * _xbuffer = 0; 3524 3525 if (!_xbuffer) { 3526 _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; 3527 } 3528 3529 if (shapefile != NULL && shapenum != -1) { 3530 3531 int width = Get_Build_Frame_Width(shapefile); 3532 int height = Get_Build_Frame_Height(shapefile); 3533 3534 #ifdef NEVER 3535 /* 3536 ** Perform a quick clip check against the destination rectangle. 3537 */ 3538 if (flags & SHAPE_CENTER) { 3539 if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return; 3540 if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return; 3541 if (x+width/2 < 0) return; 3542 if (y+height/2 < 0) return; 3543 3544 } else { 3545 if (x >= WindowList[window][WINDOWWIDTH]) return; 3546 if (y >= WindowList[window][WINDOWHEIGHT]) return; 3547 if (x+width < 0) return; 3548 if (y+height < 0) return; 3549 } 3550 #endif 3551 3552 3553 #ifdef WIN32 3554 /* 3555 ** In WIn95, build shape returns a pointer to the shape not its size 3556 */ 3557 shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer); 3558 if (shape_pointer) { 3559 GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), 3560 WindowList[window][WINDOWX] + LogicPage->Get_XPos(), 3561 WindowList[window][WINDOWY] + LogicPage->Get_YPos(), 3562 WindowList[window][WINDOWWIDTH], 3563 WindowList[window][WINDOWHEIGHT]); 3564 unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer); 3565 3566 #else //WIN32 3567 if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) { 3568 GraphicViewPortClass draw_window(LogicPage, 3569 WindowList[window][WINDOWX], 3570 WindowList[window][WINDOWY], 3571 WindowList[window][WINDOWWIDTH], 3572 WindowList[window][WINDOWHEIGHT]); 3573 unsigned char * buffer = (unsigned char *)_ShapeBuffer; 3574 #endif //WIN32 3575 3576 UseOldShapeDraw = false; 3577 /* 3578 ** Rotation handler. 3579 */ 3580 if (rotation != DIR_N) { 3581 3582 /* 3583 ** Get the raw shape data without the new header and flag to use the old shape drawing 3584 */ 3585 UseOldShapeDraw = true; 3586 #ifdef WIN32 3587 buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer); 3588 #endif 3589 3590 if (Debug_Rotate) { 3591 #if (0)//PG 3592 GraphicBufferClass src(width, height, buffer); 3593 width *= 2; 3594 height *= 2; 3595 memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); 3596 GraphicBufferClass dst(width, height, _xbuffer); 3597 Rotate_Bitmap(&src, &dst, rotation); 3598 buffer = _xbuffer; 3599 #endif 3600 } else { 3601 3602 BitmapClass bm(width, height, buffer); 3603 width *= 2; 3604 height *= 2; 3605 memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); 3606 GraphicBufferClass gb(width, height, _xbuffer); 3607 TPoint2D pt(width/2, height/2); 3608 3609 gb.Scale_Rotate(bm, pt, 0x0100, (256-(rotation-64))); 3610 buffer = _xbuffer; 3611 } 3612 } 3613 3614 /* 3615 ** Special shadow drawing code (used for aircraft and bullets). 3616 */ 3617 if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { 3618 flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); 3619 flags = flags | SHAPE_GHOST; 3620 ghostdata = DisplayClass::SpecialGhost; 3621 } 3622 3623 predoffset = Frame; 3624 3625 if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { 3626 predoffset = -predoffset; 3627 } 3628 3629 if (draw_window.Lock()) { 3630 if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { 3631 Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); 3632 } else { 3633 if (flags & SHAPE_FADING) { 3634 Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); 3635 } else { 3636 if (flags & SHAPE_PREDATOR) { 3637 Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset); 3638 } else { 3639 Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); 3640 } 3641 } 3642 } 3643 draw_window.Unlock(); 3644 } 3645 } 3646 } 3647 } 3648 3649 3650 /*********************************************************************************************** 3651 * Shape_Dimensions -- Determine the minimum rectangle for the shape. * 3652 * * 3653 * This routine will calculate (using brute forced) the minimum rectangle that will * 3654 * enclose the pixels of the shape. This rectangle will be relative to the upper left * 3655 * corner of the maximum shape size. By using this minimum rectangle, it is possible to * 3656 * greatly optimize the map 'dirty rectangle' logic. * 3657 * * 3658 * INPUT: shapedata -- Pointer to the shape data block. * 3659 * * 3660 * shapenum -- The shape number to examine. Each shape would have a different * 3661 * dimension rectangle. * 3662 * * 3663 * OUTPUT: Returns with the rectangle that encloses the shape. * 3664 * * 3665 * WARNINGS: This routine uses brute force and is slow. It is presumed that the results * 3666 * will be cached for subsiquent reuse. * 3667 * * 3668 * HISTORY: * 3669 * 07/22/1996 JLB : Created. * 3670 *=============================================================================================*/ 3671 Rect const Shape_Dimensions(void const * shapedata, int shapenum) 3672 { 3673 Rect rect; 3674 3675 if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) { 3676 return(rect); 3677 } 3678 3679 char * shape; 3680 #ifdef WIN32 3681 void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer); 3682 if (sh == NULL) return(rect); 3683 // shape = (char *)sh; 3684 shape = (char *)Get_Shape_Header_Data(sh); 3685 #else 3686 Build_Frame(shapedata, shapenum, _ShapeBuffer); 3687 shape = (char *)_ShapeBuffer; 3688 #endif 3689 3690 int width = Get_Build_Frame_Width(shapedata); 3691 int height = Get_Build_Frame_Height(shapedata); 3692 3693 rect.X = 0; 3694 rect.Y = 0; 3695 int xlimit = width-1; 3696 int ylimit = height-1; 3697 3698 /* 3699 ** Find top edge of the shape. 3700 */ 3701 for (int y = 0; y <= ylimit; y++) { 3702 for (int x = 0; x <= xlimit; x++) { 3703 if (shape[y*width + x] != 0) { 3704 rect.Y = y; 3705 rect.X = x; 3706 y = ylimit+1; 3707 break; 3708 } 3709 } 3710 } 3711 3712 /* 3713 ** Find bottom edge of the shape. 3714 */ 3715 for (int y = ylimit; y >= rect.Y; y--) { 3716 for (int x = xlimit; x >= 0; x--) { 3717 if (shape[y*width + x] != 0) { 3718 rect.Height = (y-rect.Y)+1; 3719 xlimit = x; 3720 y = rect.Y-1; 3721 break; 3722 } 3723 } 3724 } 3725 3726 /* 3727 ** Find left edge of the shape. 3728 */ 3729 for (int x = 0; x < rect.X; x++) { 3730 for (int y = rect.Y; y < rect.Y+rect.Height; y++) { 3731 if (shape[y*width + x] != 0) { 3732 rect.X = x; 3733 x = rect.X; 3734 break; 3735 } 3736 } 3737 } 3738 3739 /* 3740 ** Find the right edge of the shape. 3741 */ 3742 for (int x = width-1; x >= xlimit; x--) { 3743 for (int y = rect.Y; y < rect.Y+rect.Height; y++) { 3744 if (shape[y*width + x] != 0) { 3745 rect.Width = (x-rect.X)+1; 3746 x = xlimit-1; 3747 break; 3748 } 3749 } 3750 } 3751 3752 /* 3753 ** Normalize the rectangle around the center of the shape. 3754 */ 3755 rect.X -= width / 2; 3756 rect.Y -= height / 2; 3757 3758 /* 3759 ** Return with the minimum rectangle that encloses the shape. 3760 */ 3761 return(rect); 3762 } 3763 3764 3765 /*********************************************************************************************** 3766 * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * 3767 * * 3768 * This routine will convert the supplied RTTI type number and the ID value into a valid * 3769 * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * 3770 * * 3771 * INPUT: type -- RTTI type of the techno class object. * 3772 * * 3773 * id -- Integer representation of the techno sub type number. * 3774 * * 3775 * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * 3776 * conversion could not occur. * 3777 * * 3778 * WARNINGS: none * 3779 * * 3780 * HISTORY: * 3781 * 05/08/1995 JLB : Created. * 3782 *=============================================================================================*/ 3783 TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) 3784 { 3785 switch (type) { 3786 case RTTI_UNITTYPE: 3787 case RTTI_UNIT: 3788 return(&UnitTypeClass::As_Reference(UnitType(id))); 3789 3790 case RTTI_VESSELTYPE: 3791 case RTTI_VESSEL: 3792 return(&VesselTypeClass::As_Reference(VesselType(id))); 3793 3794 case RTTI_BUILDINGTYPE: 3795 case RTTI_BUILDING: 3796 return(&BuildingTypeClass::As_Reference(StructType(id))); 3797 3798 case RTTI_INFANTRYTYPE: 3799 case RTTI_INFANTRY: 3800 return(&InfantryTypeClass::As_Reference(InfantryType(id))); 3801 3802 case RTTI_AIRCRAFTTYPE: 3803 case RTTI_AIRCRAFT: 3804 return(&AircraftTypeClass::As_Reference(AircraftType(id))); 3805 3806 default: 3807 break; 3808 } 3809 return(NULL); 3810 } 3811 3812 3813 /*********************************************************************************************** 3814 * VQ_Call_Back -- Maintenance callback used for VQ movies. * 3815 * * 3816 * This routine is called every frame of the VQ movie as it is being played. If this * 3817 * routine returns non-zero, then the movie will stop. * 3818 * * 3819 * INPUT: buffer -- Pointer to the image buffer for the current frame. * 3820 * * 3821 * frame -- The frame number about to be displayed. * 3822 * * 3823 * OUTPUT: Should the movie be stopped? * 3824 * * 3825 * WARNINGS: none * 3826 * * 3827 * HISTORY: * 3828 * 06/24/1995 JLB : Created. * 3829 *=============================================================================================*/ 3830 #ifdef WIN32 3831 void VQA_PauseAudio(void) {}; 3832 void Check_VQ_Palette_Set(void); 3833 3834 extern GraphicBufferClass VQ640; 3835 extern bool IsVQ640; 3836 long VQ_Call_Back(unsigned char *, long ) 3837 { 3838 return 0; 3839 #if (0)//PG 3840 int key = 0; 3841 if (Keyboard->Check()) { 3842 key = Keyboard->Get(); 3843 Keyboard->Clear(); 3844 } 3845 Check_VQ_Palette_Set(); 3846 #ifdef MOVIE640 3847 if(IsVQ640) { 3848 VQ640.Blit(SeenBuff); 3849 } else { 3850 Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); 3851 } 3852 #else 3853 Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); 3854 #endif 3855 //Call_Back(); 3856 3857 if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { 3858 Keyboard->Clear(); 3859 Brokeout = true; 3860 return(true); 3861 } 3862 3863 if (!GameInFocus) { 3864 VQA_PauseAudio(); 3865 while (!GameInFocus) { 3866 Check_For_Focus_Loss(); 3867 } 3868 } 3869 return(false); 3870 #endif 3871 } 3872 3873 #else //WIN32 3874 3875 long VQ_Call_Back(unsigned char *, long ) 3876 { 3877 Call_Back(); 3878 if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) { 3879 if (Keyboard->Get() == KN_ESC) { 3880 Keyboard->Clear(); 3881 Brokeout = true; 3882 return(true); 3883 } 3884 Keyboard->Clear(); 3885 } 3886 return(false); 3887 } 3888 #endif //WIN32 3889 3890 3891 /*********************************************************************************************** 3892 * Handle_Team -- Processes team selection command. * 3893 * * 3894 * This routine will handle creation and selection of pseudo teams that the player can * 3895 * create or control. A team in this sense is an arbitrary grouping of units such that * 3896 * rapid selection control is allowed. * 3897 * * 3898 * INPUT: team -- The logical team number to process. * 3899 * * 3900 * action-- The action to perform on this team: * 3901 * 0 - Toggle the select state for all members of this team. * 3902 * 1 - Select the members of this team. * 3903 * 2 - Make all selected objects members of this team. * 3904 * * 3905 * OUTPUT: none * 3906 * * 3907 * WARNINGS: none * 3908 * * 3909 * HISTORY: * 3910 * 06/27/1995 JLB : Created. * 3911 *=============================================================================================*/ 3912 void Handle_Team(int team, int action) 3913 { 3914 int index; 3915 3916 // 3917 // Recording support 3918 // 3919 if (Session.Record) { 3920 TeamNumber = (char)team; 3921 TeamEvent = (char)action + 1; 3922 } 3923 3924 TeamFormDataStruct& team_form_data = TeamFormData[PlayerPtr->Class->House]; 3925 3926 AllowVoice = true; 3927 switch (action) { 3928 3929 /* 3930 ** Toggle the team selection. If the team is selected, then merely unselect it. If the 3931 ** team is not selected, then unselect all others before selecting this team. 3932 */ 3933 case 3: 3934 case 0: 3935 3936 /* 3937 ** If a non team member is currently selected, then deselect all objects 3938 ** before selecting this team. 3939 */ 3940 for (index = 0; index < CurrentObject.Count(); index++) { 3941 ObjectClass * obj = CurrentObject[index]; 3942 if (obj->Is_Foot() && ((FootClass *)obj)->Group != team) { 3943 Unselect_All(); 3944 break; 3945 } 3946 } 3947 for (index = 0; index < Vessels.Count(); index++) { 3948 VesselClass * obj = Vessels.Ptr(index); 3949 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 3950 if (!obj->Is_Selected_By_Player()) { 3951 obj->Select(); 3952 AllowVoice = false; 3953 } 3954 } 3955 } 3956 for (index = 0; index < Units.Count(); index++) { 3957 UnitClass * obj = Units.Ptr(index); 3958 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 3959 if (!obj->Is_Selected_By_Player()) { 3960 obj->Select(); 3961 AllowVoice = false; 3962 } 3963 } 3964 } 3965 for (index = 0; index < Infantry.Count(); index++) { 3966 InfantryClass * obj = Infantry.Ptr(index); 3967 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 3968 if (!obj->Is_Selected_By_Player()) { 3969 obj->Select(); 3970 AllowVoice = false; 3971 } 3972 } 3973 } 3974 for (index = 0; index < Aircraft.Count(); index++) { 3975 AircraftClass * obj = Aircraft.Ptr(index); 3976 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 3977 if (!obj->Is_Selected_By_Player()) { 3978 obj->Select(); 3979 AllowVoice = false; 3980 } 3981 } 3982 } 3983 3984 /* 3985 ** Center the map around the team if the ALT key was pressed too. 3986 */ 3987 if (action == 3) { 3988 Map.Center_Map(); 3989 #ifdef WIN32 3990 Map.Flag_To_Redraw(true); 3991 #endif //WIn32 3992 } 3993 break; 3994 3995 /* 3996 ** Additive selection of team. 3997 */ 3998 case 1: 3999 for (index = 0; index < Units.Count(); index++) { 4000 UnitClass * obj = Units.Ptr(index); 4001 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 4002 if (!obj->Is_Selected_By_Player()) { 4003 obj->Select(); 4004 AllowVoice = false; 4005 } 4006 } 4007 } 4008 for (index = 0; index < Vessels.Count(); index++) { 4009 VesselClass * obj = Vessels.Ptr(index); 4010 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 4011 if (!obj->Is_Selected_By_Player()) { 4012 obj->Select(); 4013 AllowVoice = false; 4014 } 4015 } 4016 } 4017 for (index = 0; index < Infantry.Count(); index++) { 4018 InfantryClass * obj = Infantry.Ptr(index); 4019 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 4020 if (!obj->Is_Selected_By_Player()) { 4021 obj->Select(); 4022 AllowVoice = false; 4023 } 4024 } 4025 } 4026 for (index = 0; index < Aircraft.Count(); index++) { 4027 AircraftClass * obj = Aircraft.Ptr(index); 4028 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { 4029 if (!obj->Is_Selected_By_Player()) { 4030 obj->Select(); 4031 AllowVoice = false; 4032 } 4033 } 4034 } 4035 break; 4036 4037 /* 4038 ** Create the team. 4039 */ 4040 case 2: { 4041 long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; 4042 long maxx = 0, maxy = 0; 4043 team_form_data.TeamSpeed[team] = SPEED_WHEEL; 4044 team_form_data.TeamMaxSpeed[team] = MPH_LIGHT_SPEED; 4045 for (index = 0; index < Units.Count(); index++) { 4046 UnitClass * obj = Units.Ptr(index); 4047 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { 4048 if (obj->Group == team) obj->Group = 0xFF; 4049 if (obj->Is_Selected_By_Player()) { 4050 obj->Group = team; 4051 obj->Mark(MARK_CHANGE); 4052 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 4053 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 4054 if (xc < minx) minx = xc; 4055 if (xc > maxx) maxx = xc; 4056 if (yc < miny) miny = yc; 4057 if (yc > maxy) maxy = yc; 4058 if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) { 4059 team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed; 4060 team_form_data.TeamSpeed[team] = obj->Class->Speed; 4061 } 4062 } 4063 } 4064 } 4065 4066 for (index = 0; index < Vessels.Count(); index++) { 4067 VesselClass * obj = Vessels.Ptr(index); 4068 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { 4069 if (obj->Group == team) obj->Group = -1; 4070 if (obj->Is_Selected_By_Player()) { 4071 obj->Group = team; 4072 obj->Mark(MARK_CHANGE); 4073 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 4074 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 4075 if (xc < minx) minx = xc; 4076 if (xc > maxx) maxx = xc; 4077 if (yc < miny) miny = yc; 4078 if (yc > maxy) maxy = yc; 4079 if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) { 4080 team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed; 4081 team_form_data.TeamSpeed[team] = obj->Class->Speed; 4082 } 4083 } 4084 } 4085 } 4086 4087 for (index = 0; index < Infantry.Count(); index++) { 4088 InfantryClass * obj = Infantry.Ptr(index); 4089 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { 4090 if (obj->Group == team) obj->Group = 0xFF; 4091 if (obj->Is_Selected_By_Player()) { 4092 obj->Group = team; 4093 obj->Mark(MARK_CHANGE); 4094 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 4095 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 4096 if (xc < minx) minx = xc; 4097 if (xc > maxx) maxx = xc; 4098 if (yc < miny) miny = yc; 4099 if (yc > maxy) maxy = yc; 4100 if (obj->Class->MaxSpeed < team_form_data.TeamMaxSpeed[team]) { 4101 team_form_data.TeamMaxSpeed[team] = obj->Class->MaxSpeed; 4102 } 4103 } 4104 } 4105 } 4106 for (index = 0; index < Aircraft.Count(); index++) { 4107 AircraftClass * obj = Aircraft.Ptr(index); 4108 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { 4109 if (obj->Group == team) obj->Group = 0xFF; 4110 if (obj->Is_Selected_By_Player()) { 4111 obj->Group = team; 4112 obj->Mark(MARK_CHANGE); 4113 } 4114 } 4115 } 4116 4117 for (index = 0; index < Units.Count(); index++) { 4118 UnitClass * obj = Units.Ptr(index); 4119 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl && 4120 (obj->Group == team) && (obj->Is_Selected_By_Player()) ) { 4121 4122 /* 4123 ** When a team is first created, they're created without a 4124 ** formation offset, so they will not be created in 4125 ** formation. Later, if they're assigned a formation, the 4126 ** XFormOffset & YFormOffset numbers will change to valid 4127 ** offsets, and they'll be formationed. 4128 */ 4129 #if(1) 4130 obj->XFormOffset = obj->YFormOffset = (int)0x80000000; 4131 #else 4132 #if(1) 4133 // Old always-north formation stuff 4134 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 4135 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 4136 4137 obj->XFormOffset = xc - centerx; 4138 obj->YFormOffset = yc - centery; 4139 #else 4140 // New method: save direction and distance rather than x & y offset 4141 obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); 4142 obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); 4143 #endif 4144 #endif 4145 } 4146 } 4147 4148 for (index = 0; index < Infantry.Count(); index++) { 4149 InfantryClass * obj = Infantry.Ptr(index); 4150 if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { 4151 if (obj->Group == team) obj->Group = 0xFF; 4152 if (obj->Is_Selected_By_Player()) obj->Group = team; 4153 if (obj->Group == team && obj->Is_Selected_By_Player()) { 4154 #if(1) 4155 obj->XFormOffset = obj->YFormOffset = (int)0x80000000; 4156 #else 4157 #if(1) 4158 // Old always-north formation stuff 4159 long xc = Cell_X(Coord_Cell(obj->Center_Coord())); 4160 long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); 4161 4162 obj->XFormOffset = xc - centerx; 4163 obj->YFormOffset = yc - centery; 4164 #else 4165 // New method: save direction and distance rather than x & y offset 4166 obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); 4167 obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); 4168 #endif 4169 #endif 4170 } 4171 } 4172 } 4173 break; 4174 } 4175 4176 default: 4177 break; 4178 } 4179 AllowVoice = true; 4180 } 4181 4182 4183 /*********************************************************************************************** 4184 * Handle_View -- Either records or restores the tactical view. * 4185 * * 4186 * This routine is used to record or restore the current map tactical view. * 4187 * * 4188 * INPUT: view -- The view number to work with. * 4189 * * 4190 * action-- The action to perform with this view number. * 4191 * 0 = Restore the view to this previously remembered location. * 4192 * 1 = Record the current view location. * 4193 * * 4194 * OUTPUT: none * 4195 * * 4196 * WARNINGS: none * 4197 * * 4198 * HISTORY: * 4199 * 07/04/1995 JLB : Created. * 4200 *=============================================================================================*/ 4201 void Handle_View(int view, int action) 4202 { 4203 if ((unsigned)view < ARRAY_SIZE(Scen.Views)) { 4204 if (action == 0) { 4205 4206 Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view] - (MAP_CELL_W * 4 * RESFACTOR) - (5*RESFACTOR)))); 4207 4208 #ifdef WIN32 4209 /* 4210 ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot. 4211 */ 4212 Map.Flag_To_Redraw (true); 4213 #endif //WIN32 4214 } else { 4215 Scen.Views[view] = Coord_Cell(Map.TacticalCoord) + (MAP_CELL_W*4*RESFACTOR) + (5*RESFACTOR); 4216 } 4217 } 4218 } 4219 4220 4221 #ifndef ROR_NOT_READY 4222 #define ROR_NOT_READY 21 4223 #endif 4224 4225 static char * _CD_Volume_Label[] = { 4226 "CD1", 4227 "CD2", 4228 "CD3", 4229 "CD4", 4230 // Denzil 4/15/98 4231 #ifdef DVD 4232 "CD1", // ajw - Pushes RADVD to position 5, to match enum in Force_CD_Available(). 4 will never be returned here. 4233 "RADVD", 4234 #endif 4235 }; 4236 static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label); 4237 4238 4239 #ifdef WIN32 4240 /*********************************************************************************************** 4241 * Get_CD_Index -- returns the volume type of the CD in the given drive * 4242 * * 4243 * * 4244 * * 4245 * INPUT: drive number * 4246 * timeout * 4247 * * 4248 * OUTPUT: 0 = gdi * 4249 * 1 = nod * 4250 * 2 = covert or CS * 4251 * 3 = Aftermath 4252 * 5 = DVD 4253 * -1 = non C&C * 4254 * * 4255 * WARNINGS: None * 4256 * * 4257 * HISTORY: * 4258 * 5/21/96 5:27PM ST : Created * 4259 * 01/20/97 V.Grippi added CS support * 4260 *=============================================================================================*/ 4261 int Get_CD_Index (int cd_drive, int timeout) 4262 { 4263 char volume_name[128]; 4264 char buffer[128]; 4265 unsigned filename_length; 4266 unsigned misc_dword; 4267 int count = 0; 4268 4269 CountDownTimerClass timer; 4270 4271 timer.Set(timeout); 4272 4273 /* 4274 ** Get the volume label. If we get a 'not ready' error then retry for the timeout 4275 ** period. 4276 */ 4277 for (;;) 4278 { 4279 sprintf(buffer, "%c:\\", 'A' + cd_drive); 4280 4281 if (GetVolumeInformation ((char const *)buffer, &volume_name[0] , 4282 (unsigned long)sizeof(volume_name), (unsigned long *)NULL , 4283 (unsigned long *)&filename_length, (unsigned long *)&misc_dword, 4284 (char *)NULL, (unsigned long)0)) 4285 { 4286 /* 4287 ** Try opening 'movies.mix' to verify that the CD is really there and is what 4288 ** it says it is. 4289 */ 4290 sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); 4291 4292 HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 4293 4294 if (handle != INVALID_HANDLE_VALUE) 4295 { 4296 CloseHandle(handle); 4297 4298 /* 4299 ** Match the volume label to the list of known C&C volume labels. 4300 */ 4301 for (int i = 0 ; i < _Num_Volumes; i++) 4302 { 4303 if (!stricmp(_CD_Volume_Label[i], volume_name)) 4304 return(i); 4305 } 4306 } 4307 else 4308 { 4309 if (!count) 4310 count++; 4311 else 4312 return -1; 4313 } 4314 } 4315 else 4316 { 4317 /* 4318 ** Failed to get the volume label on a known CD drive. 4319 ** If this is a CD changer it may require time to swap the disks so dont return 4320 ** immediately if the error is ROR_NOT_READY 4321 */ 4322 if (!timer.Time()) 4323 return -1; 4324 4325 int val = GetLastError(); 4326 4327 if (val != ROR_NOT_READY) 4328 return -1; 4329 } 4330 } 4331 } 4332 #else 4333 int Get_CD_Index(int cd_drive, int) 4334 { 4335 char buffer[128]; 4336 4337 /* 4338 ** We need to do this twice because of the possibilities of a directory 4339 ** being cached. If this is so, it will only be discovered when we 4340 ** actually attempt to read a file from the drive. 4341 */ 4342 if(cd_drive) for (int count = 0; count < 2; count ++) 4343 { 4344 struct find_t ft; 4345 int file; 4346 int open_failed; 4347 4348 /* 4349 ** Create a path for the cd drive and attempt to read the volume label from 4350 ** it. 4351 */ 4352 sprintf(buffer, "%c:\\", 'A' + cd_drive); 4353 4354 /* 4355 ** If we are able to read the volume label, this is good but not enough. 4356 ** Further verification must be done. 4357 */ 4358 if (!_dos_findfirst(buffer, _A_VOLID, &ft)) 4359 { 4360 /* 4361 ** Since some versions of disk cacheing software will cache the CD's 4362 ** directory tracks, we may think the CD is in the drive when it is 4363 ** actually not. To resolve this we must attempt to open a file on 4364 ** the cd. Opening a file will always update the directory tracks 4365 ** (suposedly). 4366 */ 4367 sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); 4368 open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file); 4369 4370 if (!open_failed) 4371 { 4372 _dos_close(file); 4373 4374 /* 4375 ** Hey some times the stupid dos driver appends a period to the 4376 ** name if it is eight characters long. If the last char is a 4377 ** period then erase it. 4378 */ 4379 if (ft.name[strlen(ft.name)-1] == '.') 4380 { 4381 ft.name[strlen(ft.name)-1] = 0; 4382 } 4383 4384 /* 4385 ** Match the volume label to the list of known C&C volume labels. 4386 */ 4387 for (int i = 0 ; i < _Num_Volumes; i++) 4388 { 4389 if (!stricmp(_CD_Volume_Label[i], ft.name)) 4390 return (i); 4391 } 4392 } 4393 } 4394 } 4395 4396 return -1; 4397 } 4398 #endif 4399 4400 4401 /*********************************************************************************************** 4402 * Force_CD_Available -- Ensures that specified CD is available. * 4403 * * 4404 * Call this routine when you need to ensure that the specified CD is actually in the * 4405 * CD-ROM drive. * 4406 * * 4407 * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * 4408 * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * 4409 * 0 = CD1 4410 * 1 = CD2 4411 * 2 = Counterstrike 4412 * 3 = Aftermath 4413 * 4 = Counterstrike or Aftermath 4414 * 5 = DVD 4415 * -1 = Any CD 4416 * -2 = Local Harddisk 4417 * * 4418 * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * 4419 * the player pressed <CANCEL>. * 4420 * * 4421 * WARNINGS: none * 4422 * * 4423 * HISTORY: * 4424 * 07/11/1995 JLB : Created. * 4425 * 05/22/1996 ST : Handles multiple CD drives / CD changers * 4426 * 01/20/1997 V.Grippi added expansion cd message 4427 *=============================================================================================*/ 4428 4429 #if (1) //ST - 5/13/2019 4430 bool Force_CD_Available(int cd) 4431 { 4432 static int _last = -1; 4433 4434 if (_last != cd || cd == ALWAYS_RELOAD_CD) { 4435 if (cd != ALWAYS_RELOAD_CD) { 4436 _last = cd; 4437 } 4438 Theme.Stop(); 4439 4440 // if (ConquerMix) delete ConquerMix; 4441 if (MoviesMix) { 4442 delete MoviesMix; 4443 MoviesMix = 0; 4444 } 4445 if (GeneralMix) { 4446 delete GeneralMix; 4447 GeneralMix = 0; 4448 } 4449 if (ScoreMix) { 4450 delete ScoreMix; 4451 ScoreMix = 0; 4452 } 4453 if (MainMix) { 4454 delete MainMix; 4455 MainMix = 0; 4456 } 4457 4458 MainMix = new MFCD("MAIN.MIX", &FastKey); 4459 assert(MainMix != NULL); 4460 // ConquerMix = new MFCD("CONQUER.MIX", &FastKey); 4461 if (CCFileClass("MOVIES1.MIX").Is_Available()) 4462 MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); 4463 else 4464 MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); 4465 assert(MoviesMix != NULL); 4466 GeneralMix = new MFCD("GENERAL.MIX", &FastKey); 4467 ScoreMix = new MFCD("SCORES.MIX", &FastKey); 4468 } 4469 4470 return true; 4471 } 4472 4473 4474 #endif 4475 4476 #if (0) //ST - 5/13/2019 4477 4478 typedef enum { 4479 CD_LOCAL = -2, 4480 CD_ANY = -1, 4481 CD_SOVIET = 0, 4482 CD_ALLIED, 4483 CD_COUNTERSTRIKE, 4484 CD_AFTERMATH, 4485 CD_CS_OR_AM, 4486 CD_DVD 4487 } CD_VOLUME; 4488 4489 #ifdef FIXIT_VERSION_3 4490 4491 #ifndef DVD 4492 #error DVD must be defined! 4493 #endif 4494 4495 bool Force_CD_Available( int cd_desired ) // ajw 4496 { 4497 static int _last = -1; 4498 static void *font; 4499 #ifdef FRENCH 4500 static char * _cd_name[] = { 4501 "ALERTE ROUGE CD1", 4502 "ALERTE ROUGE CD2", 4503 "CD Missions Taiga", 4504 "CD Missions M.A.D.", 4505 "ALERTE ROUGE DVD", 4506 }; 4507 #endif 4508 #ifdef GERMAN 4509 static char * _cd_name[] = { 4510 "ALARMSTUFE ROT CD1", 4511 "ALARMSTUFE ROT CD2", 4512 "CD Gegenangriff einlegen", 4513 "CD TRANS einlegen", 4514 "ALARMSTUFE ROT DVD", 4515 }; 4516 #endif 4517 #ifdef ENGLISH 4518 static char * _cd_name[] = { 4519 "RED ALERT DISK 1", 4520 "RED ALERT DISK 2", 4521 "CounterStrike CD", 4522 "Aftermath CD", 4523 "RED ALERT DVD", 4524 }; 4525 #endif 4526 4527 4528 4529 int new_cd_drive = 0; 4530 int cd_current; 4531 int current_drive; 4532 4533 ThemeType theme_playing = THEME_NONE; 4534 4535 /* 4536 ** If the required CD is set to -2 then it means that the file is present 4537 ** on the local hard drive and we shouldn't have to worry about it. 4538 */ 4539 if (cd_desired == CD_LOCAL) return(true); 4540 4541 /* 4542 ** Find out if the CD in the current drive is the one we are looking for 4543 */ 4544 current_drive = CCFileClass::Get_CD_Drive(); 4545 cd_current = Get_CD_Index(current_drive, 1*60); 4546 4547 // debugprint("Get_CD_Index just returned %d\n", cd_current); 4548 // debugprint("We are checking for %d\n", cd_desired); 4549 // debugprint("current_drive = %d\n", current_drive); 4550 4551 if( Using_DVD() ) 4552 { 4553 // All requested cd indexes get rerouted to the DVD. 4554 cd_desired = CD_DVD; 4555 // if( RequiredCD != -1 ) 4556 // RequiredCD = CD_DVD; // Just seems like a good idea. Not sure if necessary. ajw 4557 } 4558 4559 if (cd_current >= 0 ) 4560 { 4561 if( cd_desired == CD_CS_OR_AM ) 4562 { 4563 // If the current cd is CS or AM then change request to whatever 4564 // is present. 4565 if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) 4566 cd_desired = cd_current; 4567 } 4568 // If the current CD is requested or any CD will work 4569 if( cd_desired == cd_current || cd_desired == CD_ANY ) 4570 { 4571 /* 4572 ** The required CD is still in the CD drive we used last time 4573 */ 4574 new_cd_drive = current_drive; 4575 } 4576 } 4577 4578 /* 4579 ** Flag that we will have to restart the theme 4580 */ 4581 theme_playing = Theme.What_Is_Playing(); 4582 Theme.Stop(); 4583 4584 // Check the last drive 4585 if (!new_cd_drive) 4586 { 4587 /* 4588 ** Check the last CD drive we used if it's different from the current one 4589 */ 4590 int last_drive = CCFileClass::Get_Last_CD_Drive(); 4591 4592 /* 4593 ** Make sure the last drive is valid and it isn't the current drive 4594 */ 4595 if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) // Else we have already checked this cd. 4596 { 4597 /* 4598 ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for 4599 ** Give it a nice big timeout so the CD changer has time to swap the discs 4600 */ 4601 cd_current = Get_CD_Index(last_drive, 10*60); 4602 4603 if (cd_current >= 0 ) 4604 { 4605 if( cd_desired == CD_CS_OR_AM ) 4606 { 4607 // If the cd is CS or AM then change request to whatever 4608 // is present. 4609 if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) 4610 cd_desired = cd_current; 4611 } 4612 // If the cd is present or any cd will work 4613 if( cd_desired == cd_current || cd_desired == CD_ANY ) 4614 { 4615 /* 4616 ** The required CD is in the CD drive we used last time 4617 */ 4618 new_cd_drive = last_drive; 4619 } 4620 } 4621 } 4622 } 4623 4624 /* 4625 ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives 4626 ** then if we still can't find it prompt the user to insert it. 4627 */ 4628 if (!new_cd_drive) 4629 { 4630 /* 4631 ** Small timeout for the first pass through the drives 4632 */ 4633 int drive_search_timeout = 2*60; 4634 4635 for (;;) 4636 { 4637 char buffer[128]; 4638 /* 4639 ** Search all present CD drives for the required disc. 4640 */ 4641 for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) 4642 { 4643 int cd_drive = CDList.Get_Next_CD_Drive(); 4644 cd_current = Get_CD_Index(cd_drive, drive_search_timeout); 4645 4646 if (cd_current >= 0) 4647 { 4648 /* 4649 ** We found a C&C cd - lets see if it was the one we were looking for 4650 */ 4651 // Require CS or AM 4652 if( cd_desired == CD_CS_OR_AM ) 4653 { 4654 // If the cd is CS or AM then change request to whatever 4655 // is present. 4656 if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) 4657 cd_desired = cd_current; 4658 } 4659 4660 if( cd_desired == cd_current || cd_desired == CD_ANY ) 4661 { 4662 /* 4663 ** Woohoo! The disk was in a different cd drive. Refresh the search path list 4664 * and return. 4665 */ 4666 new_cd_drive = cd_drive; 4667 break; 4668 } 4669 } 4670 } 4671 4672 /* 4673 ** A new disc has become available so break 4674 */ 4675 if (new_cd_drive) break; 4676 4677 /* 4678 ** Increase the timeout for subsequent drive searches. 4679 */ 4680 drive_search_timeout = 5*60; 4681 4682 /* 4683 ** Prompt to insert the CD into the drive. 4684 */ 4685 //V.Grippi 4686 if( cd_desired == CD_CS_OR_AM ) 4687 cd_desired = CD_AFTERMATH; 4688 4689 if( cd_desired == CD_DVD ) 4690 { 4691 #ifdef FRENCH 4692 sprintf(buffer, "Ins�rez le %s", _cd_name[4]); 4693 #else 4694 #ifdef GERMAN 4695 sprintf(buffer, "Bitte %s", _cd_name[4]); 4696 #else 4697 sprintf(buffer, "Please insert the %s", _cd_name[4]); 4698 #endif 4699 #endif 4700 } 4701 else if( cd_desired == CD_COUNTERSTRIKE || cd_desired == CD_AFTERMATH ) 4702 { 4703 #ifdef FRENCH 4704 sprintf(buffer, "Ins�rez le %s", _cd_name[cd_desired]); 4705 #else 4706 #ifdef GERMAN 4707 sprintf(buffer, "Bitte %s", _cd_name[cd_desired]); 4708 #else 4709 sprintf(buffer, "Please insert the %s", _cd_name[cd_desired]); 4710 #endif 4711 #endif 4712 } 4713 else if( cd_desired == CD_ANY ) 4714 { 4715 sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd_desired+1, _cd_name[cd_desired]); 4716 } 4717 else // 0 or 1 4718 { 4719 sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd_desired+1, _cd_name[cd_desired]); 4720 } 4721 4722 GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); 4723 theme_playing = Theme.What_Is_Playing(); 4724 Theme.Stop(); 4725 int hidden = Get_Mouse_State(); 4726 font = (void *)FontPtr; 4727 4728 /* 4729 ** Only set the palette if necessary. 4730 */ 4731 if (PaletteClass::CurrentPalette[1].Red_Component() + 4732 PaletteClass::CurrentPalette[1].Blue_Component() + 4733 PaletteClass::CurrentPalette[1].Green_Component() == 0) 4734 { 4735 GamePalette.Set(); 4736 } 4737 4738 Keyboard->Clear(); 4739 4740 while (Get_Mouse_State()) Show_Mouse(); 4741 4742 if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) 4743 { 4744 Set_Logic_Page(oldpage); 4745 #ifdef FIXIT_VERSION_3 4746 while (hidden--) Hide_Mouse(); 4747 #else 4748 Hide_Mouse(); 4749 #endif 4750 return(false); 4751 } 4752 4753 while (hidden--) Hide_Mouse(); 4754 Set_Font(font); 4755 Set_Logic_Page(oldpage); 4756 } 4757 } 4758 4759 CurrentCD = cd_current; 4760 4761 CCFileClass::Set_CD_Drive(new_cd_drive); 4762 CCFileClass::Refresh_Search_Drives(); 4763 4764 /* 4765 ** If it broke out of the query for CD-ROM loop, then this means that the 4766 ** CD-ROM has been inserted. 4767 */ 4768 if (cd_desired == 4) cd_desired--; 4769 4770 // ajw - Added condition of cd_desired != 5 to the following if. 4771 // Reason: This was triggering before Init_Secondary_Mixfiles(), which was screwing up the mixfile system somehow. 4772 // 4773 // Since the DVD is the only disk that can possibly be required when Using_DVD(), I never have to reload the mix 4774 // files here, because no other disk could ever have been asked for. And if not Using_DVD(), cd_desired will never 4775 // be equal to 5. So this is safe. 4776 if (cd_desired > -1 && _last != cd_desired && cd_desired != 5) 4777 { 4778 _last = cd_desired; 4779 4780 Theme.Stop(); 4781 4782 // if (ConquerMix) delete ConquerMix; 4783 if (MoviesMix) delete MoviesMix; 4784 if (GeneralMix) delete GeneralMix; 4785 if (ScoreMix) delete ScoreMix; 4786 if (MainMix) delete MainMix; 4787 4788 MainMix = new MFCD("MAIN.MIX", &FastKey); 4789 assert(MainMix != NULL); 4790 // ConquerMix = new MFCD("CONQUER.MIX", &FastKey); 4791 if (CCFileClass("MOVIES1.MIX").Is_Available()) 4792 MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); 4793 else 4794 MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); 4795 assert(MoviesMix != NULL); 4796 GeneralMix = new MFCD("GENERAL.MIX", &FastKey); 4797 ScoreMix = new MFCD("SCORES.MIX", &FastKey); 4798 ThemeClass::Scan(); 4799 } 4800 4801 return(true); 4802 } 4803 4804 #else // FIXIT_VERSION_3 not defined 4805 4806 bool Force_CD_Available(int cd) 4807 { 4808 static int _last = -1; 4809 // static char _palette[768]; 4810 // static char _hold[256]; 4811 static void *font; 4812 #ifdef FRENCH 4813 static char * _cd_name[] = { 4814 "ALERTE ROUGE CD1", 4815 "ALERTE ROUGE CD2", 4816 "CD Missions Taiga", 4817 "CD Missions M.A.D.", 4818 4819 // Denzil 4/15/98 4820 #ifdef DVD 4821 "ALERTE ROUGE DVD", 4822 #endif 4823 }; 4824 4825 #endif 4826 #ifdef GERMAN 4827 static char * _cd_name[] = { 4828 "ALARMSTUFE ROT CD1 einlegen", 4829 "ALARMSTUFE ROT CD2 einlegen", 4830 "CD Gegenangriff einlegen", 4831 "CD TRANS einlegen", 4832 4833 // Denzil 4/15/98 4834 #ifdef DVD 4835 "ALARMSTUFE ROT DVD einlegen", 4836 #endif 4837 }; 4838 #endif 4839 #ifdef ENGLISH 4840 static char * _cd_name[] = { 4841 "RED ALERT DISK 1", 4842 "RED ALERT DISK 2", 4843 "CounterStrike CD", 4844 "Aftermath CD", 4845 4846 // Denzil 4/15/98 4847 #ifdef DVD 4848 "RED ALERT DVD", 4849 #endif 4850 }; 4851 #endif 4852 4853 int new_cd_drive = 0; 4854 int cd_index; 4855 char buffer[128]; 4856 int cd_drive; 4857 int current_drive; 4858 int drive_search_timeout; 4859 4860 ThemeType theme_playing = THEME_NONE; 4861 4862 //#ifdef FIXIT_ANTS 4863 // if(Scen.ScenarioName[2] == 'A') 4864 // cd = 2; 4865 //#endif 4866 /* 4867 ** If the required CD is set to -2 then it means that the file is present 4868 ** on the local hard drive and we shouldn't have to worry about it. 4869 */ 4870 if (cd == CD_LOCAL) return(true); 4871 4872 /* 4873 ** Find out if the CD in the current drive is the one we are looking for 4874 */ 4875 current_drive = CCFileClass::Get_CD_Drive(); 4876 cd_index = Get_CD_Index(current_drive, 1*60); 4877 4878 #ifdef CHEAT_KEYS 4879 // Mono_Printf("Get_CD_Index just returned %d\n", cd_index); 4880 // Mono_Printf("We are checking for %d\n", cd); 4881 // Mono_Printf("current_drive = %d\n", current_drive); 4882 #endif //CHEAT_KEYS 4883 4884 #ifdef DVD // Denzil 4885 // CD1 and CD2 are ignored, force the DVD 4886 if (cd_index == 0 || cd_index == 1) 4887 cd_index = -1; 4888 #endif 4889 4890 if (cd_index >= 0 ) 4891 { 4892 #ifdef FIXIT_CSII // checked - ajw 9/28/98 4893 // Require CS or AM 4894 if (cd == CD_CS_OR_AM) 4895 { 4896 // If the current cd is CS or AM then change request to whatever 4897 // is present. 4898 if (cd_index == 2 || cd_index == 3) 4899 { 4900 cd = cd_index; 4901 } 4902 } 4903 #endif 4904 4905 #ifdef DVD // Denzil 4906 // If the current drive is the DVD then requests for CD1 and CD2 are okay 4907 if (cd_index == 4) 4908 { 4909 // CD1, CD2 & DVD requests 4910 if (cd == 0 || cd == 1 || cd == 5) 4911 { 4912 cd_index = cd; 4913 } 4914 } 4915 #endif 4916 4917 // If the current CD is requested or any CD will work 4918 if (cd == cd_index || cd == -1 ) 4919 { 4920 /* 4921 ** The required CD is still in the CD drive we used last time 4922 */ 4923 new_cd_drive = current_drive; 4924 } 4925 } 4926 4927 /* 4928 ** Flag that we will have to restart the theme 4929 */ 4930 theme_playing = Theme.What_Is_Playing(); 4931 Theme.Stop(); 4932 4933 // Check the last drive 4934 if (!new_cd_drive) 4935 { 4936 /* 4937 ** Check the last CD drive we used if it's different from the current one 4938 */ 4939 int last_drive = CCFileClass::Get_Last_CD_Drive(); 4940 4941 /* 4942 ** Make sure the last drive is valid and it isn't the current drive 4943 */ 4944 if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) 4945 { 4946 /* 4947 ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for 4948 ** Give it a nice big timeout so the CD changer has time to swap the discs 4949 */ 4950 cd_index = Get_CD_Index(last_drive, 10*60); 4951 4952 #ifdef DVD // Denzil 4953 // Ignore CD1 and CD2 disks, force DVD 4954 if (cd_index == 0 || cd_index == 1) 4955 cd_index = -1; 4956 #endif 4957 4958 if (cd_index >= 0 ) 4959 { 4960 #ifdef FIXIT_CSII // checked - ajw 9/28/98 4961 // Require CS or AM 4962 if (cd == 4) 4963 { 4964 // If CS or AM was the last drive then use it 4965 if (cd_index == 2 || cd_index == 3) 4966 { 4967 cd = cd_index; 4968 } 4969 } 4970 #endif 4971 4972 #ifdef DVD // Denzil 4973 // If DVD is in drive 4974 if (cd_index == 4) 4975 { 4976 // CD1, CD2 and DVD requests are all on the DVD 4977 if ((cd == 0) || (cd == 1) || (cd == 5)) 4978 { 4979 cd_index = cd; 4980 } 4981 } 4982 #endif 4983 4984 // If the cd is present or any cd will work 4985 if (cd == cd_index || cd == -1 ) 4986 { 4987 /* 4988 ** The required CD is in the CD drive we used last time 4989 */ 4990 new_cd_drive = last_drive; 4991 } 4992 } 4993 } 4994 } 4995 4996 /* 4997 ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives 4998 ** then if we still can't find it prompt the user to insert it. 4999 */ 5000 if (!new_cd_drive) 5001 { 5002 /* 5003 ** Small timeout for the first pass through the drives 5004 */ 5005 drive_search_timeout = 2*60; 5006 5007 for (;;) 5008 { 5009 /* 5010 ** Search all present CD drives for the required disc. 5011 */ 5012 for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) 5013 { 5014 cd_drive = CDList.Get_Next_CD_Drive(); 5015 cd_index = Get_CD_Index(cd_drive, drive_search_timeout); 5016 5017 #ifdef DVD // Denzil 5018 // Ignore CD1 and CD2, force the DVD 5019 if (cd_index == 0 || cd_index == 1) 5020 cd_index = -1; 5021 #endif 5022 5023 if (cd_index >= 0) 5024 { 5025 /* 5026 ** We found a C&C cd - lets see if it was the one we were looking for 5027 */ 5028 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5029 // Require CS or AM 5030 if (cd == 4) 5031 { 5032 // If the disk is CS or AM then request it 5033 if (cd_index == 2 || cd_index == 3) 5034 { 5035 cd = cd_index; 5036 } 5037 } 5038 #endif 5039 5040 #ifdef DVD // Denzil 5041 if (cd_index == 4) 5042 { 5043 if ((cd == 0) || (cd == 1) || (cd == 5)) 5044 { 5045 cd_index = cd; 5046 } 5047 } 5048 #endif 5049 5050 if (cd == cd_index || cd == -1 || cd == -2 ) 5051 { 5052 /* 5053 ** Woohoo! The disk was in a different cd drive. Refresh the search path list 5054 * and return. 5055 */ 5056 new_cd_drive = cd_drive; 5057 break; 5058 } 5059 } 5060 } 5061 5062 /* 5063 ** A new disc has become available so break 5064 */ 5065 if (new_cd_drive) break; 5066 5067 /* 5068 ** Increase the timeout for subsequent drive searches. 5069 */ 5070 drive_search_timeout = 5*60; 5071 5072 /* 5073 ** Prompt to insert the CD into the drive. 5074 */ 5075 //V.Grippi 5076 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5077 if(cd == 4) cd--; 5078 5079 // CS or AM 5080 if(cd == 2 || cd == 3) 5081 { 5082 #else 5083 if(cd == 2) 5084 { 5085 #endif 5086 5087 #ifdef FRENCH 5088 sprintf(buffer, "Ins�rez le %s", _cd_name[cd]); 5089 #else 5090 #ifdef GERMAN 5091 sprintf(buffer, "Bitte %s", _cd_name[cd]); 5092 #else 5093 sprintf(buffer, "Please insert the %s", _cd_name[cd]); 5094 #endif 5095 #endif 5096 } 5097 else 5098 { 5099 #ifdef DVD 5100 #ifdef FRENCH 5101 sprintf(buffer, "Ins�rez le %s", _cd_name[4]); 5102 #else 5103 #ifdef GERMAN 5104 sprintf(buffer, "Bitte %s", _cd_name[4]); 5105 #else 5106 sprintf(buffer, "Please insert the %s", _cd_name[4]); 5107 #endif 5108 #endif 5109 #else 5110 if (cd == -1 ) 5111 { 5112 sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]); 5113 } 5114 else 5115 { 5116 sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]); 5117 } 5118 #endif 5119 } 5120 5121 #ifdef WIN32 5122 GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); 5123 #else 5124 GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff); 5125 #endif 5126 theme_playing = Theme.What_Is_Playing(); 5127 Theme.Stop(); 5128 int hidden = Get_Mouse_State(); 5129 font = (void *)FontPtr; 5130 5131 /* 5132 ** Only set the palette if necessary. 5133 */ 5134 if (PaletteClass::CurrentPalette[1].Red_Component() + 5135 PaletteClass::CurrentPalette[1].Blue_Component() + 5136 PaletteClass::CurrentPalette[1].Green_Component() == 0) 5137 { 5138 GamePalette.Set(); 5139 } 5140 5141 Keyboard->Clear(); 5142 5143 while (Get_Mouse_State()) Show_Mouse(); 5144 5145 if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) 5146 { 5147 Set_Logic_Page(oldpage); 5148 Hide_Mouse(); 5149 return(false); 5150 } 5151 5152 while (hidden--) Hide_Mouse(); 5153 Set_Font(font); 5154 Set_Logic_Page(oldpage); 5155 } 5156 } 5157 5158 CurrentCD = cd_index; 5159 5160 CCFileClass::Set_CD_Drive(new_cd_drive); 5161 CCFileClass::Refresh_Search_Drives(); 5162 5163 /* 5164 ** If it broke out of the query for CD-ROM loop, then this means that the 5165 ** CD-ROM has been inserted. 5166 */ 5167 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5168 if (cd == 4) cd--; 5169 #endif 5170 // if (cd > -3 && _last != cd) { 5171 if (cd > -1 && _last != cd) 5172 { 5173 _last = cd; 5174 5175 Theme.Stop(); 5176 5177 // if (ConquerMix) delete ConquerMix; 5178 if (MoviesMix) delete MoviesMix; 5179 if (GeneralMix) delete GeneralMix; 5180 if (ScoreMix) delete ScoreMix; 5181 if (MainMix) delete MainMix; 5182 5183 MainMix = new MFCD("MAIN.MIX", &FastKey); 5184 5185 assert(MainMix != NULL); 5186 // ConquerMix = new MFCD("CONQUER.MIX", &FastKey); 5187 5188 if (CCFileClass("MOVIES1.MIX").Is_Available()) 5189 { 5190 MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); 5191 } 5192 else 5193 { 5194 MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); 5195 } 5196 assert(MoviesMix != NULL); 5197 GeneralMix = new MFCD("GENERAL.MIX", &FastKey); 5198 ScoreMix = new MFCD("SCORES.MIX", &FastKey); 5199 ThemeClass::Scan(); 5200 } 5201 5202 if (theme_playing != THEME_NONE) 5203 { 5204 Theme.Queue_Song(theme_playing); 5205 } 5206 5207 return(true); 5208 } 5209 5210 5211 #endif // FIXIT_VERSION_3 5212 #endif // ST - 5/13/2019 5213 5214 5215 /*************************************************************************** 5216 * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * 5217 * * 5218 * INPUT: none * 5219 * * 5220 * OUTPUT: returns amount of free disk space * 5221 * * 5222 * HISTORY: * 5223 * 08/11/1995 PWG : Created. * 5224 *=========================================================================*/ 5225 unsigned long Disk_Space_Available(void) 5226 { 5227 return 0x7fffffff; //ST - 5/8/2019 5228 #if (0) 5229 struct diskfree_t diskdata; 5230 unsigned drive; 5231 5232 _dos_getdrive(&drive); 5233 _dos_getdiskfree(drive, &diskdata); 5234 5235 return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); 5236 #endif 5237 } 5238 5239 5240 /*********************************************************************************************** 5241 * Do_Record_Playback -- handles saving/loading map pos & current object * 5242 * * 5243 * INPUT: * 5244 * none. * 5245 * * 5246 * OUTPUT: * 5247 * none. * 5248 * * 5249 * WARNINGS: * 5250 * none. * 5251 * * 5252 * HISTORY: * 5253 * 08/15/1995 BRR : Created. * 5254 *=============================================================================================*/ 5255 static void Do_Record_Playback(void) 5256 { 5257 int count; 5258 TARGET tgt; 5259 int i; 5260 COORDINATE coord; 5261 ObjectClass * obj; 5262 unsigned long sum; 5263 unsigned long sum2; 5264 unsigned long ltgt; 5265 5266 /* 5267 ** Record a game 5268 */ 5269 if (Session.Record) { 5270 5271 /* 5272 ** Save the map's location 5273 */ 5274 Session.RecordFile.Write(&Map.DesiredTacticalCoord, 5275 sizeof (Map.DesiredTacticalCoord)); 5276 5277 /* 5278 ** Save the current object list count 5279 */ 5280 count = CurrentObject.Count(); 5281 Session.RecordFile.Write(&count, sizeof(count)); 5282 5283 /* 5284 ** Save a CRC of the selected-object list. 5285 */ 5286 sum = 0; 5287 for (i = 0; i < count; i++) { 5288 ltgt = (unsigned long)(CurrentObject[i]->As_Target()); 5289 sum += ltgt; 5290 } 5291 Session.RecordFile.Write (&sum, sizeof(sum)); 5292 5293 /* 5294 ** Save all selected objects. 5295 */ 5296 for (i = 0; i < count; i++) { 5297 tgt = CurrentObject[i]->As_Target(); 5298 Session.RecordFile.Write (&tgt, sizeof(tgt)); 5299 } 5300 5301 // 5302 // Save team-selection and formation events 5303 // 5304 Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent)); 5305 Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber)); 5306 Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent)); 5307 Session.RecordFile.Write (TeamFormData, sizeof(TeamFormData)); 5308 Session.RecordFile.Write (&FormMove, sizeof(FormMove)); 5309 Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed)); 5310 Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed)); 5311 TeamEvent = 0; 5312 TeamNumber = 0; 5313 FormationEvent = 0; 5314 } 5315 5316 /* 5317 ** Play back a game ("attract" mode) 5318 */ 5319 if (Session.Play) { 5320 5321 /* 5322 ** Read & set the map's location. 5323 */ 5324 if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { 5325 if (coord != Map.DesiredTacticalCoord) { 5326 Map.Set_Tactical_Position(coord); 5327 } 5328 } 5329 5330 if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) { 5331 /* 5332 ** Compute a CRC of the current object-selection list. 5333 */ 5334 sum = 0; 5335 for (i = 0; i < CurrentObject.Count(); i++) { 5336 ltgt = (unsigned long)(CurrentObject[i]->As_Target()); 5337 sum += ltgt; 5338 } 5339 5340 /* 5341 ** Load the CRC of the objects on disk; if it doesn't match, select 5342 ** all objects as they're loaded. 5343 */ 5344 Session.RecordFile.Read (&sum2, sizeof(sum2)); 5345 if (sum2 != sum) { 5346 Unselect_All(); 5347 } 5348 5349 AllowVoice = true; 5350 5351 for (i = 0; i < count; i++) { 5352 if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { 5353 obj = As_Object(tgt); 5354 if (obj && (sum2 != sum)) { 5355 obj->Select(); 5356 AllowVoice = false; 5357 } 5358 } 5359 } 5360 5361 AllowVoice = true; 5362 5363 } 5364 5365 // 5366 // Save team-selection and formation events 5367 // 5368 Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent)); 5369 Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber)); 5370 Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent)); 5371 if (TeamEvent) { 5372 Handle_Team(TeamNumber, TeamEvent - 1); 5373 } 5374 if (FormationEvent) { 5375 Toggle_Formation(); 5376 } 5377 5378 Session.RecordFile.Read (TeamFormData, sizeof(TeamFormData)); 5379 Session.RecordFile.Read (&FormMove, sizeof(FormMove)); 5380 Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed)); 5381 Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed)); 5382 5383 /* 5384 ** The map isn't drawn in playback mode, so draw it here. 5385 */ 5386 Map.Render(); 5387 } 5388 } 5389 5390 5391 /*********************************************************************************************** 5392 * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. * 5393 * * 5394 * * 5395 * * 5396 * INPUT: Name of file to load * 5397 * * 5398 * OUTPUT: Ptr to loaded file * 5399 * * 5400 * WARNINGS: Caller is responsible for releasing the memory allocated * 5401 * * 5402 * * 5403 * HISTORY: * 5404 * 5/13/96 3:20PM ST : Created * 5405 *=============================================================================================*/ 5406 void * Hires_Load(char * name) 5407 { 5408 char filename[30]; 5409 int length; 5410 void * return_ptr; 5411 5412 #ifdef WIN32 5413 sprintf(filename, "H%s", name); 5414 #else 5415 strcpy(filename, name); 5416 #endif 5417 CCFileClass file (filename); 5418 5419 if (file.Is_Available()) { 5420 5421 length = file.Size(); 5422 return_ptr = new char[length]; 5423 file.Read(return_ptr, length); 5424 return (return_ptr); 5425 5426 } else { 5427 return (NULL); 5428 } 5429 } 5430 5431 5432 /*********************************************************************************************** 5433 * Crate_From_Name -- Given a crate name convert it to a crate type. * 5434 * * 5435 * Use this routine to convert an ASCII crate name into a crate type. If no match could * 5436 * be found, then CRATE_MONEY is assumed. * 5437 * * 5438 * INPUT: name -- Pointer to the crate name text to convert into a crate type. * 5439 * * 5440 * OUTPUT: Returns with the crate name converted into a crate type. * 5441 * * 5442 * WARNINGS: none * 5443 * * 5444 * HISTORY: * 5445 * 08/12/1996 JLB : Created. * 5446 *=============================================================================================*/ 5447 CrateType Crate_From_Name(char const * name) 5448 { 5449 if (name != NULL) { 5450 for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { 5451 if (stricmp(name, CrateNames[crate]) == 0) return(crate); 5452 } 5453 } 5454 return(CRATE_MONEY); 5455 } 5456 5457 5458 /*********************************************************************************************** 5459 * Owner_From_Name -- Convert an owner name into a bitfield. * 5460 * * 5461 * This will take an owner specification and convert it into a bitfield that represents * 5462 * it. Sometimes this will be just a single house bit, but other times it could be * 5463 * all the allies or soviet house bits combined. * 5464 * * 5465 * INPUT: text -- Pointer to the text to convert into a house bitfield. * 5466 * * 5467 * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with * 5468 * one bit per house type. * 5469 * * 5470 * WARNINGS: none * 5471 * * 5472 * HISTORY: * 5473 * 08/12/1996 JLB : Created. * 5474 *=============================================================================================*/ 5475 int Owner_From_Name(char const * text) 5476 { 5477 int ownable = 0; 5478 if (stricmp(text, "soviet") == 0) { 5479 ownable |= HOUSEF_SOVIET; 5480 } else { 5481 if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) { 5482 ownable |= HOUSEF_ALLIES; 5483 } else { 5484 HousesType h = HouseTypeClass::From_Name(text); 5485 if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) { 5486 ownable |= (1 << h); 5487 } 5488 } 5489 } 5490 return(ownable); 5491 } 5492 5493 5494 /*********************************************************************************************** 5495 * Shake_The_Screen -- Dispatcher that shakes the screen. * 5496 * * 5497 * This routine will shake the game screen the number of shakes requested. * 5498 * * 5499 * INPUT: shakes -- The number of shakes to shake the screen. * 5500 * house -- House to perform the shake for (or HOUSE_NONE if all players). * 5501 * * 5502 * OUTPUT: none * 5503 * * 5504 * WARNINGS: none * 5505 * * 5506 * HISTORY: * 5507 * 09/04/1996 BWG : Created. * 5508 *=============================================================================================*/ 5509 void Shake_The_Screen(int shakes, HousesType house) 5510 { 5511 for (char h = HOUSE_FIRST; h < HOUSE_COUNT; ++h) { 5512 if ((house != HOUSE_NONE) && (h != house)) { 5513 continue; 5514 } 5515 HouseClass* hptr = HouseClass::As_Pointer((HousesType)h); 5516 if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { 5517 hptr->ScreenShakeTime = CDTimerClass<FrameTimerClass>(hptr->ScreenShakeTime + shakes + shakes); 5518 hptr->ScreenShakeTime.Start(); 5519 } 5520 } 5521 #if (0) 5522 #ifdef WIN32 5523 shakes += shakes; 5524 5525 Hide_Mouse(); 5526 SeenPage.Blit(HidPage); 5527 int oldyoff = 0; 5528 int newyoff = 0; 5529 while(shakes--) { 5530 int x = TickCount; 5531 // CountDownTimer = 1; 5532 do { 5533 newyoff = Sim_Random_Pick(0,2) - 1; 5534 } while (newyoff == oldyoff); 5535 switch (newyoff) { 5536 case -1: 5537 HidPage.Blit(SeenPage, 0,2, 0,0, 640,398); 5538 break; 5539 case 0: 5540 HidPage.Blit(SeenPage); 5541 break; 5542 case 1: 5543 HidPage.Blit(SeenPage, 0,0, 0,2, 640,398); 5544 break; 5545 } while (x == TickCount); 5546 // } while (CountDownTimer != 0) ; 5547 } 5548 HidPage.Blit(SeenPage); 5549 Show_Mouse(); 5550 #else 5551 Shake_Screen(shakes); 5552 #endif 5553 #endif 5554 } 5555 5556 5557 /*********************************************************************************************** 5558 * List_Copy -- Makes a copy of a cell offset list. * 5559 * * 5560 * This routine will make a copy of a cell offset list. It will only copy the significant * 5561 * elements of the list limited by the maximum length specified. * 5562 * * 5563 * INPUT: source -- Pointer to a cell offset list. * 5564 * * 5565 * len -- The maximum number of cell offset elements to store in to the * 5566 * destination list pointer. * 5567 * * 5568 * dest -- Pointer to the destination list to store the copy into. * 5569 * * 5570 * OUTPUT: none * 5571 * * 5572 * WARNINGS: Ensure that the destination list is large enough to hold the list copy. * 5573 * * 5574 * HISTORY: * 5575 * 09/04/1996 JLB : Created. * 5576 *=============================================================================================*/ 5577 void List_Copy(short const * source, int len, short * dest) 5578 { 5579 if (dest == NULL || dest == NULL) { 5580 return; 5581 } 5582 5583 while (len > 0) { 5584 *dest = *source; 5585 if (*dest == REFRESH_EOL) break; 5586 dest++; 5587 source++; 5588 len--; 5589 } 5590 } 5591 5592 5593 5594 #if 0 5595 // 5596 // Boy, this function sure is crummy 5597 // 5598 void Crummy(int crumb1, int crumb2) 5599 { 5600 if (Debug_Check_Map && Debug_Heap_Dump) { 5601 Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2); 5602 } 5603 } 5604 #endif 5605 5606 5607 5608 /*********************************************************************************************** 5609 * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. 5610 * This is located under HKEY_LOCAL_MACHINE. 5611 * HISTORY: 5612 * 11/19/98 ajw : Created 5613 *=============================================================================================*/ 5614 const char* Game_Registry_Key() 5615 { 5616 #ifdef ENGLISH 5617 static char szKey[] = "SOFTWARE\\Westwood\\Red Alert Windows 95 Edition"; 5618 #else 5619 #ifdef GERMAN 5620 static char szKey[] = "SOFTWARE\\Westwood\\Alarmstufe Rot Windows 95 Edition"; 5621 #else 5622 static char szKey[] = "SOFTWARE\\Westwood\\Alerte Rouge version Windows 95"; 5623 #endif 5624 #endif 5625 return szKey; 5626 } 5627 5628 5629 /*********************************************************************************************** 5630 * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion * 5631 * * 5632 * * 5633 * * 5634 * INPUT: Nothing * 5635 * * 5636 * OUTPUT: true if Counterstrike is present * 5637 * * 5638 * WARNINGS: None * 5639 * * 5640 * HISTORY: * 5641 * 4/1/97 11:39PM ST : Created * 5642 *=============================================================================================*/ 5643 bool Is_Counterstrike_Installed (void) 5644 { 5645 return true; // Remasters always have Counterstrike. ST - 10/18/2019 11:06AM 5646 5647 #if (0) 5648 // ajw 9/29/98 5649 static bool bAlreadyChecked = false; 5650 static bool bInstalled = false; 5651 5652 if( !bAlreadyChecked ) 5653 { 5654 HKEY hKey; 5655 if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) 5656 return false; 5657 DWORD dwValue; 5658 DWORD dwBufSize = sizeof( DWORD ); 5659 if( RegQueryValueEx( hKey, "CStrikeInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 5660 bInstalled = false; 5661 else 5662 bInstalled = (bool)dwValue; // (Presumably true, if it's there...) 5663 5664 RegCloseKey( hKey ); 5665 bAlreadyChecked = true; 5666 } 5667 return bInstalled; 5668 5669 // RawFileClass file("EXPAND.MIX"); 5670 // return(file.Is_Available()); 5671 #endif 5672 } 5673 5674 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5675 /*********************************************************************************************** 5676 *=============================================================================================*/ 5677 bool Is_Aftermath_Installed (void) 5678 { 5679 return true; // Remasters always have Aftermath. ST - 10/18/2019 11:06AM 5680 5681 #if (0) 5682 // ajw 9/29/98 5683 static bool bAlreadyChecked = false; 5684 static bool bInstalled = false; 5685 5686 if( !bAlreadyChecked ) 5687 { 5688 HKEY hKey; 5689 if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) 5690 return false; 5691 DWORD dwValue; 5692 DWORD dwBufSize = sizeof( DWORD ); 5693 if( RegQueryValueEx( hKey, "AftermathInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) 5694 bInstalled = false; 5695 else 5696 bInstalled = (bool)dwValue; // (Presumably true, if it's there...) 5697 5698 RegCloseKey( hKey ); 5699 bAlreadyChecked = true; 5700 } 5701 return bInstalled; 5702 5703 // RawFileClass file("EXPAND2.MIX"); 5704 // return(file.Is_Available()); 5705 #endif 5706 } 5707 #endif 5708 5709 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5710 void Enable_Secret_Units(void) 5711 { 5712 #if 0 5713 SecretUnitsEnabled=true; 5714 UnitTypeClass::As_Reference(UNIT_PHASE).Level=10; 5715 VesselTypeClass::As_Reference(VESSEL_CARRIER).Level=10; 5716 for (int index = 0; index < Buildings.Count(); index++) { 5717 Buildings.Ptr(index)->Update_Buildables(); 5718 } 5719 #endif 5720 } 5721 #endif 5722 5723 #ifdef FIXIT_VERSION_3 5724 bool Force_Scenario_Available( const char* szName ) 5725 { 5726 // Calls Force_CD_Available based on type of scenario. szName is assumed to be an official scenario here. 5727 if( Is_Mission_Counterstrike( (char*)szName ) ) 5728 { 5729 // debugprint( "Force_Scenario_Available requiring disk 4...\n" ); 5730 return Force_CD_Available( 4 ); 5731 } 5732 else if( Is_Mission_Aftermath( (char*)szName ) ) 5733 { 5734 // debugprint( "Force_Scenario_Available requiring disk 3...\n" ); 5735 return Force_CD_Available( 3 ); 5736 } 5737 return true; 5738 } 5739 #endif