/* ** Command & Conquer Renegade(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 . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Commando/textdisplay.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 1/02/02 2:27a $* * * * $Revision:: 55 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "textdisplay.h" #include "font3d.h" #include "assets.h" #include "render2D.h" #include "timemgr.h" #include "_globals.h" #include "registry.h" #include "wwprofile.h" #include "combat.h" #include "ccamera.h" #include "render2Dsentence.h" #include "wwmemlog.h" #include "consolemode.h" /* ** TextDisplayLine */ TextDisplayLine::TextDisplayLine( const WideStringClass & text, unsigned long color ) : Time( 0 ), LineCount( 0 ), Text( text ), Color( color ) { for( const WCHAR * ch = text; *ch; ch++ ) { if ( *ch == (WCHAR)'\n' ) { LineCount++; } } } /* ** TextDisplayGameMode */ TextDisplayGameModeClass * TextDisplayGameModeClass::Instance = NULL; void TextDisplayGameModeClass::Init() { if (!ConsoleBox.Is_Exclusive()) { // Build Fonts WWASSERT(WW3DAssetManager::Get_Instance() != NULL); Font = WW3DAssetManager::Get_Instance()->Get_Font3DInstance( "FONT8x8.TGA" ); WWASSERT(Font != NULL); SET_REF_OWNER( Font ); MonoFont = WW3DAssetManager::Get_Instance()->Get_Font3DInstance( "FONT8x8.TGA" ); WWASSERT(MonoFont != NULL); SET_REF_OWNER( MonoFont ); MonoFont->Set_Mono_Spaced(); // Update Instance WWASSERT( TextDisplayGameModeClass::Instance == NULL ); TextDisplayGameModeClass::Instance = this; DisplayVisWarning = false; MaxScrollLines = 30; ScrollLinesPersistTime = 10; Load_Registry_Keys(); Display = new Render2DTextClass(Font); Display->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() ); VerboseDisplay = new Render2DTextClass(Font); VerboseDisplay->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() ); StatisticsDisplay = new Render2DTextClass(MonoFont); StatisticsDisplay->Set_Coordinate_Range( Render2DClass::Get_Screen_Resolution() ); RendererLines.Reset_Active(); RendererColors.Reset_Active(); TextChanged=true; VerboseTextChanged=true; DisplayY=0.0f; } return ; } void TextDisplayGameModeClass::Shutdown() { if (!ConsoleBox.Is_Exclusive()) { Save_Registry_Keys(); Flush(); delete StatisticsDisplay; StatisticsDisplay=NULL; delete VerboseDisplay; VerboseDisplay=NULL; delete Display; Display=NULL; REF_PTR_RELEASE( Font ); REF_PTR_RELEASE( MonoFont ); // Update Instance WWASSERT( TextDisplayGameModeClass::Instance == this ); TextDisplayGameModeClass::Instance = NULL; } } /* ** */ void TextDisplayGameModeClass::Load_Registry_Keys(void) { RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS ); WWASSERT( registry ); if ( registry->Is_Valid() ) { MaxScrollLines = registry->Get_Int( "MaxScrollLines", MaxScrollLines ); ScrollLinesPersistTime = registry->Get_Float( "ScrollLinesPersistTime", ScrollLinesPersistTime ); } delete registry; } void TextDisplayGameModeClass::Save_Registry_Keys(void) { RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS ); WWASSERT( registry ); if ( registry->Is_Valid() ) { registry->Set_Int( "MaxScrollLines", MaxScrollLines ); registry->Set_Float( "ScrollLinesPersistTime", ScrollLinesPersistTime ); } delete registry; } /* ** */ void TextDisplayGameModeClass::Think() { WWPROFILE( "TextDisplay Think" ); if (Font == NULL) { return; } // Update time on all scroll lines SLNode *linenode; for ( linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) { TextDisplayLine *line = linenode->Data(); line->Update_Time( TimeManager::Get_Frame_Seconds() ); } // never have more than 2 over the max number while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= MaxScrollLines + 2) ) { delete ScrollLines.Remove_Head(); } // if there are lines to remove by count or time if ( ScrollLines.Get_Count() && ((ScrollLines.Head()->Data()->Update_Time(0) > ScrollLinesPersistTime) || (Count_Scroll_Lines() > MaxScrollLines) ) ) { // bump scroll position float char_height = Font->Char_Height(); VerticalScroll += TimeManager::Get_Frame_Seconds() * char_height * 4; if ( VerticalScroll >= char_height ) { // top line is hidden, remove it VerticalScroll -= char_height; int lines = Count_Scroll_Lines(); while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= lines) ) { delete ScrollLines.Remove_Head(); } } } // if no more need to be removed, clear scroll if ( ScrollLines.Get_Count() && ((ScrollLines.Head()->Data()->Update_Time(0) > ScrollLinesPersistTime) || (Count_Scroll_Lines() > MaxScrollLines) ) ) { } else { VerticalScroll = 0; } } void TextDisplayGameModeClass::Render() { WWMEMLOG(MEM_GAMEDATA); WWPROFILE( "TextDisplay Render" ); if (Font == NULL) { return; } // Verbose help first if (VerboseTextChanged) { VerboseTextChanged=false; VerboseDisplay->Reset(); VerboseDisplay->Set_Location( Vector2( 0, 0 ) ); VerboseDisplay->Draw_Text( VerboseHelpText, 0xFF00FFFF ); } VerboseDisplay->Render(); // Then statistics StatisticsDisplayManager::Render( StatisticsDisplay ); // And then the rest of the text... bool changed=TextChanged; TextChanged=false; if ( DisplayVisWarning && StatisticsDisplayManager::Is_Current_Display( "fps" ) ) { changed=true; } // never have more than 2 over the max number while ( ScrollLines.Get_Count() && (Count_Scroll_Lines() >= MaxScrollLines + 2) ) { delete ScrollLines.Remove_Head(); changed=true; } // Check if the text lines have changed SLNode *linenode; if (!changed) { int j=0; WWASSERT(RendererLines.Count()==RendererColors.Count()); for ( linenode = ScrollLines.Head(); linenode; linenode = linenode->Next(), j++ ) { if (j>=RendererLines.Count()) { changed=true; break; } if (RendererColors[j]!=linenode->Data()->Color || RendererLines[j]!=linenode->Data()->Text) { changed=true; break; } } } float y=WWMath::Floor( -VerticalScroll ); if (y==0.0f && DisplayY!=0.0f) changed=true; float delta=y-DisplayY; DisplayY=y; if (!changed) { if (delta!=0.0f) Display->Move(Vector2(0.0f,delta)); Display->Render(); return; } // Display Display->Reset(); // Cache the lines that are rendered RendererLines.Reset_Active(); RendererColors.Reset_Active(); for ( linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) { Display->Draw_Text( linenode->Data()->Text, linenode->Data()->Color ); RendererLines.Add(linenode->Data()->Text); RendererColors.Add(linenode->Data()->Color); } Display->Draw_Text( InputText ); Display->Draw_Text( HelpText ); if ( DisplayVisWarning && StatisticsDisplayManager::Is_Current_Display( "fps" ) ) { Vector2 pos = (Render2DClass::Get_Screen_Resolution().Upper_Left() + Render2DClass::Get_Screen_Resolution().Lower_Left()) * 0.5f; Display->Set_Location( pos ); Display->Draw_Text( "Vis Sector Not Found!", 0xFF0000FF ); } Display->Render(); } void TextDisplayGameModeClass::Flush( void ) { while ( ScrollLines.Get_Count() ) { delete ScrollLines.Remove_Head(); } } int TextDisplayGameModeClass::Count_Scroll_Lines( void ) { int count = 0; SLNode *linenode; for ( linenode = ScrollLines.Head(); linenode; linenode = linenode->Next() ) { count += linenode->Data()->Get_Line_Count(); } return count; } /* ** Printing */ void TextDisplayGameModeClass::Print( const char * string, const Vector4 & color ) { if (Font == NULL) { return; } unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|(unsigned(color[3]*255.0f)); WideStringClass wide_string; wide_string.Convert_From( string ); TextDisplayLine * line = new TextDisplayLine( wide_string, col ); ScrollLines.Add_Tail( line ); } void TextDisplayGameModeClass::Print( const WideStringClass & string, const Vector4 & color ) { if (Font == NULL) { return; } unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|(unsigned(color[3]*255.0f)); TextDisplayLine * line = new TextDisplayLine( string, col ); ScrollLines.Add_Tail( line ); } void TextDisplayGameModeClass::Print( const char * string, const Vector3 & color ) { if (Font == NULL) { return; } //TSS unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|0xff; unsigned long col=(unsigned(color[0]*255.0f)<<16)|(unsigned(color[1]*255.0f)<<8)|(unsigned(color[2]*255.0f))|0xFF000000; WideStringClass wide_string; wide_string.Convert_From( string ); TextDisplayLine * line = new TextDisplayLine( wide_string, col ); ScrollLines.Add_Tail( line ); } void TextDisplayGameModeClass::Print( const WideStringClass & string, const Vector3 & color ) { if (Font == NULL) { return; } //TSS unsigned long col=(unsigned(color[0]*255.0f)<<24)|(unsigned(color[1]*255.0f)<<16)|(unsigned(color[2]*255.0f)<<8)|0xff; unsigned long col=(unsigned(color[0]*255.0f)<<16)|(unsigned(color[1]*255.0f)<<8)|(unsigned(color[2]*255.0f))|0xFF000000; TextDisplayLine * line = new TextDisplayLine( string, col ); ScrollLines.Add_Tail( line ); } void TextDisplayGameModeClass::Print_System( const char * format, ... ) { va_list arg_list; va_start (arg_list, format); StringClass string; string.Format_Args( format, arg_list ); va_end (arg_list); Print( string, Vector4( 1,1,0,1 ) ); } void TextDisplayGameModeClass::Print_System( const WideStringClass & string ) { Print( string, Vector4( 1,1,0,1 ) ); } void TextDisplayGameModeClass::Print_Informational( const char * format, ... ) { va_list arg_list; va_start (arg_list, format); StringClass string; string.Format_Args( format, arg_list ); va_end (arg_list); Print( string, Vector4( 0.7f,0.7f,0.7f,1.0f ) ); } void TextDisplayGameModeClass::Print_Informational( const WideStringClass & string ) { Print( string, Vector4( 0.7f,0.7f,0.7f,1.0f ) ); } /* ** TextDebugDisplayHandler */ void TextDebugDisplayHandlerClass::Display_Text( const char * string, const Vector4 & color ) { if (Get_Text_Display() != NULL) { Get_Text_Display()->Print( string, color ); } } void TextDebugDisplayHandlerClass::Display_Text( const WideStringClass & string, const Vector4 & color ) { if (Get_Text_Display() != NULL) { Get_Text_Display()->Print( string, color ); } } /* ** StatisticsDisplay */ static float _StatsScrollOffset = 0; static StringClass _StatsTitle; static StringClass _StatsText; static unsigned long _StatsColor = 0xFFFFFFFF; static Vector2 _StatsLocation( 0,0 ); void StatisticsDisplayManager::Render(Render2DTextClass * renderer) { if (WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC0)) { _StatsScrollOffset -= 32.0f; } if (WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC1)) { _StatsScrollOffset += 32.0f; } renderer->Reset(); renderer->Set_Location( _StatsLocation ); renderer->Draw_Text( _StatsText, _StatsColor ); renderer->Render(); } void StatisticsDisplayManager::Set_Display( const char * title ) { _StatsScrollOffset = 0; _StatsTitle = title; _StatsText = ""; } bool StatisticsDisplayManager::Is_Current_Display( const char* title) // Returns true if "title" is currently active { return !stricmp( _StatsTitle, title ); } void StatisticsDisplayManager::Set_Stat( const char * title, const char * text, unsigned long color, const Vector2& location ) { if ( Is_Current_Display( title ) ) { _StatsText = text; _StatsColor = color; if ( location.Y == -240 ) { // Handle default case Vector2 pos = (Render2DClass::Get_Screen_Resolution().Upper_Left() + Render2DClass::Get_Screen_Resolution().Upper_Right()) * 0.5f; _StatsLocation = pos; } else { _StatsLocation = location; } } }