// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: F:\projects\c&c\vcs\code\gscreen.cpv 2.17 16 Oct 1995 16:51:34 JOE_BOSTIC $ */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * File Name : GSCREEN.CPP * * * * Programmer : Joe L. Bostic * * * * Start Date : 12/15/94 * * * * Last Update : January 19, 1995 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * * GScreenClass::One_Time -- Handles one time class setups. * * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * * GScreenClass::Init_Clear -- Sets the map to a known state. * * GScreenClass::Init_Theater -- Performs theater-specific initializations. * * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * * GScreenClass::Render -- General drawing dispatcher an display update function. * * GScreenClass::Input -- Fetches input and processes gadgets. * * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" #include GadgetClass * GScreenClass::Buttons = 0; GraphicBufferClass * GScreenClass::ShadowPage = 0; /*********************************************************************************************** * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * * * * This constructor merely sets the display system, so that it will redraw the first time * * the render function is called. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/15/1994 JLB : Created. * *=============================================================================================*/ GScreenClass::GScreenClass(void) { IsToUpdate = true; IsToRedraw = true; } /*********************************************************************************************** * GScreenClass::One_Time -- Handles one time class setups. * * * * This routine (and all those that overload it) must perform truly one-time initialization. * * Such init's would normally be done in the constructor, but other aspects of the game may * * not have been initialized at the time the constructors are called (such as the file system, * * the display, or other WWLIB subsystems), so many initializations should be deferred to the * * One_Time init's. * * * * Any variables set in this routine should be declared as static, so they won't be modified * * by the load/save process. Non-static variables will be over-written by a loaded game. * * * * This function allocates the shadow buffer that is used for quick screen updates. If * * there were any data files to load, they would be loaded at this time as well. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: Call this routine only ONCE at the beginning of the game. * * * * HISTORY: * * 12/15/1994 JLB : Created. * *=============================================================================================*/ void GScreenClass::One_Time(void) { /* ** Allocate the screen shadow page. This page is used to reduce access to the ** actual screen memory. It contains a duplicate of what the SEENPAGE is. */ Buttons = 0; ShadowPage = new GraphicBufferClass(320,200); if (ShadowPage) { ShadowPage->Clear(); HiddenPage.Clear(); } } /*********************************************************************************************** * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * * * * This routine shouldn't be overloaded. It's the main map initialization routine, and will * * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * * routine results in calling every initialization routine in the entire map hierarchy. * * * * INPUT: * * theater theater to initialize to * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 12/28/1994 BR : Created. * *=============================================================================================*/ void GScreenClass::Init(TheaterType theater) { Init_Clear(); Init_IO(); Init_Theater(theater); } /*********************************************************************************************** * GScreenClass::Init_Clear -- Sets the map to a known state. * * * * This routine (and those that overload it) clears any buffers and variables to a known * * state. It assumes that all buffers are allocated & valid. The map should be displayable * * after calling this function, and should draw basically an empty display. * * * * INPUT: * * none. * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 12/28/1994 BR : Created. * *=============================================================================================*/ void GScreenClass::Init_Clear(void) { /* ** Clear the ShadowPage & HidPage to force a complete shadow blit. */ if (ShadowPage) { ShadowPage->Clear(); HiddenPage.Clear(); } IsToRedraw = true; } /*********************************************************************************************** * GScreenClass::Init_Theater -- Performs theater-specific initializations. * * * * This routine (and those that overload it) performs any theater-specific initializations * * needed. This will include setting the palette, setting up remap tables, etc. This routine * * only needs to be called when the theater has changed. * * * * INPUT: * * none. * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 12/28/1994 BR : Created. * *=============================================================================================*/ void GScreenClass::Init_Theater(TheaterType ) { } /*********************************************************************************************** * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * * * * INPUT: * * none. * * * * OUTPUT: * * none. * * * * WARNINGS: * * none. * * * * HISTORY: * * 12/28/1994 BR : Created. * *=============================================================================================*/ void GScreenClass::Init_IO(void) { /* ** Reset the button list. This means that any other elements of the map that need ** buttons must attach them after this routine is called! */ Buttons = 0; } /*********************************************************************************************** * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * * * * This function is used to flag the display system whether any rendering is needed. The * * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * * has changed and the individual Draw_It functions must be called. When a sub system * * determines that it needs to render something local to itself, it would call this routine * * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * * this routine will be called with a true parameter. * * * * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * * * * OUTPUT: none * * * * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * * Render() function is called, the appropriate drawing steps will be performed. * * * * HISTORY: * * 12/15/1994 JLB : Created. * *=============================================================================================*/ void GScreenClass::Flag_To_Redraw(bool complete) { IsToUpdate = true; if (complete) { IsToRedraw = true; } } /*********************************************************************************************** * GScreenClass::Input -- Fetches input and processes gadgets. * * * * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * * system. * * * * INPUT: key -- Reference to the key code (for future examination). * * * * x,y -- Reference to mouse coordinates (for future examination). * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void GScreenClass::Input(KeyNumType & key, int & x, int & y) { key = Keyboard::Check(); x = Keyboard::Mouse_X(); y = Keyboard::Mouse_Y(); if (Buttons) { /* ** If any buttons need redrawing, they will do so in the Input routine, and ** they should draw themselves to the HidPage. So, flag ourselves for a Blit ** to show the newly drawn buttons. */ if (Buttons->Is_List_To_Redraw()) { Flag_To_Redraw(false); } GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); key = Buttons->Input(); Set_Logic_Page(oldpage); } else { if (key) { key = Keyboard::Get(); } } AI(key, x, y); } /*********************************************************************************************** * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * * * * This will add a gadget to the game input system. The gadget will be processed in * * subsiquent calls to the GScreenClass::Input() function. * * * * INPUT: gadget -- Reference to the gadget that will be added to the input system. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void GScreenClass::Add_A_Button(GadgetClass & gadget) { /*------------------------------------------------------------------------ If this gadget is already in the list, remove it before adding it in: - If 1st gadget in list, use Remove_A_Button to remove it, to reset the value of 'Buttons' appropriately - Otherwise, just call the Remove function for that gadget to remove it from any list it may be in ------------------------------------------------------------------------*/ if (Buttons == &gadget) { Remove_A_Button(gadget); } else { gadget.Remove(); } /*------------------------------------------------------------------------ Now add the gadget to our list: - If there are not buttons, start the list with this one - Otherwise, add it to the tail of the existing list ------------------------------------------------------------------------*/ if (Buttons) { gadget.Add_Tail(*Buttons); } else { Buttons = &gadget; } } /*********************************************************************************************** * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * * * * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * * * * OUTPUT: none * * * * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * * will be invalid! * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void GScreenClass::Remove_A_Button(GadgetClass & gadget) { Buttons = gadget.Remove(); } /*********************************************************************************************** * GScreenClass::Render -- General drawing dispatcher an display update function. * * * * This routine should be called in the main game loop (once every game frame). It will * * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * * copied to the visible page. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: This actually updates the graphic display. As a result it can take quite a * * while to perform. * * * * HISTORY: * * 12/15/1994 JLB : Created. * *=============================================================================================*/ void GScreenClass::Render(void) { //if (Buttons && Buttons->Is_List_To_Redraw()) { // IsToRedraw = true; //} if (IsToUpdate || IsToRedraw) { //WWMouse->Erase_Mouse(&HidPage, TRUE); GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); //if (IsToRedraw) { // Hide_Mouse(); // SeenBuff.To_Buffer(0, 0, 320, 200, ShadowPage); // Show_Mouse(); //} Draw_It(IsToRedraw); if (Buttons) Buttons->Draw_All(false); #ifdef SCENARIO_EDITOR /* ** Draw the Editor's buttons */ if (Debug_Map) { if (Buttons) { Buttons->Draw_All(); } } #endif /* ** Draw the multiplayer message system to the Hidpage at this point. ** This way, they'll Blit along with the rest of the map. */ if (Messages.Num_Messages() > 0) { Messages.Set_Width(Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); } Messages.Draw(); //Blit_Display(); // 5/19/20 SKY - Skip copying to scene page, we can get the data directly from hidden page IsToUpdate = false; IsToRedraw = false; Set_Logic_Page(oldpage); } } #ifdef CHEAT_KEYS #define MAX_SCREENS_SAVED 30*15 // Enough for 30 seconds @ 15 fps GraphicBufferClass *ScreenList[MAX_SCREENS_SAVED]; int CurrentScreen = 0; bool ScreenRecording = false; void Add_Current_Screen(void) { #if (0) // ST - 1/2/2019 5:51PM if (ScreenRecording){ ScreenList[CurrentScreen] = new GraphicBufferClass; ScreenList[CurrentScreen]->Init ( SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL, 0, (GBC_Enum) 0); SeenBuff.Blit (*ScreenList[CurrentScreen]); CurrentScreen++; if (CurrentScreen == MAX_SCREENS_SAVED){ char filename[20]; for (int i = 0 ; i < MAX_SCREENS_SAVED ; i++){ sprintf (filename, "SCRN%04d.PCX", i); Write_PCX_File (filename,*ScreenList[i], (unsigned char *)CurrentPalette); delete ScreenList[i]; } CurrentScreen = 0; ScreenRecording = 0; } } #endif } #endif //CHEAT_KEYS extern bool CanVblankSync; /*********************************************************************************************** * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * * * * This routine is used to copy the correct display from the HIDPAGE * * to the SEENPAGE. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 02/14/1994 JLB : Created. * * 05/01/1994 JLB : Converted to member function. * *=============================================================================================*/ void GScreenClass::Blit_Display(void) { if (SeenBuff.Get_Width()!=320){ #if (0) if (HidPage.Get_IsDirectDraw() && (Options.GameSpeed >1 || Options.ScrollRate==6 && CanVblankSync) ){ WWMouse->Draw_Mouse(&HidPage); SeenBuff.Get_Graphic_Buffer()->Get_DD_Surface()->Flip(NULL , DDFLIP_WAIT); SeenBuff.Blit (HidPage , 0 , 0 , 0 , 0 , SeenBuff.Get_Width() , SeenBuff.Get_Height() , (BOOL) FALSE ); #ifdef CHEAT_KEYS Add_Current_Screen(); #endif //HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); WWMouse->Erase_Mouse(&HidPage, FALSE); }else{ #else //(0) WWMouse->Draw_Mouse(&HidPage); HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); #ifdef CHEAT_KEYS Add_Current_Screen(); #endif WWMouse->Erase_Mouse(&HidPage, FALSE); #endif //(0) #if (0) } #endif //(0) } else { // ST - 1/2/2019 5:26PM //ModeX_Blit (&HiddenPage); } }