/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program 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. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Credits.cpp ///////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Electronic Arts Pacific. // // Confidential Information // Copyright (C) 2002 - All Rights Reserved // //----------------------------------------------------------------------------- // // created: Dec 2002 // // Filename: Credits.cpp // // author: Chris Huybregts // // purpose: This is where all the credit texts is going to be held. // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // SYSTEM INCLUDES //////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine //----------------------------------------------------------------------------- // USER INCLUDES ////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- #include "Common/INI.h" #include "GameClient/Credits.h" #include "GameClient/DisplayStringManager.h" #include "GameClient/Display.h" #include "GameClient/GameText.h" #include "GameClient/GlobalLanguage.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- CreditsManager *TheCredits = NULL; const FieldParse CreditsManager::m_creditsFieldParseTable[] = { { "ScrollRate", INI::parseInt, NULL, offsetof( CreditsManager, m_scrollRate ) }, { "ScrollRateEveryFrames", INI::parseInt, NULL, offsetof( CreditsManager, m_scrollRatePerFrames ) }, { "ScrollDown", INI::parseBool, NULL, offsetof( CreditsManager, m_scrollDown ) }, { "TitleColor", INI::parseColorInt, NULL, offsetof( CreditsManager, m_titleColor ) }, { "MinorTitleColor", INI::parseColorInt, NULL, offsetof( CreditsManager, m_positionColor ) }, { "NormalColor", INI::parseColorInt, NULL, offsetof( CreditsManager, m_normalColor ) }, { "Style", INI::parseLookupList, CreditStyleNames, offsetof( CreditsManager, m_currentStyle ) }, { "Blank", CreditsManager::parseBlank, NULL, NULL }, { "Text", CreditsManager::parseText, NULL, NULL }, { NULL, NULL, NULL, 0 } // keep this last }; //----------------------------------------------------------------------------- // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- void INI::parseCredits( INI *ini ) { // find existing item if present DEBUG_ASSERTCRASH( TheCredits, ("parseCredits: TheCredits has not been ininialized yet.\n") ); if( !TheCredits ) return; // parse the ini definition ini->initFromINI( TheCredits, TheCredits->getFieldParse() ); } // end parseCommandButtonDefinition CreditsLine::CreditsLine() { m_useSecond = FALSE; m_done = FALSE; m_style = CREDIT_STYLE_BLANK; m_displayString = NULL; m_secondDisplayString = NULL; } CreditsLine::~CreditsLine() { if(m_displayString) TheDisplayStringManager->freeDisplayString(m_displayString); if(m_secondDisplayString) TheDisplayStringManager->freeDisplayString(m_secondDisplayString); m_displayString = NULL; m_secondDisplayString = NULL; } CreditsManager::CreditsManager(void) { m_scrollRate = 1; // in pixels m_scrollRatePerFrames = 1; m_scrollDown = TRUE; // if TRUE text will come from the top to the bottom if False, it will go from the bottom up m_framesSinceStarted = 0; m_titleColor = m_positionColor = m_normalColor = GameMakeColor(255,255,255,255); m_currentStyle = CREDIT_STYLE_NORMAL; m_isFinished = FALSE; m_normalFontHeight = 10; } CreditsManager::~CreditsManager(void) { m_displayedCreditLineList.clear(); CreditsLineList::iterator it =m_creditLineList.begin(); while (it != m_creditLineList.end()) { CreditsLine *cLine = *it; delete cLine; it = m_creditLineList.erase(it); } } void CreditsManager::init(void ) { m_isFinished = FALSE; m_creditLineListIt = m_creditLineList.begin(); m_framesSinceStarted = 0; } void CreditsManager::load(void ) { INI ini; // Read from INI all the ControlBarSchemes ini.load( AsciiString( "Data\\INI\\Credits.ini" ), INI_LOAD_OVERWRITE, NULL ); if(m_scrollRatePerFrames <=0) m_scrollRatePerFrames = 1; if(m_scrollRate <=0) m_scrollRate = 1; GameFont *font = TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsNormalFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsNormalFont.size), TheGlobalLanguageData->m_creditsNormalFont.bold); m_normalFontHeight = font->height; } void CreditsManager::reset( void ) { m_displayedCreditLineList.clear(); m_isFinished = FALSE; m_creditLineListIt = m_creditLineList.begin(); m_framesSinceStarted = 0; } void CreditsManager::update( void ) { if(m_isFinished) return; m_framesSinceStarted++; if(m_framesSinceStarted%m_scrollRatePerFrames != 0) return; Int y = 0; Int yTest = 0; Int lastHeight = 0; Int start = m_scrollDown? 0:TheDisplay->getHeight(); Int end =m_scrollDown? TheDisplay->getHeight():0; Int offsetStartMultiplyer = m_scrollDown? -1:0; // if we're scrolling from the top, we need to subtract the height Int offsetEndMultiplyer = m_scrollDown? 0:1; Int directionMultiplyer = m_scrollDown? 1:-1; CreditsLineList::iterator drawIt = m_displayedCreditLineList.begin(); while (drawIt != m_displayedCreditLineList.end()) { CreditsLine *cLine = *drawIt; y = cLine->m_pos.y = cLine->m_pos.y + (m_scrollRate * directionMultiplyer); lastHeight = cLine->m_height; yTest = y + ((lastHeight + CREDIT_SPACE_OFFSET) * offsetEndMultiplyer); if(((m_scrollDown && (yTest > end)) || (!m_scrollDown && (yTest < end)))) { TheDisplayStringManager->freeDisplayString(cLine->m_displayString); TheDisplayStringManager->freeDisplayString(cLine->m_secondDisplayString); cLine->m_displayString = NULL; cLine->m_secondDisplayString = NULL; drawIt = m_displayedCreditLineList.erase(drawIt); } else drawIt++; } y= y + ((lastHeight + CREDIT_SPACE_OFFSET) * offsetStartMultiplyer); // is it time to add a new string? if(!((m_scrollDown && (yTest >= start)) || (!m_scrollDown && (yTest <= start)))) return; if(m_displayedCreditLineList.size() == 0 && m_creditLineListIt == m_creditLineList.end()) m_isFinished = TRUE; if(m_creditLineListIt == m_creditLineList.end()) return; CreditsLine *cLine = *m_creditLineListIt; ICoord2D pos; switch (cLine->m_style) { case CREDIT_STYLE_TITLE: { cLine->m_color = m_titleColor; if(TheGlobalLanguageData&& !cLine->m_text.isEmpty()) { DisplayString *ds = TheDisplayStringManager->newDisplayString(); if(!ds) return; ds->setFont(TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsTitleFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsTitleFont.size), TheGlobalLanguageData->m_creditsTitleFont.bold)); ds->setText(cLine->m_text); ds->getSize(&pos.x,&pos.y); cLine->m_height = pos.y; cLine->m_pos.x = TheDisplay->getWidth()/2 - pos.x/2 ; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); cLine->m_displayString = ds; } } break; case CREDIT_STYLE_POSITION: { cLine->m_color = m_positionColor; if(TheGlobalLanguageData && !cLine->m_text.isEmpty()) { DisplayString *ds = TheDisplayStringManager->newDisplayString(); if(!ds) return; ds->setFont(TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsPositionFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsPositionFont.size), TheGlobalLanguageData->m_creditsPositionFont.bold)); ds->setText(cLine->m_text); ds->getSize(&pos.x,&pos.y); cLine->m_height = pos.y; cLine->m_pos.x = TheDisplay->getWidth()/2 - pos.x/2 ; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); cLine->m_displayString = ds; } } break; case CREDIT_STYLE_NORMAL: { cLine->m_color = m_normalColor; if(TheGlobalLanguageData && !cLine->m_text.isEmpty()) { DisplayString *ds = TheDisplayStringManager->newDisplayString(); if(!ds) return; ds->setFont(TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsNormalFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsNormalFont.size), TheGlobalLanguageData->m_creditsNormalFont.bold)); ds->setText(cLine->m_text); ds->getSize(&pos.x,&pos.y); cLine->m_height = pos.y; cLine->m_pos.x = TheDisplay->getWidth()/2 - pos.x/2 ; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); cLine->m_displayString = ds; } } break; case CREDIT_STYLE_COLUMN: { cLine->m_color = m_normalColor; if(TheGlobalLanguageData && !cLine->m_text.isEmpty()) { DisplayString *ds = TheDisplayStringManager->newDisplayString(); if(!ds) return; ds->setFont(TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsNormalFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsNormalFont.size), TheGlobalLanguageData->m_creditsNormalFont.bold)); ds->setText(cLine->m_text); ds->getSize(&pos.x,&pos.y); cLine->m_height = pos.y; cLine->m_pos.x = TheDisplay->getWidth()/2 - pos.x/2 ; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); cLine->m_displayString = ds; } if(TheGlobalLanguageData && !cLine->m_secondText.isEmpty()) { DisplayString *ds = TheDisplayStringManager->newDisplayString(); if(!ds) return; ds->setFont(TheFontLibrary->getFont(TheGlobalLanguageData->m_creditsNormalFont.name, TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_creditsNormalFont.size), TheGlobalLanguageData->m_creditsNormalFont.bold)); ds->setText(cLine->m_secondText); ds->getSize(&pos.x,&pos.y); cLine->m_height = pos.y; cLine->m_pos.x = TheDisplay->getWidth()/2 - pos.x/2 ; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); cLine->m_secondDisplayString = ds; } } break; case CREDIT_STYLE_BLANK: { cLine->m_height = m_normalFontHeight; cLine->m_pos.y = start + (cLine->m_height * offsetStartMultiplyer); } break; } m_displayedCreditLineList.push_back(cLine); if(m_creditLineListIt != m_creditLineList.end()) m_creditLineListIt++; } void CreditsManager::draw( void ) { CreditsLineList::iterator drawIt = m_displayedCreditLineList.begin(); while (drawIt != m_displayedCreditLineList.end()) { CreditsLine *cLine = *drawIt; Int heightChunk = TheDisplay->getHeight()/3; Real perc = 0.0f; if(cLine->m_pos.y < heightChunk || cLine->m_pos.y > heightChunk * 2) { // adjust the color if( cLine->m_pos.y < 0 || cLine->m_pos.y > TheDisplay->getHeight()) perc = 0.0f; else if( cLine->m_pos.y < heightChunk) perc = INT_TO_REAL(cLine->m_pos.y) / heightChunk; else perc = 1 - INT_TO_REAL(cLine->m_pos.y - 2*heightChunk) / heightChunk; } else perc = 1.0f; UnsignedByte r,g,b,a; GameGetColorComponents(cLine->m_color, &r, &g, &b, &a); Int color = GameMakeColor( r,g,b, a * perc); Int bColor= GameMakeColor( 0,0,0, a * perc); switch (cLine->m_style) { case CREDIT_STYLE_TITLE: case CREDIT_STYLE_POSITION: case CREDIT_STYLE_NORMAL: { if(cLine->m_displayString) cLine->m_displayString->draw(cLine->m_pos.x,cLine->m_pos.y,color, bColor, 1,1 ); } break; case CREDIT_STYLE_COLUMN: { Int chunk = TheDisplay->getWidth()/3; ICoord2D pos; if(cLine->m_displayString) { cLine->m_displayString->getSize(&pos.x, &pos.y); cLine->m_displayString->draw(chunk - (pos.x/2),cLine->m_pos.y,color, bColor, 1,1 ); } if(cLine->m_secondDisplayString) { cLine->m_secondDisplayString->getSize(&pos.x, &pos.y); cLine->m_secondDisplayString->draw(2*chunk - (pos.x/2),cLine->m_pos.y,color, bColor, 1,1 ); } } break; } drawIt++; } } void CreditsManager::addBlank( void ) { CreditsLine *cLine = new CreditsLine; cLine->m_style = CREDIT_STYLE_BLANK; m_creditLineList.push_back(cLine); } void CreditsManager::parseBlank( INI* ini, void *instance, void *store, const void *userData ) { CreditsManager *cManager = (CreditsManager *)instance; cManager->addBlank(); } void CreditsManager::parseText( INI* ini, void *instance, void *store, const void *userData ) { AsciiString asciiString = ini->getNextQuotedAsciiString(); CreditsManager *cManager = (CreditsManager *)instance; cManager->addText(asciiString); } void CreditsManager::addText( AsciiString text ) { CreditsLine *cLine = new CreditsLine; switch (m_currentStyle) { case CREDIT_STYLE_TITLE: case CREDIT_STYLE_POSITION: case CREDIT_STYLE_NORMAL: { cLine->m_text = getUnicodeString(text); cLine->m_style = m_currentStyle; m_creditLineList.push_back(cLine); } break; case CREDIT_STYLE_COLUMN: { CreditsLineList::reverse_iterator rIt = m_creditLineList.rbegin(); CreditsLine *rcLine = *rIt; if(rIt == m_creditLineList.rend() || rcLine->m_style != CREDIT_STYLE_COLUMN || (rcLine->m_style == CREDIT_STYLE_COLUMN && rcLine->m_done == TRUE)) { cLine->m_text = getUnicodeString(text); cLine->m_style = CREDIT_STYLE_COLUMN; cLine->m_useSecond = TRUE; m_creditLineList.push_back(cLine); } else { rcLine->m_secondText = getUnicodeString(text); rcLine->m_done = TRUE; delete cLine; } } break; default: DEBUG_ASSERTCRASH( FALSE, ("CreditsManager::addText we tried to add a credit text with the wrong style before it. Style is %d\n", m_currentStyle) ); delete cLine; } } //----------------------------------------------------------------------------- // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- UnicodeString CreditsManager::getUnicodeString(AsciiString str) { UnicodeString uStr; if(str.compare("") == 0) return UnicodeString::TheEmptyString; if(str.find(':')) uStr = TheGameText->fetch(str); else uStr.translate(str); return uStr; }