// // 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: /counterstrike/EGOS.CPP 2 3/10/97 3:19p Steve_tall $ */ /************************************************************************************* ** 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 - Red Alert * * * * File Name : EGOS.CPP * * * * Programmer : Steve Tall * * * * Start Date : September 4th, 1996 * * * * Last Update : September 4th, 1996 [ST] * * * *-----------------------------------------------------------------------------------* * Overview: * * * * Scrolling movie style credits. * * * *-----------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ //PG_TO_FIX #include "function.h" #include "memflag.h" /* ** List of Ego Class instances ** There will be one instance for each line of text. */ DynamicVectorClass EgoList; /* ** Number of slideshow pictures */ #define NUM_SLIDES 17 /* ** Length of time frame is displayed for */ #define FRAME_DELAY 150 /* ** Number of frames that palete fade occurs over */ #define FADE_DELAY 37 /* ** Names of slideshow pictures to play behind the text */ char SlideNames[NUM_SLIDES][13]={ "aftr_hi.pcx", "aly1.pcx", "apc_hi.pcx", "aphi0049.pcx", "bnhi0020.pcx", "dchi0040.pcx", "frhi0166.pcx", "lab.pcx", "landsbrg.pcx", "mahi0107.pcx", "mig_hi.pcx", "mtfacthi.pcx", "needle.pcx", "sov2.pcx", "spy.pcx", "stalin.pcx", "tent.pcx" }; /* ** Array of all the palettes required for the slides */ char SlidePals[NUM_SLIDES][256*3]; /* ** Array of graphic buffers containing the slides */ GraphicBufferClass *SlideBuffers[NUM_SLIDES]; /* ** Original copy of slide (pref in video mem) that we use to undraw the text */ GraphicBufferClass *BackgroundPage; /* ** This palette contains both the font palette entries and the slide ** palette. */ PaletteClass ComboPalette; /* ** Ptr to the combo palette. */ unsigned char *ComboPalPtr; /* ** Lookup table. If an entry is non-zero then it should be faded in/out when the slide changes. */ char PaletteLUT[256]; /* ** Height of the strips that are blitted from the slides to the backgound and hid pages. ** We blit in several strips over several frames so as not to impact on the frame rate. */ #define CHUNK_HEIGHT RESFACTOR * 50 #ifndef WIN32 extern void Vsync(void); #pragma aux Vsync modify [edx ebx eax] = \ "mov edx,03DAh" \ "mov ebx,[VertBlank]" \ "and bl,001h" \ "shl bl,3" \ "in_vbi:" \ "in al,dx" \ "and al,008h" \ "xor al,bl" \ "je in_vbi" \ "out_vbi:" \ "in al,dx" \ "and al,008h" \ "xor al,bl" \ "jne out_vbi" #endif //WIN32 /*********************************************************************************************** * EC::EgoClass -- EgoClass constructor * * * * * * * * INPUT: x position of text * * y position of text * * ptr to text string * * flags to print text with * * * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:53PM ST : Created * *=============================================================================================*/ EgoClass::EgoClass (int x, int y, char *text, TextPrintType flags) { XPos = x; YPos = y; Flags= flags; Text = new char [strlen (text)+1]; strcpy (Text, text); } /*********************************************************************************************** * EC::~EgoClass -- EgoClass destructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:54PM ST : Created * *=============================================================================================*/ EgoClass::~EgoClass(void) { delete [] Text; } /*********************************************************************************************** * EC::Scroll -- Apply the given distance to the y position of the text. * * A positive distance scrolls up. * * * * * * INPUT: distance in pixels to scroll up * * * * OUTPUT: true if text scrolled beyond the top of the screen * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:55PM ST : Created * *=============================================================================================*/ bool EgoClass::Scroll(int distance) { YPos -= distance; if (YPos < -20) { return (true); }else{ return (false); } } /*********************************************************************************************** * EC::Render -- Draws the text to the logic page * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:57PM ST : Created * *=============================================================================================*/ void EgoClass::Render (void) { if (YPos < LogicPage->Get_Height() && YPos > -16) { Fancy_Text_Print(Text, XPos, YPos, GadgetClass::Get_Color_Scheme(), TBLACK, Flags); } } /*********************************************************************************************** * EC::Wipe -- Wipes the previously rendered text by blitting a rectangle from the given * * background screen. * * * * * * INPUT: ptr to screen containing original background * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:58PM ST : Created * *=============================================================================================*/ void EgoClass::Wipe (GraphicBufferClass *background) { int width = String_Pixel_Width (Text); int x = XPos; if (Flags & TPF_RIGHT) { x -= width; }else{ if (Flags & TPF_CENTER){ x -= width/2; } } GL_FillRect( TBLACK, x - 1, YPos, width + 2, 7 * RESFACTOR + 1 ); //background->Blit(*LogicPage, x-1, YPos, x-1, YPos, width+2, 7 * RESFACTOR +1, false); } /*********************************************************************************************** * Set_Pal -- Low level palette set * * * * * * * * INPUT: ptr to palette * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/9/96 11:59PM ST : Created * *=============================================================================================*/ void Set_Pal(char *palette) { //#ifndef WIN32 //Vsync(); //unsigned char *rgbptr = (unsigned char *) palette; //outportb(0x03C8, 0); //Start from color 0 //for (int index = 0; index < 256; index++) { // outrgb(rgbptr[index*3], rgbptr[index*3+1], rgbptr[index*3+2]); //} //#else //WIN32 Set_Palette((void*)palette); //#endif } /*********************************************************************************************** * Slide_Show -- Handles the blitting and fading of the background pictures. * * * * The picture frame number is used to trigger blitting and fading events * * * * * * INPUT: picture number * * frame * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/10/96 0:16AM ST : Created * *=============================================================================================*/ void Copy_Palette(PaletteClass const & source, PaletteClass & destination) { for (int index = 0; index < source.COLOR_COUNT; index++) { destination[index] = source[index]; } } void Slide_Show(int slide, int frame) { /* ** Temprary storage to save CCPalette to */ //char save_palette[256*3]; PaletteClass save_palette; if (frame >= 1 && frame <=4){ /* ** Blit in a quarter of the new frame to the background page. */ // jmarshall - removed // SlideBuffers[slide]->Blit (*BackgroundPage, 0, (frame-1) * CHUNK_HEIGHT, // 0, (frame-1) * CHUNK_HEIGHT, // SeenBuff.Get_Width(), CHUNK_HEIGHT, false); // jmarshall - removed return; } if (frame >= 5 && frame <=8 ){ /* ** Blit in a quarter of the new frame to the hid page. */ // jmarshall - removed //BackgroundPage->Blit (HidPage, 0, (frame-5) * CHUNK_HEIGHT, // 0, (frame-5) * CHUNK_HEIGHT, // SeenBuff.Get_Width(), CHUNK_HEIGHT, false); // jmarshall - removed return; } if (frame ==9){ /* ** Create the combo palette from the font entries and the picture entries. */ for (int index = 0 ; index < 256 ; index++ ){ if (PaletteLUT[index]) { ComboPalPtr[index * 3] = SlidePals[slide][index * 3]; ComboPalPtr[index * 3 + 1] = SlidePals[slide][index*3 + 1]; ComboPalPtr[index * 3 + 2] = SlidePals[slide][index*3 + 2]; } } return; } if (frame >10 && frame < FADE_DELAY+10){ /* ** Fade up the picture in the background. The text colors never fade. */ Copy_Palette( CCPalette, save_palette ); //CCPalette.Partial_Adjust (MIN (6*(frame-5), 255), ComboPalette, PaletteLUT); int ratio = MIN( ( 255 / FADE_DELAY - 1 ) * (frame - 10), 255 ); CCPalette.Partial_Adjust ( ratio, ComboPalette, PaletteLUT); //CCPalette.Partial_Adjust( 255, ComboPalette, PaletteLUT ); Set_Pal ( (char *) CCPalette.Get_Data() ); if (frame != 9+FADE_DELAY) { Copy_Palette( save_palette, CCPalette ); } return; } if (frame >FRAME_DELAY && frame < FRAME_DELAY+FADE_DELAY){ /* ** Fade down the picture in the background. The text colors never fade. */ Copy_Palette( CCPalette, save_palette ); CCPalette.Partial_Adjust ( 0, PaletteLUT ); int ratio = MIN( 255 / ( (FRAME_DELAY + FADE_DELAY) - FRAME_DELAY ) * (frame - FRAME_DELAY), 255); CCPalette.Partial_Adjust( 255 - ratio, ComboPalette, PaletteLUT); //CCPalette.Partial_Adjust( 0, ComboPalette, PaletteLUT ); if (frame != FRAME_DELAY+FADE_DELAY-1) { Set_Pal ( (char *) CCPalette.Get_Data() ); Copy_Palette( save_palette, CCPalette ); }else{ /* ** If this is the last fade down frame then zero the picture palette entries. */ unsigned char *ccpalptr = (unsigned char*)&CCPalette; for (int index = 0 ; index < 256 ; index++){ if (PaletteLUT[index]){ ccpalptr[index*3] = 0; ccpalptr[index*3+1] = 0; ccpalptr[index*3+2] = 0; } } Set_Pal ( (char *) CCPalette.Get_Data() ); } } } /*********************************************************************************************** * Show_Who_Was_Responsible -- Main function to print the credits. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/10/96 0:20AM ST : Created * *=============================================================================================*/ void Show_Who_Was_Responsible (void) { int i; int key; /* ** Deault speed of credits scolling. This is the frame delay between pixel scrolls. */ static int speed = 3; /* ** In DOS we need to scroll slower so we have a bool that lets us do it every other time */ #ifndef WIN32 bool scroll_now = false; #endif //WIN32 /* ** Read in the credits file to be displayed ** ** Lines of text in CREDITS.TXT are treated as follows.... ** ** If the text starts and ends to the left of column 40 then text will be right justified. ** If the text starts before column 40 and ends after it then it will be centered. ** If the text starts after column 40 it will be right justified. */ CCFileClass creditsfile ("credits.txt"); if ( !creditsfile.Is_Available()) return; char *credits = new char [creditsfile.Size()+1]; creditsfile.Read (credits, creditsfile.Size()); /* ** Initialise the text printing system. */ GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_GREEN]); Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); /* ** Miscellaneous stuff for parsing the credits text file. */ int length = creditsfile.Size(); int line = 0; int column = 0; char *cptr = credits; char ch, lastchar, oldchar; char *strstart, *strparse; bool gotendstr; int startcolumn, endcolumn, x; int y=SeenBuff.Get_Height()+2; EgoClass *ego; TextPrintType flags; /* ** Search through the text file and extract the strings, using each string to create ** a new EgoClass */ do { /* ** Search for text */ ch = *cptr++; length --; /* ** Look for a non whitespace character. */ switch ( ch ){ case 13: /* ** Char was carriage return. Go on to the next line starting at column 0. */ line++; column = 0; break; case 10: /* ** Ignore line feed. CR does both. */ break; /* ** Space character. Just advance the cursor and move on. */ case 32: column++; break; /* ** Tab char. Advance to the next tab column. Tabs are every 8 columns. */ case 9: column += 8; column &= 0xfffffff8; break; default: /* ** Found new string. Work out where it ends so we know how to treat it. */ lastchar = ch; strstart = cptr-1; strparse = cptr-1; endcolumn = startcolumn = column; gotendstr = false; do { ch = *strparse++; switch ( ch ){ case 9: case 10: case 13: gotendstr = true; break; case 32: if (lastchar == 32) gotendstr = true; endcolumn++; break; default: endcolumn++; } if (strparse >= cptr+length) gotendstr = true; lastchar = ch; }while (!gotendstr); if (strparse >= cptr+length) break; /* ** Strip off any trailing space. */ if (*(strparse-2) == 32){ strparse--; endcolumn -= 2; } /* ** If string straddles the center column then center it. ** ** If string is on the left hand side then right justify it. ** ** If string is on the right hand side then left justify it. */ flags = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW; //TPF_NOSHADOW; if (startcolumn <40 && endcolumn >40){ flags = flags | TPF_CENTER; x = SeenBuff.Get_Width() / 2; }else{ if (startcolumn <40){ flags = flags | TPF_RIGHT; x = endcolumn *SeenBuff.Get_Width() /80; }else{ x = startcolumn * SeenBuff.Get_Width() / 80; } } /* ** Temporarily terminate the string. */ oldchar = *(strparse-1); *(strparse-1) = 0; /* ** Create the new class and add it to our list. */ ego = new EgoClass (x, y+ line *8 *RESFACTOR, strstart, flags); EgoList.Add (ego); /* ** Restore the character that was lost when we added the terminator. */ *(strparse-1) = oldchar; /* ** Fix up our outer loop parsing variables. */ cptr = strparse; column += strparse - strstart; length -= strparse - strstart-1; if (ch == 13) { line++; column = 0; }else{ if (ch == 9){ column += 7; column &= 0xfffffff8; } } break; } } while ( length>0 ); /* ** Work out which palette entries the font needs so we dont fade those colors. */ memset (PaletteLUT, 1, sizeof (PaletteLUT)); int pcolor = PCOLOR_GREEN; for (int index = 0; index < 6; index++) { PaletteLUT[ColorRemaps[pcolor].FontRemap[10+index]] =0; } //PaletteLUT[ColorRemaps[pcolor].BrightColor] = 0; PaletteLUT[ColorRemaps[pcolor].Color] = 0; PaletteLUT[ColorRemaps[pcolor].Shadow] = 0; PaletteLUT[ColorRemaps[pcolor].Background] = 0; PaletteLUT[ColorRemaps[pcolor].Corners] = 0; PaletteLUT[ColorRemaps[pcolor].Highlight] = 0; PaletteLUT[ColorRemaps[pcolor].Bright] = 0; PaletteLUT[ColorRemaps[pcolor].Underline] = 0; PaletteLUT[ColorRemaps[pcolor].Bar] = 0; PaletteLUT[ColorRemaps[pcolor].Box] = 0; /* ** Stop the music. */ Theme.Stop(); /* ** Fade to black. */ BlackPalette.Set(TIMER_SECOND*2, Call_Back); /* ** Load the reference palette for the font. */ CCFileClass("EGOPAL.PAL").Read(&CCPalette, sizeof(CCPalette)); /* ** Copy the font palette entries into the combo palette. */ Copy_Palette( CCPalette, ComboPalette ); ComboPalPtr = (unsigned char*)&ComboPalette; for (index = 0 ; index < 256 ; index++ ) { if (PaletteLUT[index]) { ComboPalPtr[index*3] = 0; ComboPalPtr[index*3+1] = 0; ComboPalPtr[index*3+2] = 0; } } /* ** Clear the Seen Page since we will not be blitting to all of it. */ SeenBuff.Clear(); HidPage.Clear(); /* ** Set the font palette. */ Copy_Palette( ComboPalette, CCPalette ); CCPalette.Set(); /* ** Loop through and load up all the slideshow pictures */ for (index = 0 ; index < NUM_SLIDES ; index++) { //SlideBuffers[index] = new GraphicBufferClass; //SlideBuffers[index]->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); //Load_Title_Screen( &SlideNames[index][0], SlideBuffers[index], (unsigned char*) &SlidePals[index][0] ); } /* ** Create a new graphic buffer to restore the background from. Initialise it to black so ** we can start scrolling before the first slideshow picture is blitted. */ BackgroundPage = new GraphicBufferClass; BackgroundPage->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); //SeenBuff.Blit(*BackgroundPage); /* ** Go away nasty keyboard. */ Keyboard->Clear(); Set_Logic_Page(HidPage); /* ** Start any old song. */ fixed oldvolume = Options.ScoreVolume; if (oldvolume == 0) { Options.Set_Score_Volume(fixed(4, 10), false); } Theme.Queue_Song(THEME_CREDITS); /* ** Init misc timing variables. */ int time = TickCount; int frame = 0; int picture_frame = 0; int slide_number = 0; Hide_Mouse(); key = KN_NONE; /* ** Main scrolling loop. ** Keeps going until all the EgoClass objects are deleted or esc is pressed. */ while ( EgoList.Count() ) { frame++; /* ** Update the slideshow frame and switch to the next picture if its time. */ picture_frame++; if (picture_frame > FRAME_DELAY+50){ if (slide_number =0 ; i--){ EgoList[i]->Wipe(BackgroundPage); if ( EgoList[i]->Scroll(1) ){ EgoList.Delete(i); break; } } /* ** Render all the text strings in their new positions. */ if (LogicPage->Lock()){ for (i=EgoList.Count()-1 ; i>=0 ; i--){ EgoList[i]->Render(); } LogicPage->Unlock(); } if (frame > 1000 && !Theme.Still_Playing()){ Theme.Queue_Song(THEME_CREDITS); //NONE); } /* ** Stop calling Theme.AI after a while so a different song doesnt start playing */ Call_Back(); /* ** Kill any spare time before blitting the hid page forward. */ while (TickCount - time < frame *speed && !Keyboard->Check()) {} /* ** Blit all but the top and bottom of the hid page. This is beacuse the text print doesn't ** clip vertically and looks ugly when it suddenly appears and disappears. */ // jmarshall - removed //HidPage.Blit(SeenBuff, 0, 8 * RESFACTOR, 0, 8*RESFACTOR, SeenBuff.Get_Width(), SeenBuff.Get_Height() - 16* RESFACTOR, false); // jmarshall - removed /* ** If user hits escape then break. */ g_globalKeyNumType = KN_NONE; g_globalKeyFlags = 0; UserInput.Process_Input(); key = Keyboard->Check(); if (key != 0) { key = Keyboard->Get(); if (key == KN_ESC){ break; } } } if (key == KN_ESC) { Theme.Fade_Out(); BlackPalette.Set(TIMER_SECOND*2, Call_Back); } else { /* ** Wait for the picture to fade down */ while (picture_frame <= FADE_DELAY+FRAME_DELAY){ if (picture_frame < FRAME_DELAY && picture_frame > 10+FADE_DELAY){ picture_frame = FRAME_DELAY; } frame++; picture_frame++; Slide_Show(slide_number, picture_frame); Call_Back(); // Sound_Callback(); //Theme.AI(); /* ** Kill any spare time */ while (TickCount - time < frame *speed && !Keyboard->Check()) {} } } /* ** Tidy up. */ //SetPriorityClass (GetCurrentProcess() , process_priority); SeenBuff.Clear(); Show_Mouse(); GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); Theme.Stop(); Options.Set_Score_Volume(oldvolume, false); for (index = 0 ; index < NUM_SLIDES ; index++){ delete SlideBuffers[index]; } delete BackgroundPage; delete [] credits; EgoList.Clear(); }