CnC_Remastered_Collection/TIBERIANDAWN/GSCREEN.CPP

529 lines
29 KiB
C++

//
// 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 <filepcx.h>
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);
}
}