CONQUER.CPP (144701B)
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: F:\projects\c&c\vcs\code\conquer.cpv 2.18 16 Oct 1995 16:50:24 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : 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 * Disk_Space_Available -- returns bytes of free disk space * 35 * Do_Record_Playback -- handles saving/loading map pos & current object * 36 * Fading_Table_Name -- Builds a theater specific fading table name. * 37 * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * 38 * Force_CD_Available -- Ensures that specified CD is available. * 39 * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * 40 * Handle_Team -- Processes team selection command. * 41 * Handle_View -- Either records or restores the tactical view. * 42 * KN_To_Facing -- Converts a keyboard input number into a facing value. * 43 * Keyboard_Process -- Processes the tactical map input codes. * 44 * Language_Name -- Build filename for current language. * 45 * Main_Game -- Main game startup routine. * 46 * Main_Loop -- This is the main game loop (as a single loop). * 47 * Map_Edit_Loop -- a mini-main loop for map edit mode only * 48 * Message_Input -- allows inter-player message input processing * 49 * MixFileHandler -- Handles VQ file access. * 50 * Name_From_Source -- retrieves the name for the given SourceType * 51 * Play_Movie -- Plays a VQ movie. * 52 * Source_From_Name -- Converts ASCII name into SourceType. * 53 * Sync_Delay -- Forces the game into a 15 FPS rate. * 54 * Theater_From_Name -- Converts ASCII name into a theater number. * 55 * Trap_Object -- gets a ptr to object of given type & coord * 56 * Unselect_All -- Causes all selected objects to become unselected. * 57 * VQ_Call_Back -- Maintenance callback used for VQ movies. * 58 * Validate_Error -- prints an error message when an object fails validation * 59 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 60 61 #include "function.h" 62 #include "tcpip.h" 63 #include <stdlib.h> 64 #include <stdio.h> 65 #include <string.h> 66 #include <direct.h> 67 #include <fcntl.h> 68 #include <io.h> 69 #include <dos.h> 70 #include <share.h> 71 #include <malloc.h> 72 #include "ccdde.h" 73 74 #define SHAPE_TRANS 0x40 75 76 void *Get_Shape_Header_Data(void *ptr); 77 78 /**************************************** 79 ** Function prototypes for this module ** 80 *****************************************/ 81 bool Main_Loop(); 82 void Keyboard_Process(KeyNumType & input); 83 #ifndef DEMO 84 static void Message_Input(KeyNumType &input); 85 #endif 86 bool Color_Cycle(void); 87 bool Map_Edit_Loop(void); 88 void Trap_Object(void); 89 90 #ifdef CHEAT_KEYS 91 void Heap_Dump_Check( char *string ); 92 void Dump_Heap_Pointers( void ); 93 void Error_In_Heap_Pointers( char *string ); 94 #endif 95 static void Do_Record_Playback(void); 96 extern void Register_Game_Start_Time(void); 97 extern void Register_Game_End_Time(void); 98 extern void Send_Statistics_Packet(void); 99 extern "C" { 100 extern char *__nheapbeg; 101 } 102 bool InMainLoop = false; 103 104 #ifndef ARRAY_SIZE 105 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 106 #endif 107 108 /*********************************************************************************************** 109 * Main_Game -- Main game startup routine. * 110 * * 111 * This is the first official routine of the game. It handles game initialization and * 112 * the main game loop control. * 113 * * 114 * Initialization: * 115 * - Init_Game handles one-time-only inits * 116 * - Select_Game is responsible for initializations required for each new game played * 117 * (these may be different depending on whether a multiplayer game is selected, and * 118 * other parameters) * 119 * - This routine performs any un-inits required, both for each game played, and one-time * 120 * * 121 * INPUT: argc -- Number of command line arguments (including program name itself). * 122 * * 123 * argv -- Array of command line argument pointers. * 124 * * 125 * OUTPUT: none * 126 * * 127 * WARNINGS: none * 128 * * 129 * HISTORY: * 130 * 10/01/1994 JLB : Created. * 131 *=============================================================================================*/ 132 extern int TotalLocks; 133 extern bool Spawn_WChat(bool can_launch); 134 extern bool SpawnedFromWChat; 135 void Main_Game(int argc, char *argv[]) 136 { 137 bool fade = false; // don't fade title screen the first time through 138 139 /* 140 ** Perform one-time-only initializations 141 */ 142 if (!Init_Game(argc, argv)) { 143 return; 144 } 145 146 CCDebugString ("C&C95 - Game initialisation complete.\n"); 147 /* 148 ** Game processing loop: 149 ** 1) Select which game to play, or whether to exit (don't fade the palette 150 ** on the first game selection, but fade it in on subsequent calls) 151 ** 2) Invoke either the main-loop routine, or the editor-loop routine, 152 ** until they indicate that the user wants to exit the scenario. 153 */ 154 while (Select_Game(fade)) { 155 156 if (RunningAsDLL) { 157 return; 158 } 159 ScenarioInit = 0; // Kludge. 160 // Theme.Queue_Song(THEME_PICK_ANOTHER); 161 162 fade = true; 163 164 /* 165 ** Make the game screen visible, clear the keyboard buffer of spurious 166 ** values, and then show the mouse. This PRESUMES that Select_Game() has 167 ** told the map to draw itself. 168 */ 169 Fade_Palette_To(GamePalette, FADE_PALETTE_MEDIUM, NULL); 170 Keyboard::Clear(); 171 172 /* 173 ** Only show the mouse if we're not playing back a recording. 174 */ 175 if (PlaybackGame) { 176 Hide_Mouse(); 177 } else { 178 Show_Mouse(); 179 } 180 181 SpecialDialog = SDLG_NONE; 182 //Start_Profiler(); 183 if (GameToPlay == GAME_INTERNET){ 184 Register_Game_Start_Time(); 185 GameStatisticsPacketSent = false; 186 PacketLater = NULL; 187 ConnectionLost = false; 188 }else{ 189 DDEServer.Disable(); 190 } 191 192 InMainLoop = true; 193 194 #ifdef SCENARIO_EDITOR 195 /* 196 ** Scenario-editor version of main-loop processing 197 */ 198 for (;;) { 199 200 /* 201 ** Non-scenario-editor-mode: call the game's main loop 202 */ 203 if (!Debug_Map) { 204 TotalLocks=0; 205 if (Main_Loop()) { 206 break; 207 } 208 209 if (SpecialDialog != SDLG_NONE) { 210 //Stop_Profiler(); 211 switch (SpecialDialog) { 212 case SDLG_SPECIAL: 213 Map.Help_Text(TXT_NONE); 214 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 215 Special_Dialog(); 216 Map.Revert_Mouse_Shape(); 217 SpecialDialog = SDLG_NONE; 218 break; 219 220 case SDLG_OPTIONS: 221 Map.Help_Text(TXT_NONE); 222 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 223 Options.Process(); 224 Map.Revert_Mouse_Shape(); 225 SpecialDialog = SDLG_NONE; 226 break; 227 228 case SDLG_SURRENDER: 229 Map.Help_Text(TXT_NONE); 230 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 231 if (Surrender_Dialog()) { 232 OutList.Add(EventClass(EventClass::DESTRUCT)); 233 } 234 SpecialDialog = SDLG_NONE; 235 Map.Revert_Mouse_Shape(); 236 break; 237 238 default: 239 break; 240 } 241 } 242 } else { 243 244 /* 245 ** Scenario-editor-mode: call the editor's main loop 246 */ 247 if (Map_Edit_Loop()) { 248 break; 249 } 250 } 251 } 252 #else 253 /* 254 ** Non-editor version of main-loop processing 255 */ 256 for (;;) { 257 258 /* 259 ** Call the game's main loop 260 */ 261 TotalLocks=0; 262 if (Main_Loop()) { 263 break; 264 } 265 266 /* 267 ** If the SpecialDialog flag is set, invoke the given special dialog. 268 ** This must be done outside the main loop, since the dialog will call 269 ** Main_Loop(), allowing the game to run in the background. 270 */ 271 if (SpecialDialog != SDLG_NONE) { 272 //Stop_Profiler(); 273 switch (SpecialDialog) { 274 case SDLG_SPECIAL: 275 Map.Help_Text(TXT_NONE); 276 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 277 Special_Dialog(); 278 Map.Revert_Mouse_Shape(); 279 SpecialDialog = SDLG_NONE; 280 break; 281 282 case SDLG_OPTIONS: 283 Map.Help_Text(TXT_NONE); 284 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 285 Options.Process(); 286 Map.Revert_Mouse_Shape(); 287 SpecialDialog = SDLG_NONE; 288 break; 289 290 case SDLG_SURRENDER: 291 Map.Help_Text(TXT_NONE); 292 Map.Override_Mouse_Shape(MOUSE_NORMAL, false); 293 if (Surrender_Dialog()) { 294 OutList.Add(EventClass(EventClass::DESTRUCT)); 295 } 296 SpecialDialog = SDLG_NONE; 297 Map.Revert_Mouse_Shape(); 298 break; 299 300 default: 301 break; 302 } 303 } 304 } 305 #endif 306 //Stop_Profiler(); 307 InMainLoop = false; 308 309 if (!GameStatisticsPacketSent && PacketLater){ 310 Send_Statistics_Packet(); 311 } 312 313 /* 314 ** Scenario is done; fade palette to black 315 */ 316 Fade_Palette_To(BlackPalette, FADE_PALETTE_SLOW, NULL); 317 VisiblePage.Clear(); 318 319 #ifndef DEMO 320 /* 321 ** Un-initialize whatever needs it, for each game played. 322 ** 323 ** Shut down either the modem or network; they'll get re-initialized if 324 ** the user selections those options again in Select_Game(). This 325 ** "re-boots" the modem & network code, which I currently feel is safer 326 ** than just letting it hang around. 327 ** (Skip this step if we're in playback mode; the modem or net won't have 328 ** been initialized in that case.) 329 */ 330 if ( (RecordGame && !SuperRecord) || PlaybackGame) { 331 RecordFile.Close(); 332 } 333 334 if (!PlaybackGame){ 335 336 switch (GameToPlay){ 337 case GAME_NULL_MODEM: 338 case GAME_MODEM: 339 //ST - 1/2/2019 4:04PM 340 //Modem_Signoff(); 341 break; 342 343 case GAME_IPX: 344 Shutdown_Network(); 345 break; 346 347 case GAME_INTERNET: 348 //Winsock.Close(); 349 break; 350 } 351 } 352 353 354 /* 355 ** If we're playing back, the mouse will be hidden; show it. 356 ** Also, set all variables back to normal, to return to the main menu. 357 */ 358 if (PlaybackGame) { 359 Show_Mouse(); 360 GameToPlay = GAME_NORMAL; 361 PlaybackGame = 0; 362 } 363 364 365 /* 366 ** If we were spawned from WChat then dont go back to the main menu - just quit 367 ** 368 ** New: If spawned from WChat then maximise WChat and go back to the main menu after all 369 */ 370 #ifdef FORCE_WINSOCK 371 if (Special.IsFromWChat){ 372 Shutdown_Network(); // Clear up the pseudo IPX stuff 373 Winsock.Close(); 374 Special.IsFromWChat = false; 375 SpawnedFromWChat = false; 376 DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice 377 GameToPlay = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu 378 Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us 379 //break; 380 } 381 #endif //FORCE_WINSOCK 382 383 #endif //DEMO 384 385 } 386 387 #ifdef DEMO 388 Hide_Mouse(); 389 Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); 390 Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); 391 Blit_Hid_Page_To_Seen_Buff(); 392 Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, NULL); 393 Clear_KeyBuffer(); 394 Get_Key(); 395 Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); 396 // Show_Mouse(); 397 #else 398 399 /* 400 ** Free the scenario description buffers 401 */ 402 Free_Scenario_Descriptions(); 403 #endif 404 405 #ifndef NOMEMCHECK 406 Uninit_Game(); 407 #endif 408 409 } 410 411 412 /*********************************************************************************************** 413 * Keyboard_Process -- Processes the tactical map input codes. * 414 * * 415 * This routine is used to process the input codes while the player * 416 * has the tactical map displayed. It handles all the keys that * 417 * are appropriate to that mode. * 418 * * 419 * INPUT: input -- Input code as returned from Input_Num(). * 420 * * 421 * OUTPUT: none * 422 * * 423 * WARNINGS: none * 424 * * 425 * HISTORY: * 426 * 01/21/1992 JLB : Created. * 427 * 07/04/1995 JLB : Handles team and map control hotkeys. * 428 *=============================================================================================*/ 429 extern int DebugColour; 430 void Keyboard_Process(KeyNumType &input) 431 { 432 ObjectClass * obj; 433 int index; 434 435 /* 436 ** Don't do anything if there is not keyboard event. 437 */ 438 if (input == KN_NONE) { 439 return; 440 } 441 442 #ifndef DEMO 443 /* 444 ** For network & modem, process user input for inter-player messages. 445 */ 446 Message_Input(input); 447 #endif 448 /* 449 ** Use WWKEY values because KN values have WWKEY_VK_BIT or'd in with them 450 ** and we need WWKEY_VK_BIT to still be set if it is. 451 */ 452 KeyNumType plain = input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT); 453 454 #ifdef CHEAT_KEYS 455 456 if (Debug_Flag) { 457 switch (input) { 458 case (int)KN_M|(int)KN_SHIFT_BIT: 459 case (int)KN_M|(int)KN_ALT_BIT: 460 case (int)KN_M|(int)KN_CTRL_BIT: 461 PlayerPtr->Credits += 10000; 462 break; 463 464 default: 465 break; 466 } 467 } 468 #endif 469 470 #ifdef VIRGIN_CHEAT_KEYS 471 if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { 472 PlayerPtr->Blockage = false; 473 PlayerPtr->Flag_To_Win(); 474 } 475 #endif 476 477 //#ifdef CHEAT_KEYS 478 if (/*Debug_Playtest && */input == (KN_W|KN_ALT_BIT)) { 479 PlayerPtr->Blockage = false; 480 PlayerPtr->Flag_To_Win(); 481 } 482 483 if (Debug_Flag && input == KN_SLASH) { 484 if (GameToPlay != GAME_NORMAL) { 485 SpecialDialog = SDLG_SPECIAL; 486 input = KN_NONE; 487 } else { 488 Special_Dialog(); 489 } 490 } 491 //#endif 492 493 /* 494 ** If the options key(s) were pressed, then bring up the options screen. 495 */ 496 if (input == KN_SPACE || input == KN_ESC) { 497 Map.Help_Text(TXT_NONE); // Turns off help text. 498 Queue_Options(); 499 input = KN_NONE; 500 //DebugColour++; 501 //DebugColour &=7; 502 } 503 504 /* 505 ** Process prerecorded team selection. This will be an addative select 506 ** if the SHIFT key is held down. It will create the team if the 507 ** CTRL or ALT key is held down. 508 */ 509 int action = 0; 510 if (input & WWKEY_SHIFT_BIT) action = 1; 511 if (input & WWKEY_ALT_BIT) action = 3; 512 if (input & WWKEY_CTRL_BIT) action = 2; 513 514 switch (KN_To_VK(plain)) { 515 516 /* 517 ** Center the map around the currently selected objects. If no 518 ** objects are selected, then fall into the home case. 519 */ 520 case VK_HOME: 521 if (CurrentObject.Count()) { 522 Map.Center_Map(); 523 Map.Flag_To_Redraw(true); 524 break; 525 } 526 // Fall into next case. 527 528 /* 529 ** Center the map about the construction yard or construction vehicle 530 ** if one is present. 531 */ 532 case VK_H: 533 for (index = 0; index < Units.Count(); index++) { 534 UnitClass * unit = Units.Ptr(index); 535 536 if (unit && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { 537 Unselect_All(); 538 unit->Select(); 539 break; 540 } 541 } 542 for (index = 0; index < Buildings.Count(); index++) { 543 BuildingClass * building = Buildings.Ptr(index); 544 545 if (building && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { 546 Unselect_All(); 547 building->Select(); 548 break; 549 } 550 } 551 Map.Center_Map(); 552 Map.Flag_To_Redraw(true); 553 break; 554 555 #ifdef CHEAT_KEYS 556 /* 557 ** Toggle free scrolling mode. 558 */ 559 case VK_F: 560 Options.IsFreeScroll = (Options.IsFreeScroll == false); 561 break; 562 #endif 563 564 /* 565 ** If the "N" key is pressed, then select the next object. 566 */ 567 case VK_N: 568 if (action) { 569 obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 570 } else { 571 obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); 572 } 573 if (obj) { 574 Unselect_All(); 575 obj->Select(); 576 Map.Center_Map(); 577 Map.Flag_To_Redraw(true); 578 } 579 break; 580 581 /* 582 ** For multiplayer, 'R' pops up the surrender dialog. 583 */ 584 case VK_R: 585 if (/*GameToPlay != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { 586 SpecialDialog = SDLG_SURRENDER; 587 input = KN_NONE; 588 } 589 break; 590 591 /* 592 ** Handle making and breaking alliances. 593 */ 594 case VK_A: 595 if (GameToPlay != GAME_NORMAL || Debug_Flag) { 596 if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { 597 if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { 598 OutList.Add(EventClass(EventClass::ALLY, (int)CurrentObject[0]->Owner())); 599 } 600 } 601 } 602 break; 603 604 /* 605 ** Control the remembered tactical location. 606 */ 607 case VK_F7: 608 case VK_F8: 609 case VK_F9: 610 case VK_F10: 611 if (!Debug_Map) { 612 Handle_View(KN_To_VK(plain) - VK_F7, action); 613 } 614 break; 615 #if (0) 616 case VK_F11: 617 Winsock.Set_Protocol_UDP(FALSE); 618 break; 619 620 case VK_F12: 621 Winsock.Set_Protocol_UDP(TRUE); 622 break; 623 #endif //(0) 624 625 626 /* 627 ** Control the custom team select state. 628 */ 629 case VK_1: 630 case VK_2: 631 case VK_3: 632 case VK_4: 633 case VK_5: 634 case VK_6: 635 case VK_7: 636 case VK_8: 637 case VK_9: 638 case VK_0: 639 Handle_Team(KN_To_VK(plain) - VK_1, action); 640 break; 641 642 /* 643 ** All selected units will go into idle mode. 644 */ 645 case VK_S: 646 if (CurrentObject.Count()) { 647 for (int index = 0; index < CurrentObject.Count(); index++) { 648 ObjectClass const * tech = CurrentObject[index]; 649 650 if (tech && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && 651 tech->What_Am_I() != RTTI_BUILDING))) { 652 OutList.Add(EventClass(EventClass::IDLE, tech->As_Target())); 653 } 654 } 655 } 656 break; 657 658 /* 659 ** All selected units will attempt to scatter. 660 */ 661 case VK_X: 662 if (CurrentObject.Count()) { 663 for (int index = 0; index < CurrentObject.Count(); index++) { 664 ObjectClass const * tech = CurrentObject[index]; 665 666 if (tech && tech->Can_Player_Move()) { 667 OutList.Add(EventClass(EventClass::SCATTER, tech->As_Target())); 668 } 669 } 670 } 671 break; 672 673 /* 674 ** All selected units will attempt to go into guard area mode. 675 */ 676 case VK_G: 677 if (CurrentObject.Count()) { 678 for (int index = 0; index < CurrentObject.Count(); index++) { 679 ObjectClass const * tech = CurrentObject[index]; 680 681 if (tech && tech->Can_Player_Move() && tech->Can_Player_Fire()) { 682 OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA)); 683 } 684 } 685 } 686 break; 687 688 default: 689 break; 690 } 691 692 #ifdef NEVER 693 FacingType facing = KN_To_Facing(input); 694 695 /* 696 ** Scroll the map according to the cursor key pressed. 697 */ 698 if (facing != FACING_NONE) { 699 Map.Scroll_Map(facing); 700 input = 0; 701 facing = FACING_NONE; 702 } 703 #endif 704 705 #ifdef NEVER 706 /* 707 ** If the <TAB> key is pressed, then select the next object. 708 */ 709 if (input == KN_TAB) { 710 ObjectClass * obj = Map.Next_Object(CurrentObject); 711 if (obj) { 712 if (CurrentObject) { 713 CurrentObject->Unselect(); 714 } 715 obj->Select(); 716 } 717 } 718 #endif 719 720 #ifdef CHEAT_KEYS 721 if (Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { 722 Debug_Key(input); 723 } 724 #endif 725 } 726 727 728 #ifndef DEMO 729 /*********************************************************************************************** 730 * Message_Input -- allows inter-player message input processing * 731 * * 732 * INPUT: * 733 * input key value * 734 * * 735 * OUTPUT: * 736 * none. * 737 * * 738 * WARNINGS: * 739 * MAX_MESSAGE_LENGTH has increased over the DOS version. COMPAT_MESSAGE_LENGTH reflects * * 740 * the length of the DOS message and also the length of the message in the packet header. * 741 * To allow transmission of longer messages I split the message into COMPAT_MESSAGE_LENGTH-4 * 742 * sized chunks and use the extra space after the zero terminator to specify which segment * 743 * of the whole message this is and also to supply a crc for the string. * 744 * This allows message segments to arrive out of order and still be displayed correctly. * 745 * * 746 * HISTORY: * 747 * 05/22/1995 BRR : Created. * 748 * 03/26/1995 ST : Modified to break up longer messages into multiple packets * 749 *=============================================================================================*/ 750 static void Message_Input(KeyNumType &input) 751 { 752 int rc; 753 char txt[MAX_MESSAGE_LENGTH+12]; 754 int id; 755 //PG_TO_FIX 756 #if (0) 757 SerialPacketType *serial_packet; 758 int i; 759 int message_length; 760 int sent_so_far; 761 unsigned short magic_number; 762 unsigned short crc; 763 #endif 764 int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; 765 766 767 /* 768 ** Check keyboard input for a request to send a message. 769 ** The 'to' argument for Add_Edit is prefixed to the message buffer; the 770 ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. 771 ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the 772 ** 'to' portion. At the other end, the buffer allocated to display the 773 ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". 774 */ 775 if (input >= KN_F1 && input < (KN_F1 + MPlayerMax) && 776 Messages.Get_Edit_Buf()==NULL) { 777 memset (txt, 0, 40); 778 779 /* 780 ** For a serial game, send a message on F1 or F4; set 'txt' to the 781 ** "Message:" string & add an editable message to the list. 782 */ 783 if (GameToPlay==GAME_NULL_MODEM 784 || GameToPlay==GAME_MODEM){ 785 //|| GameToPlay == GAME_INTERNET) { 786 if (input==KN_F1 || input==(KN_F1 + MPlayerMax - 1)) { 787 788 strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" 789 790 Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], 791 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); 792 793 Map.Flag_To_Redraw(false); 794 } 795 } else { 796 797 /* 798 ** For a network game: 799 ** F1-F3 = "To <name> (house):" (only allowed if we're not in ObiWan mode) 800 ** F4 = "To All:" 801 */ 802 if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { 803 if (input==(KN_F1 + MPlayerMax - 1) && Messages.Get_Edit_Buf()==NULL) { 804 805 MessageAddress = IPXAddressClass(); // set to broadcast 806 strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" 807 808 Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], 809 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); 810 811 Map.Flag_To_Redraw(false); 812 } else { 813 if (Messages.Get_Edit_Buf()==NULL) { 814 if ((input - KN_F1) < Ipx.Num_Connections() && !MPlayerObiWan) { 815 816 id = Ipx.Connection_ID(input - KN_F1); 817 MessageAddress = (*(Ipx.Connection_Address (id))); 818 sprintf(txt,Text_String(TXT_TO),Ipx.Connection_Name(id)); 819 820 Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], 821 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); 822 823 Map.Flag_To_Redraw(false); 824 } 825 } 826 } 827 } 828 } 829 } 830 831 /* 832 ** Function key input is meaningless beyond this point 833 */ 834 if (input >= KN_F1 && input <= KN_F10) return; 835 if (input >= KN_F11 && input <= KN_F12) return; 836 837 /* 838 ** Process message-system input; send the message out if RETURN is hit. 839 */ 840 rc = Messages.Input(input); 841 842 /* 843 ** If a single character has been added to an edit buffer, update the display. 844 */ 845 if (rc == 1) { 846 Map.Flag_To_Redraw(false); 847 } 848 849 /* 850 ** If backspace was hit, redraw the map. This assumes the map is going to 851 ** completely refresh all cells covered by the messages. Set DisplayClass's 852 ** IsToRedraw to true to tell it to re-compute the cells that it needs to 853 ** redraw. 854 */ 855 if (rc==2) { 856 Map.Flag_To_Redraw(false); 857 Map.DisplayClass::IsToRedraw = true; 858 } 859 860 861 /* 862 ** Send a message 863 */ 864 if (rc==3) { 865 // 866 // PG_TO_FIX 867 #if (0) 868 /*..................................................................... 869 Store this message in our LastMessage buffer; the computer may send 870 us a version of it later. 871 .....................................................................*/ 872 if (strlen(Messages.Get_Edit_Buf())) { 873 strcpy(LastMessage,Messages.Get_Edit_Buf()); 874 } 875 876 message_length = strlen(Messages.Get_Edit_Buf()); 877 878 long actual_message_size; 879 char *the_string; 880 881 /* 882 ** Serial game: fill in a SerialPacketType & send it. 883 ** (Note: The size of the SerialPacketType.Command must be the same as 884 ** the EventClass.Type!) 885 */ 886 if (GameToPlay==GAME_NULL_MODEM 887 || GameToPlay==GAME_MODEM){ 888 //|| GameToPlay==GAME_INTERNET) { 889 890 sent_so_far = 0; 891 magic_number = MESSAGE_HEAD_MAGIC_NUMBER; 892 crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); 893 894 while (sent_so_far < message_length){ 895 896 serial_packet = (SerialPacketType *)NullModem.BuildBuf; 897 898 serial_packet->Command = SERIAL_MESSAGE; 899 strcpy (serial_packet->Name, MPlayerName); 900 memcpy (serial_packet->Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); 901 902 /* 903 ** Steve I's stuff for splitting message on word boundries 904 */ 905 actual_message_size = COMPAT_MESSAGE_LENGTH - 5; 906 907 /* Start at the end of the message and find a space with 10 chars. */ 908 the_string = serial_packet->Message; 909 while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && 910 the_string[actual_message_size] != ' '){ 911 --actual_message_size; 912 } 913 if ( the_string[actual_message_size] == ' ' ){ 914 915 /* Now delete the extra characters after the space (they musnt print) */ 916 for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ 917 the_string[i + actual_message_size] = 0xff; 918 } 919 }else{ 920 actual_message_size = COMPAT_MESSAGE_LENGTH - 5; 921 } 922 923 924 *(serial_packet->Message + COMPAT_MESSAGE_LENGTH-5) = 0; 925 /* 926 ** Flag this message segment as either a message head or a message tail. 927 */ 928 *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; 929 *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2)) = crc; 930 serial_packet->ID = MPlayerLocalID; 931 932 NullModem.Send_Message(NullModem.BuildBuf, sizeof(SerialPacketType), 1); 933 934 magic_number++; 935 sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; 936 } 937 938 } else { 939 940 /* 941 ** Network game: fill in a GlobalPacketType & send it. 942 */ 943 if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { 944 945 sent_so_far = 0; 946 magic_number = MESSAGE_HEAD_MAGIC_NUMBER; 947 crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); 948 949 while (sent_so_far < message_length){ 950 951 GPacket.Command = NET_MESSAGE; 952 strcpy (GPacket.Name, MPlayerName); 953 memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); 954 955 /* 956 ** Steve I's stuff for splitting message on word boundries 957 */ 958 actual_message_size = COMPAT_MESSAGE_LENGTH - 5; 959 960 /* Start at the end of the message and find a space with 10 chars. */ 961 the_string = GPacket.Message.Buf; 962 while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && 963 the_string[actual_message_size] != ' '){ 964 --actual_message_size; 965 } 966 if ( the_string[actual_message_size] == ' ' ){ 967 968 /* Now delete the extra characters after the space (they musnt print) */ 969 for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ 970 the_string[i + actual_message_size] = 0xff; 971 } 972 }else{ 973 actual_message_size = COMPAT_MESSAGE_LENGTH - 5; 974 } 975 976 *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; 977 /* 978 ** Flag this message segment as either a message head or a message tail. 979 */ 980 *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; 981 *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; 982 983 GPacket.Message.ID = MPlayerLocalID; 984 GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); 985 986 /* 987 ** If 'F4' was hit, MessageAddress will be a broadcast address; send 988 ** the message to every player we have a connection with. 989 */ 990 if (MessageAddress.Is_Broadcast()) { 991 for (i = 0; i < Ipx.Num_Connections(); i++) { 992 Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, 993 Ipx.Connection_Address(Ipx.Connection_ID(i))); 994 Ipx.Service(); 995 } 996 } else { 997 998 /* 999 ** Otherwise, MessageAddress contains the exact address to send to. 1000 ** Send to that address only. 1001 */ 1002 Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, 1003 &MessageAddress); 1004 Ipx.Service(); 1005 } 1006 1007 magic_number++; 1008 sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; 1009 } 1010 } 1011 1012 } 1013 1014 /* 1015 ** Tell the map to completely update itself, since a message is now missing. 1016 */ 1017 Map.Flag_To_Redraw(true); 1018 #endif 1019 } 1020 } 1021 #endif 1022 1023 1024 /*********************************************************************************************** 1025 * Color_Cycle -- Handle the general palette color cycling. * 1026 * * 1027 * This is a maintenance routine that handles the color cycling. It should be called as * 1028 * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * 1029 * * 1030 * INPUT: none * 1031 * * 1032 * OUTPUT: true if palette changed * 1033 * * 1034 * WARNINGS: none * 1035 * * 1036 * HISTORY: * 1037 * 05/31/1994 JLB : Created. * 1038 * 06/10/1994 JLB : Uses new cycle color values. * 1039 * 12/21/1994 JLB : Handles text fade color. * 1040 *=============================================================================================*/ 1041 bool Color_Cycle(void) 1042 { 1043 static CountDownTimerClass _timer(BT_SYSTEM,0L); 1044 static CountDownTimerClass _ftimer(BT_SYSTEM,0L); 1045 static bool _up = false; 1046 bool changed = false; 1047 1048 /* 1049 ** Process the fading white color. It is used for the radar box and other glowing 1050 ** game interface elements. 1051 */ 1052 if (!_ftimer.Time()) { 1053 _ftimer.Set(TIMER_SECOND/8); 1054 1055 /* 1056 ** Pulse the pulsing text color. 1057 */ 1058 #define STEP_RATE 5 1059 if (_up) { 1060 GamePalette[767] += STEP_RATE; 1061 GamePalette[766] += STEP_RATE; 1062 GamePalette[765] += STEP_RATE; 1063 if (GamePalette[767] > MAX_CYCLE_COLOR) { 1064 GamePalette[767] = MAX_CYCLE_COLOR; 1065 GamePalette[766] = MAX_CYCLE_COLOR; 1066 GamePalette[765] = MAX_CYCLE_COLOR; 1067 _up = false; 1068 } 1069 } else { 1070 GamePalette[767] -= STEP_RATE; 1071 GamePalette[766] -= STEP_RATE; 1072 GamePalette[765] -= STEP_RATE; 1073 if ((unsigned)GamePalette[767] < MIN_CYCLE_COLOR) { 1074 GamePalette[767] = MIN_CYCLE_COLOR; 1075 GamePalette[766] = MIN_CYCLE_COLOR; 1076 GamePalette[765] = MIN_CYCLE_COLOR; 1077 _up = true; 1078 } 1079 } 1080 changed = true; 1081 } 1082 1083 /* 1084 ** Process the color cycling effects -- water. 1085 */ 1086 if (!_timer.Time()) { 1087 unsigned char colors[3]; 1088 1089 _timer.Set(TIMER_SECOND/4); 1090 1091 memmove(colors, &GamePalette[(CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1)*3], sizeof(colors)); 1092 memmove(&GamePalette[(CYCLE_COLOR_START+1)*3], &GamePalette[CYCLE_COLOR_START*3], (CYCLE_COLOR_COUNT-1)*3); 1093 memmove(&GamePalette[CYCLE_COLOR_START*3], colors, sizeof(colors)); 1094 changed = true; 1095 } 1096 1097 /* 1098 ** If any of the processing functions changed the palette, then this palette must be 1099 ** passed to the system. 1100 */ 1101 if (changed) { 1102 Wait_Vert_Blank(); 1103 Set_Palette(GamePalette); 1104 return (true); 1105 } 1106 return (false); 1107 } 1108 1109 1110 /*********************************************************************************************** 1111 * Call_Back -- Main game maintenance callback routine. * 1112 * * 1113 * This routine handles all the "real time" processing that needs to * 1114 * occur. This includes palette fading and sound updating. It needs * 1115 * to be called as often as possible. * 1116 * * 1117 * INPUT: none * 1118 * * 1119 * OUTPUT: none * 1120 * * 1121 * WARNINGS: none * 1122 * * 1123 * HISTORY: * 1124 * 10/07/1992 JLB : Created. * 1125 *=============================================================================================*/ 1126 void Call_Back(void) 1127 { 1128 #ifndef DEMO 1129 int i; 1130 int id; 1131 int color; 1132 unsigned short magic_number; 1133 unsigned short crc; 1134 #endif 1135 1136 /* 1137 ** Score maintenance 1138 */ 1139 if (SampleType) { 1140 Theme.AI(); 1141 Speak_AI(); 1142 } 1143 1144 #ifndef DEMO 1145 /* 1146 ** Network maintenance 1147 */ 1148 if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { 1149 1150 Ipx.Service(); 1151 1152 /* 1153 ** Read packets only if the game is "closed", so we don't steal global 1154 ** messages from the connection dialogs. 1155 */ 1156 if (!NetOpen) { 1157 if (Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID)) { 1158 if (GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER) { 1159 1160 /* 1161 ** If this is another player signing off, remove the connection & 1162 ** mark that player's house as non-human, so the computer will take 1163 ** it over. 1164 */ 1165 if (GPacket.Command == NET_SIGN_OFF) { 1166 for (i = 0; i < Ipx.Num_Connections(); i++) { 1167 1168 id = Ipx.Connection_ID(i); 1169 1170 if (!strcmp (GPacket.Name, Ipx.Connection_Name(id) ) && 1171 GAddress == (*Ipx.Connection_Address(id))) { 1172 1173 CCDebugString ("C&C95 = Destroying connection due to sign off\n"); 1174 Destroy_Connection (id,0); 1175 } 1176 } 1177 } else { 1178 1179 /* 1180 ** Process a message from another user. 1181 */ 1182 if (GPacket.Command == NET_MESSAGE) { 1183 bool msg_ok = false; 1184 char txt[80]; 1185 1186 /* 1187 ** If NetProtect is set, make sure this message came from within 1188 ** this game. 1189 */ 1190 if (!NetProtect) { 1191 msg_ok = true; 1192 } else { 1193 if (GPacket.Message.NameCRC == Compute_Name_CRC(MPlayerGameName)) { 1194 msg_ok = true; 1195 } else { 1196 msg_ok = false; 1197 } 1198 } 1199 1200 if (msg_ok) { 1201 sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); 1202 magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); 1203 crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); 1204 color = MPlayerID_To_ColorIndex(GPacket.Message.ID); 1205 Messages.Add_Message(txt, MPlayerTColors[color], 1206 TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); 1207 1208 /* 1209 ** Tell the map to do a partial update (just to force the messages 1210 ** to redraw). 1211 */ 1212 Map.Flag_To_Redraw(false); 1213 1214 /* 1215 ** Save this message in our last-message buffer 1216 */ 1217 if (strlen(GPacket.Message.Buf)) { 1218 strcpy(LastMessage, GPacket.Message.Buf); 1219 } 1220 } 1221 } else { 1222 Process_Global_Packet(&GPacket, &GAddress); 1223 } 1224 } 1225 } 1226 } 1227 } 1228 } 1229 1230 /* 1231 ** Modem and Null Modem maintenance 1232 */ 1233 if (GameToPlay == GAME_NULL_MODEM 1234 || ((GameToPlay == GAME_MODEM) && ModemService)){ 1235 //|| GameToPlay == GAME_INTERNET) { 1236 1237 // PG_TO_FIX 1238 //NullModem.Service(); 1239 } 1240 #endif 1241 } 1242 1243 1244 /*********************************************************************************************** 1245 * Language_Name -- Build filename for current language. * 1246 * * 1247 * This routine attaches a language specific suffix to the base * 1248 * filename provided. Typical use of this is when loading language * 1249 * specific files at game initialization time. * 1250 * * 1251 * INPUT: basename -- Base name to append language specific * 1252 * extension to. * 1253 * * 1254 * OUTPUT: Returns with pointer to completed filename. * 1255 * * 1256 * WARNINGS: The return pointer value is valid only until the next time * 1257 * this routine is called. * 1258 * * 1259 * HISTORY: * 1260 * 10/07/1992 JLB : Created. * 1261 *=============================================================================================*/ 1262 char const *Language_Name(char const *basename) 1263 { 1264 static char _fullname[_MAX_FNAME+_MAX_EXT]; 1265 1266 if (!basename) return(NULL); 1267 1268 sprintf(_fullname, "%s.ENG", basename); 1269 return(_fullname); 1270 } 1271 1272 1273 /*********************************************************************************************** 1274 * Source_From_Name -- Converts ASCII name into SourceType. * 1275 * * 1276 * This routine is used to convert an ASCII name representing a * 1277 * SourceType into the actual SourceType value. Typically, this is * 1278 * used when processing the scenario INI file. * 1279 * * 1280 * INPUT: name -- The ASCII source name to process. * 1281 * * 1282 * OUTPUT: Returns with the SourceType represented by the name * 1283 * specified. * 1284 * * 1285 * WARNINGS: none * 1286 * * 1287 * HISTORY: * 1288 * 04/17/1994 JLB : Created. * 1289 *=============================================================================================*/ 1290 SourceType Source_From_Name(char const *name) 1291 { 1292 if (name) { 1293 for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { 1294 if (stricmp(SourceName[source], name) == 0) { 1295 return(source); 1296 } 1297 } 1298 } 1299 return(SOURCE_NONE); 1300 } 1301 1302 1303 /*********************************************************************************************** 1304 * Name_From_Source -- retrieves the name for the given SourceType * 1305 * * 1306 * INPUT: * 1307 * source SourceType to get the name for * 1308 * * 1309 * OUTPUT: * 1310 * name of SourceType * 1311 * * 1312 * WARNINGS: * 1313 * none. * 1314 * * 1315 * HISTORY: * 1316 * 11/15/1994 BR : Created. * 1317 *=============================================================================================*/ 1318 char const *Name_From_Source(SourceType source) 1319 { 1320 if ((unsigned)source < SOURCE_COUNT) { 1321 return(SourceName[source]); 1322 } 1323 return("None"); 1324 } 1325 1326 1327 /*********************************************************************************************** 1328 * Theater_From_Name -- Converts ASCII name into a theater number. * 1329 * * 1330 * This routine converts an ASCII representation of a theater and converts it into a * 1331 * matching theater number. If no match was found, then THEATER_NONE is returned. * 1332 * * 1333 * INPUT: name -- Pointer to ASCII name to convert. * 1334 * * 1335 * OUTPUT: Returns with the name converted into a theater number. * 1336 * * 1337 * WARNINGS: none * 1338 * * 1339 * HISTORY: * 1340 * 10/01/1994 JLB : Created. * 1341 *=============================================================================================*/ 1342 TheaterType Theater_From_Name(char const *name) 1343 { 1344 TheaterType index; 1345 1346 if (name) { 1347 for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { 1348 if (stricmp(name, Theaters[index].Name) == 0) { 1349 return(index); 1350 } 1351 } 1352 } 1353 return(THEATER_NONE); 1354 } 1355 1356 1357 /*********************************************************************************************** 1358 * KN_To_Facing -- Converts a keyboard input number into a facing value. * 1359 * * 1360 * This routine determine which compass direction is represented by the keyboard value * 1361 * provided. It is used for map scrolling and other directional control operations from * 1362 * the keyboard. * 1363 * * 1364 * INPUT: input -- The KN number to convert. * 1365 * * 1366 * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * 1367 * not be translated, then FACING_NONE is returned. * 1368 * * 1369 * WARNINGS: none * 1370 * * 1371 * HISTORY: * 1372 * 05/28/1994 JLB : Created. * 1373 *=============================================================================================*/ 1374 FacingType KN_To_Facing(int input) 1375 { 1376 input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); 1377 switch (input) { 1378 case KN_LEFT: 1379 return(FACING_W); 1380 1381 case KN_RIGHT: 1382 return(FACING_E); 1383 1384 case KN_UP: 1385 return(FACING_N); 1386 1387 case KN_DOWN: 1388 return(FACING_S); 1389 1390 case KN_UPLEFT: 1391 return(FACING_NW); 1392 1393 case KN_UPRIGHT: 1394 return(FACING_NE); 1395 1396 case KN_DOWNLEFT: 1397 return(FACING_SW); 1398 1399 case KN_DOWNRIGHT: 1400 return(FACING_SE); 1401 } 1402 return(FACING_NONE); 1403 } 1404 1405 1406 /*********************************************************************************************** 1407 * Sync_Delay -- Forces the game into a 15 FPS rate. * 1408 * * 1409 * This routine will wait until the timer for the current frame has expired before * 1410 * returning. It is called at the end of every game loop in order to force the game loop * 1411 * to run at a fixed rate. * 1412 * * 1413 * INPUT: none * 1414 * * 1415 * OUTPUT: none * 1416 * * 1417 * WARNINGS: Will delay for up to 1/15 of a second. * 1418 * * 1419 * HISTORY: * 1420 * 01/04/1995 JLB : Created. * 1421 * 03/06/1995 JLB : Fixed. * 1422 *=============================================================================================*/ 1423 static void Sync_Delay(void) 1424 { 1425 /* 1426 ** Delay one tick and keep a record that one tick was "wasted" here. 1427 ** This accumulates into a running histogram of performance. 1428 */ 1429 SpareTicks += FrameTimer.Time(); 1430 while (FrameTimer.Time()) { 1431 Color_Cycle(); 1432 Call_Back(); 1433 1434 if (SpecialDialog == SDLG_NONE) { 1435 WWMouse->Erase_Mouse(&HidPage, TRUE); 1436 KeyNumType input = KN_NONE; 1437 int x, y; 1438 WWMouse->Erase_Mouse(&HidPage, TRUE); 1439 Map.Input(input, x, y); 1440 if (input) { 1441 Keyboard_Process(input); 1442 } 1443 Map.Render(); 1444 } 1445 } 1446 Color_Cycle(); 1447 Call_Back(); 1448 } 1449 1450 1451 /*********************************************************************************************** 1452 * Main_Loop -- This is the main game loop (as a single loop). * 1453 * * 1454 * This function will perform one game loop. * 1455 * * 1456 * INPUT: none * 1457 * * 1458 * OUTPUT: bool; Should the game end? * 1459 * * 1460 * WARNINGS: none * 1461 * * 1462 * HISTORY: * 1463 * 10/01/1994 JLB : Created. * 1464 *=============================================================================================*/ 1465 extern void Check_For_Focus_Loss(void); 1466 void Reallocate_Big_Shape_Buffer(void); 1467 1468 1469 bool Main_Loop() 1470 { 1471 KeyNumType input; // Player input. 1472 int x; 1473 int y; 1474 int framedelay; 1475 1476 // InMainLoop = true; 1477 1478 /* 1479 ** I think I'm gonna cry if this makes it work 1480 */ 1481 if (Get_Mouse_State())Show_Mouse(); 1482 1483 /* 1484 ** Call the focus loss handler 1485 */ 1486 Check_For_Focus_Loss(); 1487 1488 /* 1489 ** Allocate extra memory for uncompressed shapes as needed 1490 */ 1491 Reallocate_Big_Shape_Buffer(); 1492 1493 /* 1494 ** Sync-bug trapping code 1495 */ 1496 if (Frame >= TrapFrame) { 1497 Trap_Object(); 1498 } 1499 1500 // 1501 // Initialize our AI processing timer 1502 // 1503 ProcessTimer.Set(0, true); 1504 1505 1506 #if 1 1507 if (TrapCheckHeap) { 1508 Debug_Trap_Check_Heap = true; 1509 } 1510 #endif 1511 1512 #ifdef CHEAT_KEYS 1513 Heap_Dump_Check( "After Trap" ); 1514 1515 /* 1516 ** Update the running status debug display. 1517 */ 1518 Self_Regulate(); 1519 #endif 1520 1521 /* 1522 ** If there is no theme playing, but it looks like one is required, then start one 1523 ** playing. This is usually the symptom of there being no transition score. 1524 */ 1525 if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { 1526 Theme.Queue_Song(THEME_PICK_ANOTHER); 1527 } 1528 1529 /* 1530 ** Setup the timer so that the Main_Loop function processes at the correct rate. 1531 */ 1532 if (GameToPlay != GAME_NORMAL && CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { 1533 framedelay = 60 / DesiredFrameRate; 1534 FrameTimer.Set(framedelay); 1535 } else { 1536 FrameTimer.Set(Options.GameSpeed); 1537 } 1538 1539 /* 1540 ** Update the display, unless we're inside a dialog. 1541 */ 1542 if (!PlaybackGame) { 1543 if (SpecialDialog == SDLG_NONE && GameInFocus) { 1544 1545 WWMouse->Erase_Mouse(&HidPage, TRUE); 1546 Map.Input(input, x, y); 1547 if (input) { 1548 Keyboard_Process(input); 1549 } 1550 // HidPage.Lock(); 1551 Map.Render(); 1552 // HidPage.Unlock(); 1553 } 1554 } 1555 1556 /* 1557 ** Save map's position & selected objects, if we're recording the game. 1558 */ 1559 if (RecordGame || PlaybackGame) { 1560 Do_Record_Playback(); 1561 } 1562 1563 /* 1564 ** Sort the map's ground layer by y-coordinate value. This is done 1565 ** outside the IsToRedraw check, for the purposes of game sync'ing 1566 ** between machines; this way, all machines will sort the Map's 1567 ** layer in the same way, and any processing done that's based on 1568 ** the order of this layer will sync on different machines. 1569 */ 1570 Map.Layer[LAYER_GROUND].Sort(); 1571 1572 // Heap_Dump_Check( "Before Logic.AI" ); 1573 1574 /* 1575 ** AI logic operations are performed here. 1576 */ 1577 Logic.AI(); 1578 1579 // Heap_Dump_Check( "After Logic.AI" ); 1580 1581 /* 1582 ** Manage the inter-player message list. If Manage() returns true, it means 1583 ** a message has expired & been removed, and the entire map must be updated. 1584 */ 1585 if (Messages.Manage()) { 1586 HiddenPage.Clear(); 1587 Map.Flag_To_Redraw(true); 1588 } 1589 1590 // 1591 // Measure how long it took to process the AI 1592 // 1593 ProcessTicks += ProcessTimer.Time(); 1594 ProcessFrames++; 1595 1596 // Heap_Dump_Check( "Before Queue_AI" ); 1597 1598 /* 1599 ** Process all commands that are ready to be processed. 1600 */ 1601 Queue_AI(); 1602 1603 // Heap_Dump_Check( "After Queue_AI" ); 1604 1605 /* 1606 ** Keep track of elapsed time in the game. 1607 */ 1608 Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; 1609 1610 Call_Back(); 1611 1612 // Heap_Dump_Check( "After Call_Back" ); 1613 1614 /* 1615 ** Perform any win/lose code as indicated by the global control flags. 1616 */ 1617 if (EndCountDown) EndCountDown--; 1618 1619 /* 1620 ** Check for player wins or loses according to global event flag. 1621 */ 1622 1623 1624 1625 if (PlayerWins) { 1626 1627 if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ 1628 Register_Game_End_Time(); 1629 Send_Statistics_Packet(); 1630 } 1631 1632 WWMouse->Erase_Mouse(&HidPage, TRUE); 1633 PlayerLoses = false; 1634 PlayerWins = false; 1635 PlayerRestarts = false; 1636 Map.Help_Text(TXT_NONE); 1637 Do_Win(); 1638 } 1639 if (PlayerLoses) { 1640 1641 if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ 1642 Register_Game_End_Time(); 1643 Send_Statistics_Packet(); 1644 } 1645 1646 WWMouse->Erase_Mouse(&HidPage, TRUE); 1647 PlayerWins = false; 1648 PlayerLoses = false; 1649 PlayerRestarts = false; 1650 Map.Help_Text(TXT_NONE); 1651 Do_Lose(); 1652 } 1653 if (PlayerRestarts) { 1654 WWMouse->Erase_Mouse(&HidPage, TRUE); 1655 PlayerWins = false; 1656 PlayerLoses = false; 1657 PlayerRestarts = false; 1658 Map.Help_Text(TXT_NONE); 1659 Do_Restart(); 1660 } 1661 1662 /* 1663 ** The frame logic has been completed. Increment the frame 1664 ** counter. 1665 */ 1666 Frame++; 1667 1668 /* 1669 ** Very rarely, the human players will get a message from the computer. 1670 */ 1671 if (GameToPlay != GAME_NORMAL && MPlayerGhosts && IRandom(0,10000) == 1) { 1672 Computer_Message(); 1673 } 1674 1675 /* 1676 ** Is there a memory trasher altering the map?? 1677 */ 1678 if (Debug_Check_Map) { 1679 if (!Map.Validate()) { 1680 #ifdef GERMAN 1681 if (CCMessageBox().Process ("Kartenfehler!","Halt","Weiter")==0) 1682 #else 1683 #ifdef FRENCH 1684 if (CCMessageBox().Process ("Erreur de carte!","Stop","Continuer")==0) 1685 #else 1686 if (CCMessageBox().Process ("Map Error!","Stop","Continue")==0) 1687 #endif 1688 #endif 1689 GameActive = false; 1690 Map.Validate(); // give debugger a chance to catch it 1691 } 1692 } 1693 1694 Sync_Delay(); 1695 // InMainLoop = false; 1696 return(!GameActive); 1697 } 1698 1699 1700 #ifdef SCENARIO_EDITOR 1701 /*************************************************************************** 1702 * Map_Edit_Loop -- a mini-main loop for map edit mode only * 1703 * * 1704 * INPUT: * 1705 * * 1706 * OUTPUT: * 1707 * * 1708 * WARNINGS: * 1709 * * 1710 * HISTORY: * 1711 * 10/19/1994 BR : Created. * 1712 *=========================================================================*/ 1713 bool Map_Edit_Loop(void) 1714 { 1715 /* 1716 ** Redraw the map. 1717 */ 1718 Map.Render(); 1719 1720 /* 1721 ** Get user input (keys, mouse clicks). 1722 */ 1723 KeyNumType input; 1724 1725 int x; 1726 int y; 1727 Map.Input(input, x, y); 1728 1729 /* 1730 ** Process keypress. 1731 */ 1732 if (input) { 1733 Keyboard_Process(input); 1734 } 1735 1736 Call_Back(); // maintains Theme.AI() for music 1737 Color_Cycle(); 1738 1739 return(!GameActive); 1740 } 1741 1742 1743 /*************************************************************************** 1744 * Go_Editor -- Enables/disables the map editor * 1745 * * 1746 * INPUT: * 1747 * flag true = go into editor mode; false = go into game mode * 1748 * * 1749 * OUTPUT: * 1750 * none. * 1751 * * 1752 * WARNINGS: * 1753 * none. * 1754 * * 1755 * HISTORY: * 1756 * 10/19/1994 BR : Created. * 1757 *=========================================================================*/ 1758 void Go_Editor(bool flag) 1759 { 1760 /* 1761 ** Go into Scenario Editor mode 1762 */ 1763 if (flag) { 1764 Debug_Map = true; 1765 Debug_Unshroud = true; 1766 1767 /* 1768 ** Un-select any selected objects 1769 */ 1770 Unselect_All(); 1771 1772 /* 1773 ** Turn off the sidebar if it's on 1774 */ 1775 Map.Activate(0); 1776 1777 /* 1778 ** Reset the map's Button list for the new mode 1779 */ 1780 Map.Init_IO(); 1781 1782 /* 1783 ** Force a complete redraw of the screen 1784 */ 1785 HiddenPage.Clear(); 1786 Map.Flag_To_Redraw(true); 1787 Map.Render(); 1788 1789 } else { 1790 1791 /* 1792 ** Go into normal game mode 1793 */ 1794 Debug_Map = false; 1795 Debug_Unshroud = false; 1796 1797 /* 1798 ** Un-select any selected objects 1799 */ 1800 Unselect_All(); 1801 1802 /* 1803 ** Reset the map's Button list for the new mode 1804 */ 1805 Map.Init_IO(); 1806 1807 /* 1808 ** Force a complete redraw of the screen 1809 */ 1810 HiddenPage.Clear(); 1811 Map.Flag_To_Redraw(true); 1812 Map.Render(); 1813 } 1814 } 1815 1816 #endif 1817 1818 #if (0) 1819 #define VQ_THREAD_BUFFER_SIZE 1024*1024 1820 #define VQ_THREAD_BUFFER_CHUNK VQ_THREAD_BUFFER_SIZE/4 1821 unsigned char *VQThreadBuffer = NULL; 1822 volatile bool ThreadReading = false; 1823 unsigned long VQThreadBlockHead; 1824 unsigned long VQThreadBlockTail; 1825 unsigned long VQBytesLeft; 1826 unsigned long VQBytesRead; 1827 1828 1829 void Init_VQ_Threading(CCFileClass *file) 1830 { 1831 if (!VQThreadBuffer){ 1832 VQThreadBuffer = new unsigned char [VQ_THREAD_BUFFER_SIZE]; 1833 } 1834 Force_VM_Page_In(VQThreadBuffer, VQ_THREAD_BUFFER_SIZE); 1835 VQThreadBlockHead = 0; 1836 VQThreadBlockTail = 0; 1837 VQBytesRead = 0; 1838 VQBytesLeft = file->Size(); 1839 } 1840 1841 1842 void Cleanup_VQ_Threading(void) 1843 { 1844 while (ThreadReading){} 1845 if (VQThreadBuffer){ 1846 delete VQThreadBuffer; 1847 VQThreadBuffer = NULL; 1848 } 1849 } 1850 1851 unsigned long __stdcall Thread_Read(void *file) 1852 { 1853 int bytes_to_read; 1854 int left_to_read; 1855 int read_this_time; 1856 unsigned long head; 1857 int sleep_time; 1858 1859 CCFileClass *ccfile = (CCFileClass*)file; 1860 1861 bytes_to_read = MIN (VQBytesLeft, VQ_THREAD_BUFFER_CHUNK); 1862 1863 if (!bytes_to_read){ 1864 ThreadReading = false; 1865 return(0); 1866 } 1867 1868 left_to_read = bytes_to_read; 1869 1870 while (left_to_read){ 1871 read_this_time = MIN(8*1024, left_to_read); 1872 //if (read_this_time & 3){ 1873 ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time); 1874 //}else{ 1875 // ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time/4); 1876 // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+read_this_time/4, read_this_time/4); 1877 // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*2, read_this_time/4); 1878 // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*3, read_this_time/4); 1879 //} 1880 VQThreadBlockHead += read_this_time; 1881 left_to_read -= read_this_time; 1882 1883 head = VQThreadBlockHead; 1884 if (head<VQThreadBlockTail) head+= VQ_THREAD_BUFFER_SIZE; 1885 sleep_time = head - VQThreadBlockTail; 1886 sleep_time = sleep_time/ (VQ_THREAD_BUFFER_CHUNK/32); 1887 sleep_time += 2; 1888 if (sleep_time<1) sleep_time=1; 1889 Sleep(sleep_time); 1890 } 1891 1892 VQThreadBlockHead &= VQ_THREAD_BUFFER_SIZE-1; 1893 VQBytesLeft -= bytes_to_read; 1894 ThreadReading = false; 1895 return (0); 1896 } 1897 1898 1899 1900 void Read_VQ_Thread_Block(CCFileClass *file) 1901 { 1902 HANDLE thread_handle; 1903 DWORD thread_id; 1904 if (!ThreadReading){ 1905 //_beginthreadex (&Thread_Read, NULL, 16*1024, NULL); 1906 ThreadReading = true; 1907 thread_handle = CreateThread(NULL, 0, &Thread_Read, (void*)file, 0, &thread_id); 1908 //SetThreadPriority (thread_handle, THREAD_PRIORITY_IDLE); 1909 1910 CloseHandle (thread_handle); 1911 } 1912 } 1913 1914 1915 1916 int VQ_Thread_Read (CCFileClass *file, void *buffer, long bytes) 1917 { 1918 long bytes_to_read; 1919 1920 do { 1921 if (VQThreadBlockHead > VQThreadBlockTail){ 1922 bytes_to_read = MIN(bytes, VQThreadBlockHead-VQThreadBlockTail); 1923 1924 }else{ 1925 bytes_to_read = MIN(bytes, VQThreadBlockHead+VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail); 1926 } 1927 1928 }while(ThreadReading && bytes_to_read<bytes); 1929 1930 1931 if (VQThreadBlockTail+bytes_to_read > VQ_THREAD_BUFFER_SIZE){ 1932 1933 int first_chunk = VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail; 1934 int second_chunk = bytes_to_read - first_chunk; 1935 1936 memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, first_chunk); 1937 memcpy ((unsigned char*)buffer + first_chunk, VQThreadBuffer, second_chunk); 1938 }else{ 1939 memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, bytes_to_read); 1940 } 1941 1942 VQThreadBlockTail += bytes_to_read; 1943 VQThreadBlockTail &= VQ_THREAD_BUFFER_SIZE - 1; 1944 1945 unsigned long head = VQThreadBlockHead; 1946 if (head<VQThreadBlockTail) head+= VQ_THREAD_BUFFER_SIZE; 1947 if (head-VQThreadBlockTail < VQ_THREAD_BUFFER_CHUNK && !ThreadReading) Read_VQ_Thread_Block(file); 1948 1949 VQBytesRead += bytes_to_read; 1950 return (bytes_to_read); 1951 1952 } 1953 1954 1955 int VQ_Thread_Seek (long bytes) 1956 { 1957 VQThreadBlockTail += bytes; 1958 VQBytesRead += bytes; 1959 return (VQBytesRead); 1960 } 1961 1962 1963 1964 1965 1966 /*********************************************************************************************** 1967 * MixFileHandler -- Handles VQ file access. * 1968 * * 1969 * This routine is called from the VQ player when it needs to access the source file. By * 1970 * using this routine it is possible to virtualize the file system. * 1971 * * 1972 * INPUT: vqa -- Pointer to the VQA handle for this animation. * 1973 * * 1974 * action-- The requested action to perform. * 1975 * * 1976 * buffer-- Optional buffer pointer as needed by the type of action. * 1977 * * 1978 * nbytes-- The number of bytes (if needed) for this operation. * 1979 * * 1980 * OUTPUT: Returns a value consistent with the action requested. * 1981 * * 1982 * WARNINGS: none * 1983 * * 1984 * HISTORY: * 1985 * 07/04/1995 JLB : Created. * 1986 *=============================================================================================*/ 1987 long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes) 1988 { 1989 CCFileClass *file; 1990 long error; 1991 1992 file = (CCFileClass *)vqa->VQAio; 1993 1994 /* 1995 ** Perform the action specified by the stream command. 1996 */ 1997 switch (action) { 1998 1999 /* 2000 ** VQACMD_READ means read NBytes from the stream and place it in the 2001 ** memory pointed to by Buffer. 2002 ** 2003 ** Any error code returned will be remapped by VQA library into 2004 ** VQAERR_READ. 2005 */ 2006 case VQACMD_READ: 2007 error = VQ_Thread_Read (file, buffer, nbytes); 2008 //error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); 2009 if (error == nbytes) error = 0; 2010 break; 2011 2012 /* 2013 ** VQACMD_WRITE is analogous to VQACMD_READ. 2014 ** 2015 ** Writing is not allowed to the VQA file, VQA library will remap the 2016 ** error into VQAERR_WRITE. 2017 */ 2018 case VQACMD_WRITE: 2019 error = 1; 2020 break; 2021 2022 /* 2023 ** VQACMD_SEEK asks that you perform a seek relative to the current 2024 ** position. NBytes is a signed number, indicating seek direction 2025 ** (positive for forward, negative for backward). Buffer has no meaning 2026 ** here. 2027 ** 2028 ** Any error code returned will be remapped by VQA library into 2029 ** VQAERR_SEEK. 2030 */ 2031 case VQACMD_SEEK: 2032 //error = (file->Seek(nbytes, SEEK_CUR) == -1); 2033 VQ_Thread_Seek(nbytes); 2034 error = 0; 2035 break; 2036 2037 /* 2038 ** VQACMD_OPEN asks that you open your stream for access. 2039 */ 2040 case VQACMD_OPEN: 2041 file = new CCFileClass((char *)buffer); 2042 2043 if (file != NULL && file->Is_Available()) { 2044 error = file->Open((char *)buffer, READ); 2045 2046 if (error != -1) { 2047 vqa->VQAio = (unsigned long)file; 2048 error = 0; 2049 //file->Set_Buffer_Size(8*1024); 2050 } else { 2051 delete file; 2052 file = 0; 2053 error = 1; 2054 } 2055 } else { 2056 error = 1; 2057 } 2058 2059 if (error != -1){ 2060 Init_VQ_Threading(file); 2061 Read_VQ_Thread_Block(file); 2062 CountDownTimerClass timer; 2063 timer.Set(60); 2064 while (ThreadReading || timer.Time()){} 2065 } 2066 break; 2067 2068 case VQACMD_CLOSE: 2069 Cleanup_VQ_Threading(); 2070 file->Close(); 2071 delete file; 2072 file = 0; 2073 vqa->VQAio = 0; 2074 error = 0; 2075 break; 2076 2077 /* 2078 ** VQACMD_INIT means to prepare your stream for reading. This is used for 2079 ** certain streams that can't be read immediately upon opening, and need 2080 ** further preparation. This operation is allowed to fail; the error code 2081 ** will be returned directly to the client. 2082 */ 2083 case VQACMD_INIT: 2084 2085 /* 2086 ** IFFCMD_CLEANUP means to terminate the transaction with the associated 2087 ** stream. This is used for streams that can't simply be closed. This 2088 ** operation is not allowed to fail; any error returned will be ignored. 2089 */ 2090 case VQACMD_CLEANUP: 2091 error = 0; 2092 break; 2093 } 2094 2095 return(error); 2096 } 2097 #endif //(0) 2098 //#if (0) 2099 2100 2101 // PG_TO_FIX 2102 typedef void* VQAHandle; 2103 2104 /*********************************************************************************************** 2105 * MixFileHandler -- Handles VQ file access. * 2106 * * 2107 * This routine is called from the VQ player when it needs to access the source file. By * 2108 * using this routine it is possible to virtualize the file system. * 2109 * * 2110 * INPUT: vqa -- Pointer to the VQA handle for this animation. * 2111 * * 2112 * action-- The requested action to perform. * 2113 * * 2114 * buffer-- Optional buffer pointer as needed by the type of action. * 2115 * * 2116 * nbytes-- The number of bytes (if needed) for this operation. * 2117 * * 2118 * OUTPUT: Returns a value consistent with the action requested. * 2119 * * 2120 * WARNINGS: none * 2121 * * 2122 * HISTORY: * 2123 * 07/04/1995 JLB : Created. * 2124 *=============================================================================================*/ 2125 long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes) 2126 { 2127 return 0;; 2128 #if (0) 2129 CCFileClass *file; 2130 long error; 2131 2132 file = (CCFileClass *)vqa->VQAio; 2133 2134 /* 2135 ** Perform the action specified by the stream command. 2136 */ 2137 switch (action) { 2138 2139 /* 2140 ** VQACMD_READ means read NBytes from the stream and place it in the 2141 ** memory pointed to by Buffer. 2142 ** 2143 ** Any error code returned will be remapped by VQA library into 2144 ** VQAERR_READ. 2145 */ 2146 case VQACMD_READ: 2147 error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); 2148 break; 2149 2150 /* 2151 ** VQACMD_WRITE is analogous to VQACMD_READ. 2152 ** 2153 ** Writing is not allowed to the VQA file, VQA library will remap the 2154 ** error into VQAERR_WRITE. 2155 */ 2156 case VQACMD_WRITE: 2157 error = 1; 2158 break; 2159 2160 /* 2161 ** VQACMD_SEEK asks that you perform a seek relative to the current 2162 ** position. NBytes is a signed number, indicating seek direction 2163 ** (positive for forward, negative for backward). Buffer has no meaning 2164 ** here. 2165 ** 2166 ** Any error code returned will be remapped by VQA library into 2167 ** VQAERR_SEEK. 2168 */ 2169 case VQACMD_SEEK: 2170 error = (file->Seek(nbytes, SEEK_CUR) == -1); 2171 break; 2172 2173 /* 2174 ** VQACMD_OPEN asks that you open your stream for access. 2175 */ 2176 case VQACMD_OPEN: 2177 file = new CCFileClass((char *)buffer); 2178 2179 if (file != NULL && file->Is_Available()) { 2180 error = file->Open((char *)buffer, READ); 2181 2182 if (error != -1) { 2183 vqa->VQAio = (unsigned long)file; 2184 error = 0; 2185 file->Set_Buffer_Size(8*1024); 2186 } else { 2187 delete file; 2188 file = 0; 2189 error = 1; 2190 } 2191 } else { 2192 error = 1; 2193 } 2194 break; 2195 2196 case VQACMD_CLOSE: 2197 file->Close(); 2198 delete file; 2199 file = 0; 2200 vqa->VQAio = 0; 2201 error = 0; 2202 break; 2203 2204 /* 2205 ** VQACMD_INIT means to prepare your stream for reading. This is used for 2206 ** certain streams that can't be read immediately upon opening, and need 2207 ** further preparation. This operation is allowed to fail; the error code 2208 ** will be returned directly to the client. 2209 */ 2210 case VQACMD_INIT: 2211 2212 /* 2213 ** IFFCMD_CLEANUP means to terminate the transaction with the associated 2214 ** stream. This is used for streams that can't simply be closed. This 2215 ** operation is not allowed to fail; any error returned will be ignored. 2216 */ 2217 case VQACMD_CLEANUP: 2218 error = 0; 2219 break; 2220 } 2221 2222 return(error); 2223 #endif 2224 } 2225 2226 //#endif //(0) 2227 2228 2229 void Rebuild_Interpolated_Palette(unsigned char *interpal) 2230 { 2231 for (int y=0 ; y<255 ; y++){ 2232 for (int x=y+1 ; x<256 ; x++){ 2233 *(interpal + (y*256+x)) = *(interpal + (x*256+y)); 2234 } 2235 } 2236 } 2237 2238 2239 unsigned char *InterpolatedPalettes[100]; 2240 BOOL PalettesRead; 2241 unsigned PaletteCounter; 2242 2243 2244 int Load_Interpolated_Palettes(char const *filename, BOOL add) 2245 { 2246 int num_palettes=0; 2247 int i; 2248 int start_palette; 2249 2250 PalettesRead = FALSE; 2251 CCFileClass file(filename); 2252 2253 // RawFileClass *palette_file; 2254 2255 if (!add){ 2256 for (i=0 ; i<ARRAY_SIZE(InterpolatedPalettes) ; i++){ 2257 InterpolatedPalettes[i]=NULL; 2258 } 2259 start_palette=0; 2260 } else { 2261 for (start_palette=0 ; start_palette< ARRAY_SIZE(InterpolatedPalettes) ; start_palette++){ 2262 if (!InterpolatedPalettes[start_palette]) break; 2263 } 2264 } 2265 2266 // palette_file = new RawFileClass (filename); 2267 // if (file.Is_Available()){ 2268 2269 file.Open(READ); 2270 file.Read(&num_palettes , 4); 2271 2272 for (i=0 ; i<num_palettes ; i++){ 2273 InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); 2274 memset (InterpolatedPalettes[i+start_palette],0,65536); 2275 for (int y=0 ; y<256 ;y++){ 2276 file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); 2277 } 2278 2279 Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); 2280 } 2281 2282 PalettesRead = TRUE; 2283 file.Close(); 2284 // } 2285 PaletteCounter = 0; 2286 return (num_palettes); 2287 } 2288 2289 2290 void Free_Interpolated_Palettes(void) 2291 { 2292 for (int i=0 ; i<ARRAY_SIZE(InterpolatedPalettes) ; i++){ 2293 if (InterpolatedPalettes[i]){ 2294 free(InterpolatedPalettes[i]); 2295 InterpolatedPalettes[i]=NULL; 2296 } 2297 } 2298 } 2299 2300 2301 /*********************************************************************************************** 2302 * Play_Movie -- Plays a VQ movie. * 2303 * * 2304 * Use this routine to play a VQ movie. It will disptach the specified movie to the * 2305 * VQ player. The routine will not return until the movie has finished playing. * 2306 * * 2307 * INPUT: file -- The file object that contains the movie. * 2308 * * 2309 * anim -- The anim control and configuration structure that controls how the * 2310 * movie will be played. * 2311 * * 2312 * clrscrn -- Set to 1 to clear the screen when the movie is over * 2313 * * 2314 * OUTPUT: none * 2315 * * 2316 * WARNINGS: none * 2317 * * 2318 * HISTORY: * 2319 * 12/19/1994 JLB : Created. * 2320 *=============================================================================================*/ 2321 extern BOOL InMovie; 2322 extern bool VQPaletteChange; 2323 extern void Suspend_Audio_Thread(void); 2324 extern void Resume_Audio_Thread(void); 2325 2326 //Play 2327 extern void Play_Movie_GlyphX(const char * movie_name, ThemeType theme ); 2328 2329 void Play_Movie(char const * name, ThemeType theme, bool clrscrn) 2330 { 2331 #if (1) 2332 if (strcmp(name, "x") == 0 || strcmp(name, "X") == 0) { 2333 return; 2334 } 2335 2336 Play_Movie_GlyphX(name, theme); 2337 return; 2338 #else 2339 /* 2340 ** Don't play movies in editor mode 2341 */ 2342 if (Debug_Map) { 2343 return; 2344 } 2345 2346 /* 2347 ** Don't play movies in multiplayer mode 2348 */ 2349 if (GameToPlay != GAME_NORMAL) { 2350 return; 2351 } 2352 2353 memset (&PaletteInterpolationTable[0][0],0,65536); 2354 2355 if (name) { 2356 char fullname[_MAX_FNAME+_MAX_EXT]; 2357 char palname [_MAX_FNAME+_MAX_EXT]; 2358 2359 _makepath(fullname, NULL, NULL, name, ".VQA"); 2360 _makepath(palname , NULL, NULL, name, ".VQP"); 2361 #ifdef CHEAT_KEYS 2362 Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); 2363 #endif 2364 2365 /* 2366 ** Reset the anim control structure. 2367 */ 2368 Anim_Init(); 2369 VQPaletteChange = false; 2370 2371 /* 2372 ** Prepare to play a movie. First hide the mouse and stop any score that is playing. 2373 ** While the score (if any) is fading to silence, fade the palette to black as well. 2374 ** When the palette has finished fading, wait until the score has finished fading 2375 ** before launching the movie. 2376 */ 2377 Hide_Mouse(); 2378 //Theme.Stop(); 2379 //Theme.AI(); 2380 Theme.Queue_Song(theme); 2381 if (PreserveVQAScreen == 0) { 2382 Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); 2383 VisiblePage.Clear(); 2384 memset(BlackPalette, 0x01, 768); 2385 Set_Palette(BlackPalette); 2386 memset(BlackPalette, 0x00, 768); 2387 } 2388 PreserveVQAScreen = 0; 2389 Keyboard::Clear(); 2390 2391 VQAHandle *vqa = NULL; 2392 2393 if (!Debug_Quiet && Get_Digi_Handle() != -1) { 2394 AnimControl.OptionFlags |= VQAOPTF_AUDIO; 2395 } else { 2396 AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; 2397 } 2398 2399 2400 if ((vqa = VQA_Alloc()) != NULL) { 2401 VQA_Init(vqa, MixFileHandler); 2402 if (VQA_Open(vqa, fullname, &AnimControl) == 0) { 2403 Brokeout = false; 2404 //Suspend_Audio_Thread(); 2405 2406 #if (FRENCH | GERMAN | JAPANESE) 2407 /* 2408 ** Kludge to use the old palette interpolation table for CC2TEASE 2409 ** unless the covert CD is inserted. 2410 */ 2411 if (!stricmp (palname, "CC2TEASE.VQP")){ 2412 int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); 2413 /* 2414 ** If cd_index == 2 then its a covert CD 2415 */ 2416 if (cd_index != 2){ 2417 strcpy (palname, "OLDCC2T.VQP"); 2418 } 2419 } 2420 #endif //(FRENCH | GERMAN) 2421 2422 #if (GERMAN) 2423 /* 2424 ** Kludge to use a different palette interpolation table for RETRO.VQA 2425 ** if the covert CD is inserted. 2426 */ 2427 if (!stricmp (palname, "RETRO.VQP")){ 2428 int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); 2429 /* 2430 ** If cd_index == 2 then its a covert CD 2431 */ 2432 if (cd_index == 2){ 2433 strcpy (palname, "RETROGER.VQP"); 2434 } 2435 } 2436 2437 #endif //GERMAN 2438 2439 Load_Interpolated_Palettes(palname); 2440 //Set_Palette(BlackPalette); 2441 SysMemPage.Clear(); 2442 InMovie = TRUE; 2443 VQA_Play(vqa, VQAMODE_RUN); 2444 VQA_Close(vqa); 2445 //Resume_Audio_Thread(); 2446 InMovie = FALSE; 2447 Free_Interpolated_Palettes(); 2448 Set_Primary_Buffer_Format(); 2449 2450 /* 2451 ** Any movie that ends prematurely must have the screen 2452 ** cleared to avoid any unexpected palette glitches. 2453 */ 2454 if (Brokeout) { 2455 clrscrn = true; 2456 VisiblePage.Clear(); 2457 Brokeout = false; 2458 } 2459 } 2460 2461 VQA_Free(vqa); 2462 } 2463 2464 2465 /* 2466 ** Presume that the screen is left in a garbage state as well as the palette 2467 ** being in an unknown condition. Recover from this by clearing the screen and 2468 ** forcing the palette to black. 2469 */ 2470 if (clrscrn) { 2471 VisiblePage.Clear(); 2472 memset(BlackPalette, 0x01, 768); 2473 Set_Palette(BlackPalette); 2474 memset(BlackPalette, 0x00, 768); 2475 Set_Palette(BlackPalette); 2476 } 2477 Show_Mouse(); 2478 } 2479 #endif 2480 } 2481 2482 2483 /*********************************************************************************************** 2484 * Unselect_All -- Causes all selected objects to become unselected. * 2485 * * 2486 * This routine will unselect all objects that are currently selected. * 2487 * * 2488 * INPUT: none * 2489 * * 2490 * OUTPUT: none * 2491 * * 2492 * WARNINGS: none * 2493 * * 2494 * HISTORY: * 2495 * 01/19/1995 JLB : Created. * 2496 *=============================================================================================*/ 2497 void Unselect_All(void) 2498 { 2499 //while (CurrentObject.Count()) { 2500 // CurrentObject[0]->Unselect(); 2501 //} 2502 2503 //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 2504 while (CurrentObject.Count()) { 2505 2506 int count_before = CurrentObject.Count(); 2507 CurrentObject[0]->Unselect(); 2508 2509 if (count_before <= CurrentObject.Count()) { 2510 GlyphX_Debug_Print("Unselect_All failed to remove an object"); 2511 CurrentObject.Delete(CurrentObject[0]); 2512 } 2513 } 2514 //End of change - JAS 6/28/2019 2515 } 2516 2517 2518 void Unselect_All_Except(ObjectClass* object) 2519 { 2520 int index = 0; 2521 while (index < CurrentObject.Count()) { 2522 2523 if (CurrentObject[index] == object) { 2524 index++; 2525 continue; 2526 } 2527 2528 int count_before = CurrentObject.Count(); 2529 CurrentObject[index]->Unselect(); 2530 2531 if (count_before <= CurrentObject.Count()) { 2532 GlyphX_Debug_Print("Unselect_All failed to remove an object"); 2533 CurrentObject.Delete(CurrentObject[index]); 2534 } 2535 } 2536 } 2537 2538 2539 /*********************************************************************************************** 2540 * Fading_Table_Name -- Builds a theater specific fading table name. * 2541 * * 2542 * This routine builds a standard fading table name. This name is dependant on the theater * 2543 * being played, since each theater has its own palette. * 2544 * * 2545 * INPUT: base -- The base name of this fading table. The base name can be no longer than * 2546 * seven characters. * 2547 * * 2548 * theater -- The theater that this fading table is specific to. * 2549 * * 2550 * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * 2551 * valid until this function is called again. * 2552 * * 2553 * WARNINGS: none * 2554 * * 2555 * HISTORY: * 2556 * 01/19/1995 JLB : Created. * 2557 *=============================================================================================*/ 2558 char const * Fading_Table_Name(char const * base, TheaterType theater) 2559 { 2560 static char _buffer[_MAX_FNAME+_MAX_EXT]; 2561 char root[_MAX_FNAME]; 2562 2563 sprintf(root, "%1.1s%s", Theaters[theater].Root, base); 2564 _makepath(_buffer, NULL, NULL, root, ".MRF"); 2565 return(_buffer); 2566 } 2567 2568 2569 /*********************************************************************************************** 2570 * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * 2571 * * 2572 * INPUT: void const * shapefile - pointer to a key framed shapefile * 2573 * int shapenum - shape to extract from shapefile * 2574 * * 2575 * OUTPUT: void const * - 3/3 icon set of shape from file * 2576 * * 2577 * HISTORY: * 2578 * 04/12/1995 PWG : Created. * 2579 * 05/10/1995 JLB : Handles a null shapefile pointer. * 2580 *=============================================================================================*/ 2581 void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) 2582 { 2583 static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; 2584 static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; 2585 int lp,framelp; 2586 char pixel; 2587 2588 char *retval = NULL; 2589 char *buffer = NULL; 2590 void *ptr; 2591 2592 2593 /* 2594 ** If there is no shape file, then there can be no radar icon imagery. 2595 */ 2596 if (!shapefile) return(NULL); 2597 2598 /* 2599 ** Get the pixel width and height of the frame we built. This will 2600 ** be used to extract icons and build pixels. 2601 */ 2602 int pixel_width = Get_Build_Frame_Width( shapefile ); 2603 int pixel_height = Get_Build_Frame_Height( shapefile ); 2604 2605 /* 2606 ** Find the width and height in icons, adjust these by half an 2607 ** icon because the artists may be sloppy and miss the edge of an 2608 ** icon one way or the other. 2609 */ 2610 int icon_width = (pixel_width + 12) / 24; 2611 int icon_height = (pixel_height + 12) / 24; 2612 2613 /* 2614 ** If we have been told to build as many frames as possible, then 2615 ** find out how many frames there are to build. 2616 */ 2617 if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); 2618 2619 /* 2620 ** Allocate a position to store our icons. If the alloc fails then 2621 ** we dont add these icons to the set. 2622 **/ 2623 buffer = new char[(icon_width * icon_height * 9 * frames)+2]; 2624 if (!buffer) return(NULL); 2625 2626 /* 2627 ** Save off the return value so that we can return it to the calling 2628 ** function. 2629 */ 2630 retval = (char *)buffer; 2631 *buffer++ = (char)icon_width; 2632 *buffer++ = (char)icon_height; 2633 int val = 24/zoomfactor; 2634 2635 for (framelp = 0; framelp < frames; framelp ++) { 2636 /* 2637 ** Build the current frame. If the frame can not be built then we 2638 ** just need to skip past this set of icons and try to build the 2639 ** next frame. 2640 */ 2641 if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { 2642 ptr = Get_Shape_Header_Data(ptr); 2643 /* 2644 ** Loop through the icon width and the icon height building icons 2645 ** into the buffer pointer. When the getx or gety falls outside of 2646 ** the width and height of the shape, just insert transparent pixels. 2647 */ 2648 for (int icony = 0; icony < icon_height; icony ++) { 2649 for (int iconx = 0; iconx < icon_width; iconx ++) { 2650 for (int y = 0; y < zoomfactor; y++) { 2651 for (int x = 0; x < zoomfactor; x++) { 2652 int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); 2653 int gety = (icony * 24) + (y * val) + (zoomfactor / 2); 2654 if ((getx < pixel_width) && (gety < pixel_height)) { 2655 for (lp = 0; lp < 9; lp ++) { 2656 pixel = *((char *)(Add_Long_To_Pointer(ptr, ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]))); 2657 if (pixel == LTGREEN) pixel = 0; 2658 if (pixel) { 2659 break; 2660 } 2661 } 2662 *buffer++ = pixel; 2663 } else { 2664 *buffer++ = 0; 2665 } 2666 } 2667 } 2668 } 2669 } 2670 } else { 2671 buffer += icon_width * icon_height * 9; 2672 } 2673 } 2674 return(retval); 2675 } 2676 2677 2678 2679 2680 void CC_Texture_Fill (void const *shapefile, int shapenum, int xpos, int ypos, int width, int height) 2681 { 2682 unsigned char *shape_pointer; 2683 //unsigned char *shape_save; 2684 unsigned long shape_size; 2685 //int x,y; 2686 2687 if (shapefile && shapenum != -1) { 2688 2689 /* 2690 ** Build frame returns a pointer now instead of the shapes length 2691 */ 2692 shape_size=Build_Frame(shapefile , shapenum , _ShapeBuffer); 2693 if (Get_Last_Frame_Length() > _ShapeBufferSize) { 2694 Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); 2695 Get_Key(); 2696 } 2697 2698 if (shape_size) { 2699 shape_pointer = (unsigned char *)Get_Shape_Header_Data ((void*)shape_size); 2700 int source_width = Get_Build_Frame_Width (shapefile); 2701 int source_height = Get_Build_Frame_Height (shapefile); 2702 2703 2704 LogicPage->Texture_Fill_Rect (xpos, ypos, width, height, shape_pointer, source_width, source_height); 2705 #if (0) 2706 if (LogicPage->Lock()){ 2707 2708 for (y = ypos ; y < ypos + MIN(source_height, height) ; y++ ){ 2709 2710 shape_save = shape_pointer; 2711 2712 for (x = xpos ; x < xpos + MIN(source_width, width) ; x++ ){ 2713 LogicPage->Put_Pixel (x, y, *shape_pointer++); 2714 } 2715 2716 shape_pointer = shape_save + source_width; 2717 } 2718 2719 LogicPage->Unlock(); 2720 } 2721 #endif 2722 } 2723 } 2724 } 2725 2726 2727 2728 2729 extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name, char override_owner, int scale); 2730 extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); 2731 extern void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame); 2732 2733 void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, int scale, int width, int height) 2734 { 2735 if (window == WINDOW_VIRTUAL) { 2736 if (width == 0) width = Get_Build_Frame_Width(shapefile); 2737 if (height == 0) height = Get_Build_Frame_Height(shapefile); 2738 DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, NULL, -1, scale); 2739 return; 2740 } 2741 2742 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); 2743 } 2744 2745 2746 void CC_Draw_Shape(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, char override_owner) 2747 { 2748 if (window == WINDOW_VIRTUAL) { 2749 int width = Get_Build_Frame_Width(shapefile); 2750 int height = Get_Build_Frame_Height(shapefile); 2751 DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, shape_file_name, override_owner, 0x100); 2752 return; 2753 } 2754 2755 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); 2756 } 2757 2758 2759 void CC_Draw_Pip(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata) 2760 { 2761 if (window == WINDOW_VIRTUAL) { 2762 DLL_Draw_Pip_Intercept(object, shapenum); 2763 return; 2764 } 2765 2766 CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata); 2767 } 2768 2769 2770 void CC_Draw_Line(int x, int y, int x1, int y1, unsigned char color, int frame, WindowNumberType window) 2771 { 2772 if (window == WINDOW_VIRTUAL) { 2773 DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); 2774 return; 2775 } 2776 2777 LogicPage->Draw_Line(x, y, x1, y1, color); 2778 } 2779 2780 2781 /*********************************************************************************************** 2782 * CC_Draw_Shape -- Custom draw shape handler. * 2783 * * 2784 * All draw shape calls will route through this function. It handles all draws for * 2785 * C&C. Such draws always occur to the logical page and assume certain things about * 2786 * the parameters passed. * 2787 * * 2788 * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * 2789 * embedded shapes. * 2790 * * 2791 * shapenum -- The shape number within the shapefile that will be drawn. * 2792 * * 2793 * x,y -- The pixel coordinates to draw the shape. * 2794 * * 2795 * window -- The clipping window to use. * 2796 * * 2797 * flags -- The custom draw shape flags. This controls how the parameters * 2798 * are used (if any). * 2799 * * 2800 * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * 2801 * data table. * 2802 * * 2803 * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * 2804 * table. * 2805 * * 2806 * OUTPUT: none * 2807 * * 2808 * WARNINGS: none * 2809 * * 2810 * HISTORY: * 2811 * 02/21/1995 JLB : Created. * 2812 *=============================================================================================*/ 2813 //#pragma off(unreferenced) 2814 void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata) 2815 { 2816 #if(TRUE) 2817 int predoffset; 2818 char *shape_pointer; 2819 unsigned long shape_size; 2820 2821 if (shapefile && shapenum != -1) { 2822 2823 /* 2824 ** Build frame returns a pointer now instead of the shapes length 2825 */ 2826 shape_size=Build_Frame(shapefile , shapenum , _ShapeBuffer); 2827 if (Get_Last_Frame_Length() > _ShapeBufferSize) { 2828 Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); 2829 Get_Key(); 2830 } 2831 2832 if (shape_size) { 2833 GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), 2834 (WindowList[window][WINDOWX] << 3) + LogicPage->Get_XPos(), 2835 WindowList[window][WINDOWY] + LogicPage->Get_YPos(), 2836 WindowList[window][WINDOWWIDTH] << 3, 2837 WindowList[window][WINDOWHEIGHT]); 2838 2839 shape_pointer = (char *)shape_size; 2840 2841 /* 2842 ** Special shadow drawing code (used for aircraft and bullets). 2843 */ 2844 if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { 2845 flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); 2846 flags = flags | SHAPE_GHOST; 2847 ghostdata = Map.SpecialGhost; 2848 } 2849 2850 predoffset = Frame; 2851 2852 if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { 2853 predoffset = -predoffset; 2854 } 2855 2856 if (draw_window.Lock()){ 2857 if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { 2858 Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), 2859 shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); 2860 } else { 2861 if (flags & SHAPE_FADING) { 2862 Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), 2863 shape_pointer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); 2864 } else { 2865 if (flags & SHAPE_PREDATOR) { 2866 Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), 2867 shape_pointer, draw_window, flags | SHAPE_TRANS, predoffset); 2868 } else { 2869 Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), 2870 shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); 2871 } 2872 } 2873 } 2874 } 2875 draw_window.Unlock(); 2876 // } else { 2877 // Mono_Printf( "Overrun ShapeBuffer!!!!!!!!!\n" ); 2878 } 2879 } 2880 #endif 2881 } 2882 2883 2884 2885 /*********************************************************************************************** 2886 * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * 2887 * * 2888 * This routine will convert the supplied RTTI type number and the ID value into a valid * 2889 * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * 2890 * * 2891 * INPUT: type -- RTTI type of the techno class object. * 2892 * * 2893 * id -- Integer representation of the techno sub type number. * 2894 * * 2895 * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * 2896 * conversion could not occur. * 2897 * * 2898 * WARNINGS: none * 2899 * * 2900 * HISTORY: * 2901 * 05/08/1995 JLB : Created. * 2902 *=============================================================================================*/ 2903 TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) 2904 { 2905 switch (type) { 2906 case RTTI_UNITTYPE: 2907 case RTTI_UNIT: 2908 return(&UnitTypeClass::As_Reference((UnitType)id)); 2909 2910 case RTTI_BUILDINGTYPE: 2911 case RTTI_BUILDING: 2912 return(&BuildingTypeClass::As_Reference((StructType)id)); 2913 2914 case RTTI_INFANTRYTYPE: 2915 case RTTI_INFANTRY: 2916 return(&InfantryTypeClass::As_Reference((InfantryType)id)); 2917 2918 case RTTI_AIRCRAFTTYPE: 2919 case RTTI_AIRCRAFT: 2920 return(&AircraftTypeClass::As_Reference((AircraftType)id)); 2921 } 2922 return(NULL); 2923 } 2924 2925 2926 /*************************************************************************** 2927 * Trap_Object -- gets a ptr to object of given type & coord * 2928 * * 2929 * INPUT: * 2930 * * 2931 * OUTPUT: * 2932 * * 2933 * WARNINGS: * 2934 * * 2935 * HISTORY: * 2936 * 06/02/1995 BRR : Created. * 2937 *=========================================================================*/ 2938 void Trap_Object(void) 2939 { 2940 int i; 2941 2942 TrapObject.Ptr.All = NULL; 2943 2944 switch (TrapObjType) { 2945 case RTTI_AIRCRAFT: 2946 for (i = 0; i < Aircraft.Count(); i++) { 2947 if (Aircraft.Ptr(i)->Coord == TrapCoord || Aircraft.Ptr(i)==TrapThis) { 2948 TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); 2949 break; 2950 } 2951 } 2952 break; 2953 2954 case RTTI_ANIM: 2955 for (i = 0; i < Anims.Count(); i++) { 2956 if (Anims.Ptr(i)->Coord == TrapCoord || Anims.Ptr(i)==TrapThis) { 2957 TrapObject.Ptr.Anim = Anims.Ptr(i); 2958 break; 2959 } 2960 } 2961 break; 2962 2963 case RTTI_BUILDING: 2964 for (i = 0; i < Buildings.Count(); i++) { 2965 if (Buildings.Ptr(i)->Coord == TrapCoord || Buildings.Ptr(i)==TrapThis) { 2966 TrapObject.Ptr.Building = Buildings.Ptr(i); 2967 break; 2968 } 2969 } 2970 break; 2971 2972 case RTTI_BULLET: 2973 for (i = 0; i < Bullets.Count(); i++) { 2974 if (Bullets.Ptr(i)->Coord == TrapCoord || Bullets.Ptr(i)==TrapThis) { 2975 TrapObject.Ptr.Bullet = Bullets.Ptr(i); 2976 break; 2977 } 2978 } 2979 break; 2980 2981 case RTTI_INFANTRY: 2982 for (i = 0; i < Infantry.Count(); i++) { 2983 if (Infantry.Ptr(i)->Coord == TrapCoord || Infantry.Ptr(i)==TrapThis) { 2984 TrapObject.Ptr.Infantry = Infantry.Ptr(i); 2985 break; 2986 } 2987 } 2988 break; 2989 2990 case RTTI_UNIT: 2991 for (i = 0; i < Units.Count(); i++) { 2992 if (Units.Ptr(i)->Coord == TrapCoord || Units.Ptr(i)==TrapThis) { 2993 TrapObject.Ptr.Unit = Units.Ptr(i); 2994 break; 2995 } 2996 } 2997 break; 2998 2999 /* 3000 ** Last-ditch find-the-object-right-now-darnit loop 3001 */ 3002 case RTTI_NONE: 3003 for (i = 0; i < Aircraft.Count(); i++) { 3004 if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || Aircraft.Raw_Ptr(i)==TrapThis) { 3005 TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); 3006 TrapObjType = RTTI_AIRCRAFT; 3007 return; 3008 } 3009 } 3010 for (i = 0; i < Anims.Count(); i++) { 3011 if (Anims.Raw_Ptr(i)->Coord == TrapCoord || Anims.Raw_Ptr(i)==TrapThis) { 3012 TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); 3013 TrapObjType = RTTI_ANIM; 3014 return; 3015 } 3016 } 3017 for (i = 0; i < Buildings.Count(); i++) { 3018 if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || Buildings.Raw_Ptr(i)==TrapThis) { 3019 TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); 3020 TrapObjType = RTTI_BUILDING; 3021 return; 3022 } 3023 } 3024 for (i = 0; i < Bullets.Count(); i++) { 3025 if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || Bullets.Raw_Ptr(i)==TrapThis) { 3026 TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); 3027 TrapObjType = RTTI_BULLET; 3028 return; 3029 } 3030 } 3031 for (i = 0; i < Infantry.Count(); i++) { 3032 if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || Infantry.Raw_Ptr(i)==TrapThis) { 3033 TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); 3034 TrapObjType = RTTI_INFANTRY; 3035 return; 3036 } 3037 } 3038 for (i = 0; i < Units.Count(); i++) { 3039 if (Units.Raw_Ptr(i)->Coord == TrapCoord || Units.Raw_Ptr(i)==TrapThis) { 3040 TrapObject.Ptr.Unit = Units.Raw_Ptr(i); 3041 TrapObjType = RTTI_UNIT; 3042 return; 3043 } 3044 } 3045 3046 default: 3047 break; 3048 } 3049 } 3050 3051 3052 /*********************************************************************************************** 3053 * VQ_Call_Back -- Maintenance callback used for VQ movies. * 3054 * * 3055 * This routine is called every frame of the VQ movie as it is being played. If this * 3056 * routine returns non-zero, then the movie will stop. * 3057 * * 3058 * INPUT: buffer -- Pointer to the image buffer for the current frame. * 3059 * * 3060 * frame -- The frame number about to be displayed. * 3061 * * 3062 * OUTPUT: Should the movie be stopped? * 3063 * * 3064 * WARNINGS: none * 3065 * * 3066 * HISTORY: * 3067 * 06/24/1995 JLB : Created. * 3068 *=============================================================================================*/ 3069 void VQA_PauseAudio(void) {}; 3070 void Check_VQ_Palette_Set(void); 3071 3072 long VQ_Call_Back(unsigned char *, long ) 3073 { 3074 // PG_TO_FIX - 1/2/2019 3:59PM 3075 #if (0) 3076 int key = 0; 3077 if (Keyboard::Check()){ 3078 key = Keyboard::Get(); 3079 Keyboard::Clear(); 3080 } 3081 3082 Check_VQ_Palette_Set(); 3083 3084 Interpolate_2X_Scale(&SysMemPage,&SeenBuff,NULL); 3085 //Call_Back(); 3086 if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { 3087 Keyboard::Clear(); 3088 Brokeout = true; 3089 return(true); 3090 } 3091 3092 if (!GameInFocus){ 3093 VQA_PauseAudio(); 3094 while (!GameInFocus){ 3095 Keyboard::Check(); 3096 Check_For_Focus_Loss(); 3097 } 3098 } 3099 #endif 3100 return(false); 3101 } 3102 3103 3104 /*********************************************************************************************** 3105 * Handle_Team -- Processes team selection command. * 3106 * * 3107 * This routine will handle creation and selection of pseudo teams that the player can * 3108 * create or control. A team in this sense is an arbitrary grouping of units such that * 3109 * rapid selection control is allowed. * 3110 * * 3111 * INPUT: team -- The logical team number to process. * 3112 * * 3113 * action-- The action to perform on this team: * 3114 * 0 - Toggle the select state for all members of this team. * 3115 * 1 - Select the members of this team. * 3116 * 2 - Make all selected objects members of this team. * 3117 * * 3118 * OUTPUT: none * 3119 * * 3120 * WARNINGS: none * 3121 * * 3122 * HISTORY: * 3123 * 06/27/1995 JLB : Created. * 3124 *=============================================================================================*/ 3125 void Handle_Team(int team, int action) 3126 { 3127 int index; 3128 3129 AllowVoice = true; 3130 switch (action) { 3131 3132 /* 3133 ** Toggle the team selection. If the team is selected, then merely unselect it. If the 3134 ** team is not selected, then unselect all others before selecting this team. 3135 */ 3136 case 3: 3137 case 0: 3138 3139 /* 3140 ** If a non team member is currently selected, then deselect all objects 3141 ** before selecting this team. 3142 */ 3143 for (index = 0; index < CurrentObject.Count(); index++) { 3144 ObjectClass * obj = CurrentObject[index]; 3145 if (obj->What_Am_I() == RTTI_UNIT || obj->What_Am_I() == RTTI_INFANTRY || obj->What_Am_I() == RTTI_AIRCRAFT) { 3146 if (((FootClass *)obj)->Group != team) { 3147 Unselect_All(); 3148 break; 3149 } 3150 } 3151 } 3152 for (index = 0; index < Units.Count(); index++) { 3153 UnitClass * obj = Units.Ptr(index); 3154 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3155 if (!obj->Is_Selected_By_Player()) { 3156 obj->Select(); 3157 AllowVoice = false; 3158 } 3159 } 3160 } 3161 for (index = 0; index < Infantry.Count(); index++) { 3162 InfantryClass * obj = Infantry.Ptr(index); 3163 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3164 if (!obj->Is_Selected_By_Player()) { 3165 obj->Select(); 3166 AllowVoice = false; 3167 } 3168 } 3169 } 3170 for (index = 0; index < Aircraft.Count(); index++) { 3171 AircraftClass * obj = Aircraft.Ptr(index); 3172 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3173 if (!obj->Is_Selected_By_Player()) { 3174 obj->Select(); 3175 AllowVoice = false; 3176 } 3177 } 3178 } 3179 3180 /* 3181 ** Center the map around the team if the ALT key was pressed too. 3182 */ 3183 if (action == 3) { 3184 Map.Center_Map(); 3185 Map.Flag_To_Redraw(true); 3186 } 3187 break; 3188 3189 /* 3190 ** Additive selection of team. 3191 */ 3192 case 1: 3193 for (index = 0; index < Units.Count(); index++) { 3194 UnitClass * obj = Units.Ptr(index); 3195 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3196 if (!obj->Is_Selected_By_Player()) { 3197 obj->Select(); 3198 AllowVoice = false; 3199 } 3200 } 3201 } 3202 for (index = 0; index < Infantry.Count(); index++) { 3203 InfantryClass * obj = Infantry.Ptr(index); 3204 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3205 if (!obj->Is_Selected_By_Player()) { 3206 obj->Select(); 3207 AllowVoice = false; 3208 } 3209 } 3210 } 3211 for (index = 0; index < Aircraft.Count(); index++) { 3212 AircraftClass * obj = Aircraft.Ptr(index); 3213 if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { 3214 if (!obj->Is_Selected_By_Player()) { 3215 obj->Select(); 3216 AllowVoice = false; 3217 } 3218 } 3219 } 3220 break; 3221 3222 /* 3223 ** Create the team. 3224 */ 3225 case 2: 3226 for (index = 0; index < Units.Count(); index++) { 3227 UnitClass * obj = Units.Ptr(index); 3228 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { 3229 if (obj->Group == team) obj->Group = -1; 3230 if (obj->Is_Selected_By_Player()) obj->Group = team; 3231 } 3232 } 3233 for (index = 0; index < Infantry.Count(); index++) { 3234 InfantryClass * obj = Infantry.Ptr(index); 3235 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { 3236 if (obj->Group == team) obj->Group = -1; 3237 if (obj->Is_Selected_By_Player()) obj->Group = team; 3238 } 3239 } 3240 for (index = 0; index < Aircraft.Count(); index++) { 3241 AircraftClass * obj = Aircraft.Ptr(index); 3242 if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { 3243 if (obj->Group == team) obj->Group = -1; 3244 if (obj->Is_Selected_By_Player()) obj->Group = team; 3245 } 3246 } 3247 break; 3248 } 3249 AllowVoice = true; 3250 } 3251 3252 3253 /*********************************************************************************************** 3254 * Handle_View -- Either records or restores the tactical view. * 3255 * * 3256 * This routine is used to record or restore the current map tactical view. * 3257 * * 3258 * INPUT: view -- The view number to work with. * 3259 * * 3260 * action-- The action to perform with this view number. * 3261 * 0 = Restore the view to this previously remembered location. * 3262 * 1 = Record the current view location. * 3263 * * 3264 * OUTPUT: none * 3265 * * 3266 * WARNINGS: none * 3267 * * 3268 * HISTORY: * 3269 * 07/04/1995 JLB : Created. * 3270 *=============================================================================================*/ 3271 void Handle_View(int view, int action) 3272 { 3273 if ((unsigned)view < sizeof(Views)/sizeof(Views[0])) { 3274 if (action == 0) { 3275 Map.Set_Tactical_Position(Cell_Coord(Views[view])&0xFF00FF00L); 3276 Map.Flag_To_Redraw(true); 3277 } else { 3278 Views[view] = Coord_Cell(Map.TacticalCoord); 3279 } 3280 } 3281 } 3282 3283 #ifdef CHEAT_KEYS 3284 void Heap_Dump_Check( char *string ) 3285 { 3286 #if 0 3287 struct _heapinfo h_info; 3288 int heap_status; 3289 #endif 3290 3291 3292 if ( !Debug_Trap_Check_Heap ) { // check the heap? 3293 return; 3294 } 3295 3296 // Debug_Heap_Dump = true; 3297 3298 Smart_Printf( "%s\n", string ); 3299 3300 Dump_Heap_Pointers(); 3301 3302 #if 0 3303 heap_status = _heapset( 0xee ); 3304 3305 #if (1) 3306 if ( heap_status == _HEAPOK || 3307 heap_status == _HEAPEMPTY ) { 3308 Debug_Heap_Dump = false; 3309 return; 3310 } 3311 3312 h_info._pentry = NULL; 3313 3314 for(;;) { 3315 heap_status = _heapwalk( &h_info ); 3316 3317 if ( heap_status != _HEAPOK ) 3318 break; 3319 } 3320 3321 if (heap_status != _HEAPEND && 3322 heap_status != _HEAPEMPTY) { 3323 #endif 3324 h_info._pentry = NULL; 3325 3326 for(;;) { 3327 heap_status = _heapwalk( &h_info ); 3328 3329 if ( heap_status != _HEAPOK ) 3330 break; 3331 3332 Smart_Printf( " %s block at %Fp of size %4.4X\n", 3333 (h_info._useflag == _USEDENTRY ? "USED" : "FREE"), 3334 h_info._pentry, h_info._size ); 3335 } 3336 3337 Smart_Printf( " %d block at %Fp of size %4.4X\n", 3338 h_info._useflag, h_info._pentry, h_info._size ); 3339 3340 switch ( heap_status ) { 3341 // case _HEAPEND: 3342 // Smart_Printf( "OK - end of heap\n" ); 3343 // break; 3344 3345 // case _HEAPEMPTY: 3346 // Smart_Printf( "OK - heap is empty\n" ); 3347 // break; 3348 3349 case _HEAPBADBEGIN: 3350 Smart_Printf( "ERROR - heap is damaged\n" ); 3351 break; 3352 3353 case _HEAPBADPTR: 3354 Smart_Printf( "ERROR - bad pointer to heap\n" ); 3355 break; 3356 3357 case _HEAPBADNODE: 3358 Smart_Printf( "ERROR - bad node in heap\n" ); 3359 break; 3360 } 3361 #if (1) 3362 } 3363 #endif 3364 #endif 3365 3366 // Debug_Heap_Dump = false; 3367 } 3368 3369 3370 void Dump_Heap_Pointers( void ) 3371 { 3372 // ST - 1/2/2019 4:04PM 3373 #if (0) 3374 char *ptr, *lptr, *nptr, *cptr, *dptr, *wlptr, *nlptr, *aptr, *clptr; 3375 int numallocs, numfrees, sizefree; 3376 static char _freeorused[2][5] = { "FREE", "USED" }; 3377 3378 3379 ptr = (char *)__nheapbeg; 3380 3381 while ( ptr ) { 3382 3383 if ( Debug_Heap_Dump ) { 3384 Smart_Printf( "%p pre header\n", (ptr - 8) ); 3385 Hex_Dump_Data( (ptr - 8), 0x08); 3386 3387 Smart_Printf( "%p header\n", ptr ); 3388 Hex_Dump_Data( ptr, 0x30); 3389 } 3390 3391 dptr = (char *)*(int *)(ptr + 0x0c); 3392 3393 sizefree = *(int *)(ptr + 0x14); 3394 numallocs = *(int *)(ptr + 0x18); 3395 numfrees = *(int *)(ptr + 0x1c); 3396 3397 cptr = (char *)*(int *)(ptr + 0x24); 3398 lptr = (char *)*(int *)(ptr + 0x28); 3399 3400 if ( ((int)cptr & 0xff000000) || 3401 ((int)dptr & 0xff000000) || 3402 ((int)lptr & 0xff000000) ) { 3403 Error_In_Heap_Pointers( "local free heap ptrs too large" ); 3404 } 3405 3406 if ( Debug_Heap_Dump ) { 3407 if ( lptr != dptr || 3408 lptr != cptr || 3409 cptr != dptr ) { 3410 Smart_Printf( "The pointers are different!!\n" ); 3411 } 3412 } 3413 3414 nptr = (char *)*(int *)(ptr + 8); // next block 3415 3416 if ( ((int)nptr & 0xFF000000) ) { 3417 Error_In_Heap_Pointers( "next block ptr too large" ); 3418 } 3419 3420 if ( lptr != (ptr + 0x20) ) { 3421 3422 if ( !(*((int *)(ptr + 0x20))) ) { // allocated 3423 aptr = (ptr + 0x2c); 3424 3425 while ( aptr < lptr ) { 3426 3427 if ( (*(int *)(aptr)) == -1) { 3428 // Smart_Printf( "end alloc chain %p.\n", aptr ); 3429 // Hex_Dump_Data( aptr, 0x10); 3430 break; 3431 } 3432 3433 if ( Debug_Heap_Dump ) { 3434 Smart_Printf( "%p chain %s, size %X.\n", aptr, 3435 _freeorused[ ((*aptr) & 1) ], 3436 ((*(int *)(aptr)) & 0xfffffffe) ); 3437 Hex_Dump_Data( aptr, 0x10); 3438 } 3439 3440 if ( ((*(int *)(aptr)) & 0xff000000) ) { 3441 Error_In_Heap_Pointers( "alloc block size way too large" ); 3442 } 3443 3444 aptr += ((*(int *)(aptr)) & 0xfffffffe); 3445 3446 if ( ((int)aptr & 0xff000000) ) { 3447 Error_In_Heap_Pointers( "next alloc block ptr way too large" ); 3448 } 3449 3450 numallocs--; 3451 3452 if ( aptr > lptr ) { 3453 Error_In_Heap_Pointers( "next alloc block ptr too large" ); 3454 } 3455 } 3456 } else { 3457 if ( sizefree != -1 ) { 3458 sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); 3459 } 3460 numfrees--; 3461 } 3462 3463 wlptr = lptr; 3464 3465 while ( wlptr != (ptr + 0x20) ) { 3466 3467 if ( Debug_Heap_Dump ) { 3468 Smart_Printf( "%p link %s, size %X.\n", wlptr, 3469 _freeorused[ ((*wlptr) & 1) ], 3470 ((*(int *)(wlptr)) & 0xfffffffe) ); 3471 Hex_Dump_Data( wlptr, 0x10); 3472 } 3473 3474 nlptr = (char *)*(int *)(wlptr + 8); 3475 3476 if ( !(*((int *)(wlptr))) ) { // allocated 3477 aptr = (wlptr + 0x0c); 3478 } else { 3479 if ( sizefree != -1 ) { 3480 sizefree -= ((*(int *)(wlptr)) & 0xfffffffe); 3481 } 3482 numfrees--; 3483 3484 aptr = (wlptr + ((*(int *)(wlptr)) & 0xfffffffe)); 3485 } 3486 3487 if (nlptr == (ptr + 0x20) ) { 3488 clptr = nptr; 3489 } else { 3490 clptr = nlptr; 3491 } 3492 3493 while ( aptr < clptr ) { 3494 3495 if ( (*(int *)(aptr)) == -1) { 3496 // Smart_Printf( "end alloc chain %p.\n", aptr ); 3497 // Hex_Dump_Data( aptr, 0x10); 3498 break; 3499 } 3500 3501 if ( Debug_Heap_Dump ) { 3502 Smart_Printf( "%p chain %s, size %X.\n", aptr, 3503 _freeorused[ ((*aptr) & 1) ], 3504 ((*(int *)(aptr)) & 0xfffffffe) ); 3505 Hex_Dump_Data( aptr, 0x10); 3506 } 3507 3508 if ( ((*(int *)(aptr)) & 0xff000000) ) { 3509 Error_In_Heap_Pointers( "alloc block size way too large" ); 3510 } 3511 3512 aptr += ((*(int *)(aptr)) & 0xfffffffe); 3513 3514 if ( ((int)aptr & 0xff000000) ) { 3515 Error_In_Heap_Pointers( "next alloc block ptr way too large" ); 3516 } 3517 3518 numallocs--; 3519 3520 if ( aptr > clptr ) { 3521 Error_In_Heap_Pointers( "next alloc block ptr too large" ); 3522 } 3523 } 3524 3525 wlptr = nlptr; 3526 } 3527 } else { 3528 // Smart_Printf( "only link %s, size %X.\n", 3529 // _freeorused[ ((*lptr) & 1) ], 3530 // ((*(int *)(lptr)) & 0xfffffffe) ); 3531 3532 if ( !(*((int *)(lptr))) ) { // allocated 3533 aptr = (ptr + 0x2c); 3534 3535 while ( aptr < nptr ) { 3536 3537 if ( (*(int *)(aptr)) == -1) { 3538 // Smart_Printf( "end alloc chain %p.\n", aptr ); 3539 // Hex_Dump_Data( aptr, 0x10); 3540 break; 3541 } 3542 3543 if ( Debug_Heap_Dump ) { 3544 Smart_Printf( "%p chain %s, size %X.\n", aptr, 3545 _freeorused[ ((*aptr) & 1) ], 3546 ((*(int *)(aptr)) & 0xfffffffe) ); 3547 Hex_Dump_Data( aptr, 0x10); 3548 } 3549 3550 if ( ((*(int *)(aptr)) & 0xff000000) ) { 3551 Error_In_Heap_Pointers( "alloc block size way too large" ); 3552 } 3553 3554 aptr += ((*(int *)(aptr)) & 0xfffffffe); 3555 3556 if ( ((int)aptr & 0xff000000) ) { 3557 Error_In_Heap_Pointers( "next alloc block ptr way too large" ); 3558 } 3559 3560 numallocs--; 3561 3562 if ( aptr > nptr ) { 3563 Error_In_Heap_Pointers( "next alloc block ptr too large" ); 3564 } 3565 } 3566 } else { 3567 if ( sizefree != -1 ) { 3568 sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); 3569 } 3570 numfrees--; 3571 } 3572 } 3573 3574 if ( sizefree != 0 && sizefree != -1 ) { 3575 Smart_Printf( "sizefree left over %X.\n", sizefree ); 3576 } 3577 3578 if ( numallocs != 0 ) { 3579 Smart_Printf( "numallocs unaccounted for %d.\n", numallocs ); 3580 } 3581 3582 if ( numfrees != 0 ) { 3583 Smart_Printf( "numfrees unaccounted for %d.\n", numfrees ); 3584 } 3585 3586 ptr = nptr; 3587 } 3588 #endif 3589 } 3590 3591 3592 void Error_In_Heap_Pointers( char *string ) 3593 { 3594 Smart_Printf( "Error in Heap for %s\n", string ); 3595 } 3596 #endif 3597 3598 3599 #ifndef ROR_NOT_READY 3600 #define ROR_NOT_READY 21 3601 #endif 3602 3603 /*********************************************************************************************** 3604 * Get_CD_Index -- returns the volume type of the CD in the given drive * 3605 * * 3606 * * 3607 * * 3608 * INPUT: drive number * 3609 * timeout * 3610 * * 3611 * OUTPUT: 0 = gdi * 3612 * 1 = nod * 3613 * 2 = covert * 3614 * -1 = non C&C * 3615 * * 3616 * WARNINGS: None * 3617 * * 3618 * HISTORY: * 3619 * 5/21/96 5:27PM ST : Created * 3620 *=============================================================================================*/ 3621 int Get_CD_Index (int cd_drive, int timeout) 3622 { 3623 char volume_name[128]; 3624 unsigned filename_length; 3625 unsigned misc_dword; 3626 //unsigned error_code; 3627 int count = 0; 3628 3629 CountDownTimerClass timer; 3630 3631 static char * _volid[] = { 3632 "GDI95", 3633 "NOD95", 3634 "COVERT" 3635 }; 3636 static int num_volumes = 3; 3637 3638 char buffer[128]; 3639 3640 timer.Set(timeout); 3641 3642 3643 /* 3644 ** Get the volume label. If we get a 'not ready' error then retry for the timeout 3645 ** period. 3646 */ 3647 do{ 3648 sprintf(buffer, "%c:\\", 'A' + cd_drive); 3649 3650 if (GetVolumeInformation ((char const *)buffer, 3651 &volume_name[0] , 3652 (unsigned long)128 , 3653 (unsigned long *)NULL , 3654 (unsigned long *)&filename_length, 3655 (unsigned long *)&misc_dword , 3656 (char *)NULL , 3657 (unsigned long)0)) { 3658 3659 /* 3660 ** Try opening 'movies.mix' to verify that the CD is really there and is what 3661 ** it says it is. 3662 */ 3663 sprintf(buffer, "%c:\\movies.mix", 'A' + cd_drive); 3664 3665 HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, 3666 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 3667 3668 if (handle != INVALID_HANDLE_VALUE) { 3669 CloseHandle(handle); 3670 3671 /* 3672 ** Match the volume label to the list of known C&C volume labels. 3673 */ 3674 for (int i=0 ; i<num_volumes ; i++){ 3675 if (!stricmp(_volid[i], volume_name)) return (i); 3676 } 3677 }else{ 3678 if (count++){ 3679 return (-1); 3680 } 3681 } 3682 }else{ 3683 /* 3684 ** Failed to get the volume label on a known CD drive. 3685 ** If this is a CD changer it may require time to swap the disks so dont return 3686 ** immediately if the error is ROR_NOT_READY 3687 */ 3688 if (GetLastError() != ROR_NOT_READY || !timer.Time()) return (-1); 3689 } 3690 }while(true); 3691 } 3692 3693 3694 3695 3696 /*********************************************************************************************** 3697 * Force_CD_Available -- Ensures that specified CD is available. * 3698 * * 3699 * Call this routine when you need to ensure that the specified CD is actually in the * 3700 * CD-ROM drive. * 3701 * * 3702 * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * 3703 * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * 3704 * * 3705 * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * 3706 * the player pressed <CANCEL>. * 3707 * * 3708 * WARNINGS: none * 3709 * * 3710 * HISTORY: * 3711 * 07/11/1995 JLB : Created. * 3712 * 05/22/1996 ST : Handles multiple CD drives / CD changers * 3713 *=============================================================================================*/ 3714 #pragma warning (disable : 4101) 3715 bool Force_CD_Available(int cd) 3716 { 3717 3718 #if (1) //ST - 1/2/2019 5:44PM 3719 3720 static int _last = -1; 3721 3722 if (_last != cd) { 3723 3724 _last = cd; 3725 3726 Theme.Stop(); 3727 3728 if (MoviesMix) { 3729 delete MoviesMix; 3730 MoviesMix = 0; 3731 } 3732 if (GeneralMix) { 3733 delete GeneralMix; 3734 GeneralMix = 0; 3735 } 3736 if (ScoreMix) { 3737 delete ScoreMix; 3738 ScoreMix = 0; 3739 } 3740 3741 MoviesMix = new MixFileClass("MOVIES.MIX"); 3742 GeneralMix = new MixFileClass("GENERAL.MIX"); 3743 ScoreMix = new MixFileClass("SCORES.MIX"); 3744 #if (0) 3745 switch ( cd ) { 3746 case -1: 3747 default: 3748 MoviesMix = new MixFileClass("MOVIES.MIX"); 3749 GeneralMix = new MixFileClass("GENERAL.MIX"); 3750 ScoreMix = new MixFileClass("SCORES.MIX"); 3751 break; 3752 3753 case 0: 3754 MoviesMix = new MixFileClass("GDIMOVIES.MIX"); 3755 GeneralMix = new MixFileClass("GENERAL.MIX"); 3756 ScoreMix = new MixFileClass("GDISCORES.MIX"); 3757 break; 3758 3759 case 1: 3760 MoviesMix = new MixFileClass("NODMOVIES.MIX"); 3761 GeneralMix = new MixFileClass("GENERAL.MIX"); 3762 ScoreMix = new MixFileClass("NODSCORES.MIX"); 3763 break; 3764 3765 case 2: 3766 MoviesMix = new MixFileClass("COVERTMOVIES.MIX"); 3767 GeneralMix = new MixFileClass("GENERAL.MIX"); 3768 ScoreMix = new MixFileClass("COVERTSCORES.MIX"); 3769 break; 3770 } 3771 #endif 3772 ThemeClass::Scan(); 3773 3774 } 3775 3776 return true; 3777 3778 3779 3780 #else 3781 3782 #ifndef DEMO 3783 static int _last = -1; 3784 int open_failed; 3785 int file; 3786 #endif 3787 static char _palette[768]; 3788 static char _hold[256]; 3789 static void *font; 3790 static char * _volid[] = { 3791 "GDI", 3792 "NOD", 3793 "COVERT" 3794 }; 3795 3796 int drive; 3797 3798 char volume_name[100]; 3799 unsigned filename_length; 3800 unsigned misc_dword; 3801 int new_cd_drive = 0; 3802 int cd_index; 3803 char buffer[128]; 3804 int cd_drive; 3805 int current_drive; 3806 int drive_search_timeout; 3807 bool old_in_main_loop; 3808 3809 ThemeType theme_playing = THEME_NONE; 3810 3811 /* 3812 ** If the required CD is set to -2 then it means that the file is present 3813 ** on the local hard drive and we shouldn't have to worry about it. 3814 */ 3815 if (cd == -2) return(true); 3816 3817 3818 /* 3819 ** Find out if the CD in the current drive is the one we are looking for 3820 */ 3821 current_drive = CCFileClass::Get_CD_Drive(); 3822 cd_index = Get_CD_Index(current_drive, 1*60); 3823 if (cd_index >= 0){ 3824 if (cd == cd_index || cd == -1){ 3825 /* 3826 ** The required CD is still in the CD drive we used last time 3827 */ 3828 new_cd_drive = current_drive; 3829 } 3830 } 3831 3832 3833 /* 3834 ** Flag that we will have to restart the theme 3835 */ 3836 theme_playing = Theme.What_Is_Playing(); 3837 Theme.Stop(); 3838 3839 3840 if (!new_cd_drive){ 3841 /* 3842 ** Check the last CD drive we used if its different from the current one 3843 */ 3844 int last_drive = CCFileClass::Get_Last_CD_Drive(); 3845 /* 3846 ** Make sure the last drive is valid and it isnt the current drive 3847 */ 3848 if (last_drive && last_drive != CCFileClass::Get_CD_Drive()){ 3849 /* 3850 ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for 3851 ** Give it a nice big timeout so the CD changer has time to swap the discs 3852 */ 3853 cd_index = Get_CD_Index(last_drive, 10*60); 3854 if (cd_index >= 0){ 3855 if (cd == cd_index || cd == -1){ 3856 /* 3857 ** The required CD is in the CD drive we used last time 3858 */ 3859 new_cd_drive = last_drive; 3860 } 3861 } 3862 } 3863 } 3864 3865 /* 3866 ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives 3867 ** then if we still cant find it prompt the user to insert it. 3868 */ 3869 if (!new_cd_drive){ 3870 3871 /* 3872 ** Small timeout for the first pass through the drives 3873 */ 3874 drive_search_timeout = 2*60; 3875 3876 for (;;) { 3877 /* 3878 ** Search all present CD drives for the required disc. 3879 */ 3880 for (int i=0 ; i<CDList.Get_Number_Of_Drives() ; i++){ 3881 cd_drive = CDList.Get_Next_CD_Drive(); 3882 cd_index = Get_CD_Index(cd_drive, drive_search_timeout); 3883 if (cd_index>=0){ 3884 /* 3885 ** We found a C&C cd - lets see if it was the one we were looking for 3886 */ 3887 if (cd == cd_index || cd == -1){ 3888 /* 3889 ** Woohoo! The disk was in a different cd drive. Refresh the search path list 3890 * and return. 3891 */ 3892 new_cd_drive = cd_drive; 3893 break; 3894 } 3895 } 3896 } 3897 3898 /* 3899 ** A new disc has become available so break 3900 */ 3901 if (new_cd_drive) break; 3902 3903 /* 3904 ** Increase the timeout for subsequent drive searches. 3905 */ 3906 drive_search_timeout = 5*60; 3907 3908 /* 3909 ** Prompt to insert the CD into the drive. 3910 */ 3911 if (cd == -1) { 3912 sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _volid[cd]); 3913 } else { 3914 if (cd == 2) { 3915 sprintf(buffer, Text_String(TXT_CD_DIALOG_3)); 3916 } else { 3917 sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _volid[cd]); 3918 } 3919 } 3920 GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); 3921 theme_playing = Theme.What_Is_Playing(); 3922 Theme.Stop(); 3923 int hidden = Get_Mouse_State(); 3924 font = (void *)FontPtr; 3925 Mem_Copy(CurrentPalette, _palette, 768); 3926 Mem_Copy(Get_Font_Palette_Ptr(), _hold, 256); 3927 3928 3929 /* 3930 ** Only set the palette if necessary. 3931 */ 3932 // if (CurrentPalette[3] == 0) { 3933 Set_Palette(GamePalette); 3934 // } 3935 3936 /* 3937 ** Pretend we are in the game, even if we arent 3938 */ 3939 old_in_main_loop = InMainLoop; 3940 InMainLoop = true; 3941 3942 Keyboard::Clear(); 3943 3944 while (Get_Mouse_State()) Show_Mouse(); 3945 3946 if (CCMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) { 3947 Set_Logic_Page(oldpage); 3948 Hide_Mouse(); 3949 InMainLoop = old_in_main_loop; 3950 return(false); 3951 } 3952 while (hidden--) Hide_Mouse(); 3953 Set_Palette(_palette); 3954 Set_Font(font); 3955 Mem_Copy(_hold, Get_Font_Palette_Ptr(), 256); 3956 Set_Logic_Page(oldpage); 3957 InMainLoop = old_in_main_loop; 3958 } 3959 } 3960 3961 3962 #ifndef DEMO 3963 3964 CCFileClass::Set_CD_Drive(new_cd_drive); 3965 CCFileClass::Refresh_Search_Drives(); 3966 3967 /* 3968 ** If it broke out of the query for CD-ROM loop, then this means that the 3969 ** CD-ROM has been inserted. 3970 */ 3971 if (cd > -1 && _last != cd) { 3972 _last = cd; 3973 3974 Theme.Stop(); 3975 3976 if (MoviesMix) delete MoviesMix; 3977 if (GeneralMix) delete GeneralMix; 3978 if (ScoreMix) delete ScoreMix; 3979 3980 MoviesMix = new MixFileClass("MOVIES.MIX"); 3981 GeneralMix = new MixFileClass("GENERAL.MIX"); 3982 ScoreMix = new MixFileClass("SCORES.MIX"); 3983 ThemeClass::Scan(); 3984 } 3985 #endif 3986 3987 if (theme_playing != THEME_NONE) { 3988 Theme.Queue_Song(theme_playing); 3989 } 3990 3991 return(true); 3992 #endif 3993 } 3994 3995 3996 /*************************************************************************** 3997 * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * 3998 * * 3999 * INPUT: none * 4000 * * 4001 * OUTPUT: returns amount of free disk space * 4002 * * 4003 * HISTORY: * 4004 * 08/11/1995 PWG : Created. * 4005 *=========================================================================*/ 4006 unsigned long Disk_Space_Available(void) 4007 { 4008 4009 return 0x7fffffff; 4010 4011 #if (0) 4012 struct diskfree_t diskdata; 4013 unsigned drive; 4014 4015 _dos_getdrive(&drive); 4016 _dos_getdiskfree(drive, &diskdata); 4017 4018 return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); 4019 #endif 4020 } 4021 4022 4023 /*********************************************************************************************** 4024 * Validate_Error -- prints an error message when an object fails validation * 4025 * * 4026 * INPUT: * 4027 * name name of object type that failed * 4028 * * 4029 * OUTPUT: * 4030 * none. * 4031 * * 4032 * WARNINGS: * 4033 * none. * 4034 * * 4035 * HISTORY: * 4036 * 08/15/1995 BRR : Created. * 4037 *=============================================================================================*/ 4038 void Validate_Error(char *name) 4039 { 4040 GlyphX_Debug_Print("Validate_Error"); 4041 GlyphX_Debug_Print(name); 4042 #ifdef CHEAT_KEYS 4043 Prog_End(NULL, true); 4044 printf("%s object error!\n",name); 4045 if (!RunningAsDLL) { 4046 exit(0); 4047 } 4048 #else 4049 name = name; 4050 #endif 4051 } 4052 4053 4054 /*********************************************************************************************** 4055 * Do_Record_Playback -- handles saving/loading map pos & current object * 4056 * * 4057 * INPUT: * 4058 * none. * 4059 * * 4060 * OUTPUT: * 4061 * none. * 4062 * * 4063 * WARNINGS: * 4064 * none. * 4065 * * 4066 * HISTORY: * 4067 * 08/15/1995 BRR : Created. * 4068 *=============================================================================================*/ 4069 static void Do_Record_Playback(void) 4070 { 4071 int count; 4072 TARGET tgt; 4073 int i; 4074 COORDINATE coord; 4075 ObjectClass *obj; 4076 unsigned long sum; 4077 unsigned long sum2; 4078 unsigned long ltgt; 4079 4080 /*------------------------------------------------------------------------ 4081 Record a game 4082 ------------------------------------------------------------------------*/ 4083 if (RecordGame) { 4084 4085 /*..................................................................... 4086 For 'SuperRecord', we'll open & close the file with every entry. 4087 .....................................................................*/ 4088 if (SuperRecord) { 4089 RecordFile.Open(READ|WRITE); 4090 RecordFile.Seek(0,SEEK_END); 4091 } 4092 4093 /*..................................................................... 4094 Save the map's location 4095 .....................................................................*/ 4096 RecordFile.Write(&Map.DesiredTacticalCoord, sizeof (Map.DesiredTacticalCoord)); 4097 4098 /*..................................................................... 4099 Save the current object list count 4100 .....................................................................*/ 4101 count = CurrentObject.Count(); 4102 RecordFile.Write(&count, sizeof(count)); 4103 4104 /*..................................................................... 4105 Save a CRC of the selected-object list. 4106 .....................................................................*/ 4107 sum = 0; 4108 for (i = 0; i < count; i++) { 4109 ltgt = (unsigned long)(CurrentObject[i]->As_Target()); 4110 sum += ltgt; 4111 } 4112 RecordFile.Write (&sum, sizeof(sum)); 4113 4114 /*..................................................................... 4115 Save all selected objects. 4116 .....................................................................*/ 4117 for (i = 0; i < count; i++) { 4118 tgt = CurrentObject[i]->As_Target(); 4119 RecordFile.Write (&tgt, sizeof(tgt)); 4120 } 4121 4122 /*..................................................................... 4123 If 'SuperRecord', close the file now. 4124 .....................................................................*/ 4125 if (SuperRecord) { 4126 RecordFile.Close(); 4127 } 4128 } 4129 4130 /*------------------------------------------------------------------------ 4131 Play back a game ("attract" mode) 4132 ------------------------------------------------------------------------*/ 4133 if (PlaybackGame) { 4134 4135 /*..................................................................... 4136 Read & set the map's location. 4137 .....................................................................*/ 4138 if (RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { 4139 if (coord != Map.DesiredTacticalCoord) { 4140 Map.Set_Tactical_Position(coord); 4141 } 4142 } 4143 4144 if (RecordFile.Read(&count, sizeof(count))==sizeof(count)) { 4145 /*.................................................................. 4146 Compute a CRC of the current object-selection list. 4147 ..................................................................*/ 4148 sum = 0; 4149 for (i = 0; i < CurrentObject.Count(); i++) { 4150 ltgt = (unsigned long)(CurrentObject[i]->As_Target()); 4151 sum += ltgt; 4152 } 4153 4154 /*.................................................................. 4155 Load the CRC of the objects on disk; if it doesn't match, select 4156 all objects as they're loaded. 4157 ..................................................................*/ 4158 RecordFile.Read (&sum2, sizeof(sum2)); 4159 if (sum2 != sum) 4160 Unselect_All(); 4161 4162 AllowVoice = true; 4163 4164 for (i = 0; i < count; i++) { 4165 if (RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { 4166 obj = As_Object(tgt); 4167 if (obj && (sum2 != sum)) { 4168 obj->Select(); 4169 AllowVoice = false; 4170 } 4171 } 4172 } 4173 4174 AllowVoice = true; 4175 4176 } 4177 4178 /*..................................................................... 4179 The map isn't drawn in playback mode, so draw it here. 4180 .....................................................................*/ 4181 Map.Render(); 4182 } 4183 } 4184 /*************************************************************************** 4185 * HIRES_RETRIEVE -- retrieves a resolution dependant file * 4186 * * 4187 * INPUT: char * file name of the file to retrieve * 4188 * * 4189 * OUTPUT: none * 4190 * * 4191 * HISTORY: * 4192 * 01/25/1996 : Created. * 4193 *=========================================================================*/ 4194 void const * Hires_Retrieve(char *name) 4195 { 4196 char filename[30]; 4197 4198 if (SeenBuff.Get_Width() != 320) { 4199 sprintf(filename,"H%s", name); 4200 } else { 4201 strcpy(filename, name); 4202 } 4203 return(MixFileClass::Retrieve(filename)); 4204 } 4205 int Get_Resolution_Factor(void) 4206 { 4207 return ((SeenBuff.Get_Width() == 320) ? 0 : 1); 4208 } 4209 4210 4211 4212 4213 4214 /************************************************************************************************** 4215 * Blit_Hid_Page_To_Seen_Buff -- Intercept for when the game refreshes the visible page 4216 * 4217 * In: Command line 4218 * 4219 * Out: 4220 * 4221 * 4222 * 4223 * History: 1/3/2019 11:33AM - ST 4224 **************************************************************************************************/ 4225 void Blit_Hid_Page_To_Seen_Buff(void) 4226 { 4227 HidPage.Blit(SeenBuff); 4228 } 4229 4230 4231 /*********************************************************************************************** 4232 * Shake_The_Screen -- Dispatcher that shakes the screen. * 4233 * * 4234 * This routine will shake the game screen the number of shakes requested. * 4235 * * 4236 * INPUT: shakes -- The number of shakes to shake the screen. * 4237 * house -- House to perform the shake for (or HOUSE_NONE if all players). * 4238 * * 4239 * OUTPUT: none * 4240 * * 4241 * WARNINGS: none * 4242 * * 4243 * HISTORY: * 4244 * 09/04/1996 BWG : Created. * 4245 *=============================================================================================*/ 4246 void Shake_The_Screen(int shakes, HousesType house) 4247 { 4248 for (char h = HOUSE_FIRST; h < HOUSE_COUNT; ++h) { 4249 if ((house != HOUSE_NONE) && (h != house)) { 4250 continue; 4251 } 4252 HouseClass* hptr = HouseClass::As_Pointer((HousesType)h); 4253 if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { 4254 hptr->ScreenShakeTime = hptr->ScreenShakeTime + shakes + shakes; 4255 } 4256 } 4257 }