From 699612efc509097b531fa6ca1bf88dab2b1a68d9 Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:06:38 +0100 Subject: [PATCH] [ZH] Implement system time and simulation timer within InGameUI --- .../GameEngine/Include/Common/GlobalData.h | 4 + .../Include/Common/UserPreferences.h | 3 + .../GameEngine/Include/GameClient/InGameUI.h | 24 ++++ .../GameEngine/Source/Common/GlobalData.cpp | 5 + .../GUI/GUICallbacks/Menus/OptionsMenu.cpp | 48 +++++++ .../GameEngine/Source/GameClient/InGameUI.cpp | 119 ++++++++++++++++++ .../W3DDevice/GameClient/W3DInGameUI.cpp | 2 + 7 files changed, 205 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 0f5f15b5fc..34a375f549 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -409,6 +409,10 @@ class GlobalData : public SubsystemInterface Bool m_saveCameraInReplay; Bool m_useCameraInReplay; + // TheSuperHackers @feature Mauller 21/06/2025 allow the system time and game time font size to be set, a size of zero disables them + Int m_systemTimeFontSize; + Int m_gameTimeFontSize; + Real m_shakeSubtleIntensity; ///< Intensity for shaking a camera with SHAKE_SUBTLE Real m_shakeNormalIntensity; ///< Intensity for shaking a camera with SHAKE_NORMAL Real m_shakeStrongIntensity; ///< Intensity for shaking a camera with SHAKE_STRONG diff --git a/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h b/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h index dfcec0df8c..1748f6ae6f 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/UserPreferences.h @@ -129,6 +129,9 @@ class OptionPreferences : public UserPreferences Int getCampaignDifficulty(void); void setCampaignDifficulty( Int diff ); + + Int getSystemTimeFontSize(void); + Int getGameTimeFontSize(void); }; //----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h index dc011aacc6..308b54f5e9 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h @@ -382,6 +382,7 @@ friend class Drawable; // for selection/deselection transactions virtual void toggleMessages( void ) { m_messagesOn = 1 - m_messagesOn; } ///< toggle messages on/off virtual Bool isMessagesOn( void ) { return m_messagesOn; } ///< are the display messages on void freeMessageResources( void ); ///< free resources for the ui messages + void freeCustomUiResources( void ); ///< free resources for custom ui elements Color getMessageColor(Bool altColor) { return (altColor)?m_messageColor2:m_messageColor1; } // interface for military style messages @@ -466,6 +467,7 @@ friend class Drawable; // for selection/deselection transactions virtual void preDraw( void ); ///< Logic which needs to occur before the UI renders virtual void draw( void ) = 0; ///< Render the in-game user interface virtual void postDraw( void ); ///< Logic which needs to occur after the UI renders + virtual void postWindowDraw( void ); ///< Logic which needs to occur after the WindowManager has repainted the menus /// Ingame video playback virtual void playMovie( const AsciiString& movieName ); @@ -578,6 +580,9 @@ friend class Drawable; // for selection/deselection transactions virtual void updateIdleWorker( void ); virtual void resetIdleWorker( void ); + void drawSystemTime(); + void drawGameTime(); + public: void registerWindowLayout(WindowLayout *layout); // register a layout for updates void unregisterWindowLayout(WindowLayout *layout); // stop updates for this layout @@ -741,6 +746,25 @@ friend class Drawable; // for selection/deselection transactions VideoBuffer* m_cameoVideoBuffer;///< video playback buffer VideoStreamInterface* m_cameoVideoStream;///< Video stream; + // System Time + DisplayString * m_systemTimeString; + AsciiString m_systemTimeFont; + Int m_systemTimePointSize; + Bool m_systemTimeBold; + Coord2D m_systemTimePosition; + Color m_systemTimeColor; + Color m_systemTimeDropColor; + + // Game Time + DisplayString * m_gameTimeString; + DisplayString * m_gameTimeFrameString; + AsciiString m_gameTimeFont; + Int m_gameTimePointSize; + Bool m_gameTimeBold; + Coord2D m_gameTimePosition; + Color m_gameTimeColor; + Color m_gameTimeDropColor; + // message data UIMessage m_uiMessages[ MAX_UI_MESSAGES ];/**< messages to display to the user, the array is organized with newer messages at diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index 4ef49c0e36..fcf798a063 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -928,6 +928,8 @@ GlobalData::GlobalData() m_saveCameraInReplay = FALSE; m_useCameraInReplay = FALSE; + m_systemTimeFontSize = 8; + m_gameTimeFontSize = 8; m_debugShowGraphicalFramerate = FALSE; @@ -1196,6 +1198,9 @@ void GlobalData::parseGameDataDefinition( INI* ini ) TheWritableGlobalData->m_saveCameraInReplay = optionPref.saveCameraInReplays(); TheWritableGlobalData->m_useCameraInReplay = optionPref.useCameraInReplays(); + + TheWritableGlobalData->m_systemTimeFontSize = optionPref.getSystemTimeFontSize(); + TheWritableGlobalData->m_gameTimeFontSize = optionPref.getGameTimeFontSize(); Int val=optionPref.getGammaValue(); //generate a value between 0.6 and 2.0. diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp index b5c2ab8d6f..1a13ac36c9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp @@ -772,6 +772,34 @@ Real OptionPreferences::getMusicVolume(void) return volume; } +Int OptionPreferences::getSystemTimeFontSize(void) +{ + OptionPreferences::const_iterator it = find("SystemTimeFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + +Int OptionPreferences::getGameTimeFontSize(void) +{ + OptionPreferences::const_iterator it = find("GameTimeFontSize"); + if (it == end()) + return 8; + + Int fontSize = atoi(it->second.str()); + if (fontSize < 0) + { + fontSize = 0; + } + return fontSize; +} + static OptionPreferences *pref = NULL; static void setDefaults( void ) @@ -1262,6 +1290,26 @@ static void saveOptions( void ) } } + //------------------------------------------------------------------------------------------------- + // Set System Time Font Size + val = TheWritableGlobalData->m_systemTimeFontSize; // TheSuperHackers @todo replace with options input when applicable + if (val) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["SystemTimeFontSize"] = prefString; + } + + //------------------------------------------------------------------------------------------------- + // Set Game Time Font Size + val = TheWritableGlobalData->m_gameTimeFontSize; // TheSuperHackers @todo replace with options input when applicable + if (val) + { + AsciiString prefString; + prefString.format("%d", val); + (*pref)["GameTimeFontSize"] = prefString; + } + //------------------------------------------------------------------------------------------------- // Resolution // diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index e540c5ab11..b514bab49d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -876,6 +876,19 @@ const FieldParse InGameUI::s_fieldParseTable[] = { "ClearMinesRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_CLEARMINES] ) }, { "AmbulanceRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_AMBULANCE] ) }, + // TheSuperHackers @info ui enhancement configuration + { "SystemTimeFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_systemTimeFont ) }, + { "SystemTimeBold", INI::parseBool, NULL, offsetof( InGameUI, m_systemTimeBold ) }, + { "SystemTimePosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_systemTimePosition ) }, + { "SystemTimeColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_systemTimeColor ) }, + { "SystemTimeDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_systemTimeDropColor ) }, + + { "GameTimeFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_gameTimeFont ) }, + { "GameTimeBold", INI::parseBool, NULL, offsetof( InGameUI, m_gameTimeBold ) }, + { "GameTimePosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_gameTimePosition ) }, + { "GameTimeColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_gameTimeColor ) }, + { "GameTimeDropColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_gameTimeDropColor ) }, + { NULL, NULL, NULL, 0 } // keep this last }; @@ -1001,6 +1014,25 @@ InGameUI::InGameUI() m_replayWindow = NULL; m_messagesOn = TRUE; + m_systemTimeString = NULL; + m_systemTimeFont = "Tahoma"; + m_systemTimePointSize = TheGlobalData->m_systemTimeFontSize; + m_systemTimeBold = TRUE; + m_systemTimePosition.x = 3; // TheSuperHackers @info relative to the left of the screen + m_systemTimePosition.y = -1; + m_systemTimeColor = GameMakeColor( 255, 255, 255, 255 ); + m_systemTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); + + m_gameTimeString = NULL; + m_gameTimeFrameString = NULL; + m_gameTimeFont = "Tahoma"; + m_gameTimePointSize = TheGlobalData->m_gameTimeFontSize; + m_gameTimeBold = TRUE; + m_gameTimePosition.x = 3; // TheSuperHackers @info relative to the right of the screen + m_gameTimePosition.y = -1; + m_gameTimeColor = GameMakeColor( 255, 255, 255, 255 ); + m_gameTimeDropColor = GameMakeColor( 0, 0, 0, 255 ); + m_superweaponPosition.x = 0.7f; m_superweaponPosition.y = 0.7f; m_superweaponFlashDuration = 1.0f; @@ -1082,6 +1114,9 @@ InGameUI::~InGameUI() // delete the message resources freeMessageResources(); + // free custom ui strings + freeCustomUiResources(); + // delete the array for the drawbles delete [] m_placeIcon; m_placeIcon = NULL; @@ -1924,6 +1959,9 @@ void InGameUI::reset( void ) // free any message resources allocated freeMessageResources(); + // free custom ui strings + freeCustomUiResources(); + Int i; for (i=0; ifreeDisplayString(m_systemTimeString); + m_systemTimeString = NULL; + TheDisplayStringManager->freeDisplayString(m_gameTimeString); + m_gameTimeString = NULL; + TheDisplayStringManager->freeDisplayString(m_gameTimeFrameString); + m_gameTimeFrameString = NULL; +} + //------------------------------------------------------------------------------------------------- /** Same as the unicode message method, but this takes an ascii string which is assumed * to me a string manager label */ @@ -3459,6 +3507,22 @@ void InGameUI::disregardDrawable( Drawable *draw ) } +//------------------------------------------------------------------------------------------------- +/** This is called after the WindowManager has drawn the menus. */ +//------------------------------------------------------------------------------------------------- +void InGameUI::postWindowDraw( void ) +{ + if (m_systemTimePointSize > 0) + { + drawSystemTime(); + } + + if ( (m_gameTimePointSize > 0) && !TheGameLogic->isInShellGame() && TheGameLogic->isInGame() ) + { + drawGameTime(); + } +} + //------------------------------------------------------------------------------------------------- /** This is called after the UI has been drawn. */ //------------------------------------------------------------------------------------------------- @@ -5718,4 +5782,59 @@ WindowMsgHandledType IdleWorkerSystem( GameWindow *window, UnsignedInt msg, } +void InGameUI::drawSystemTime() +{ + if (!m_systemTimeString) { + m_systemTimeString = TheDisplayStringManager->newDisplayString(); + } + + // current system time + SYSTEMTIME systemTime; + GetLocalTime( &systemTime ); + + UnicodeString TimeString; + TimeString.format(L"%2.2d:%2.2d:%2.2d", systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + Int adjustedSystemTimeFontSize = TheGlobalLanguageData->adjustFontSize(m_systemTimePointSize); + GameFont* systemTimeFont = TheWindowManager->winFindFont(m_systemTimeFont, adjustedSystemTimeFontSize, m_systemTimeBold); + m_systemTimeString->setFont(systemTimeFont); + m_systemTimeString->setText(TimeString); + + m_systemTimeString->draw(m_systemTimePosition.x, m_systemTimePosition.y, m_systemTimeColor, m_systemTimeDropColor); +} + +void InGameUI::drawGameTime() +{ + if (!m_gameTimeString) { + m_gameTimeString = TheDisplayStringManager->newDisplayString(); + } + + if (!m_gameTimeFrameString) { + m_gameTimeFrameString = TheDisplayStringManager->newDisplayString(); + } + + Int currentFrame = TheGameLogic->getFrame(); + Int gameSeconds = (Int) (SECONDS_PER_LOGICFRAME_REAL * currentFrame ); + Int hours = gameSeconds / 60 / 60; + Int minutes = (gameSeconds / 60) % 60; + Int seconds = gameSeconds % 60; + Int frame = currentFrame % 30; + UnicodeString gameTimeString; + gameTimeString.format(L"%2.2d:%2.2d:%2.2d", hours, minutes, seconds); + Int adjustedGameTimeFontSize = TheGlobalLanguageData->adjustFontSize(m_gameTimePointSize); + GameFont* gameTimeFont = TheWindowManager->winFindFont(m_gameTimeFont, adjustedGameTimeFontSize, m_gameTimeBold); + m_gameTimeString->setFont(gameTimeFont); + m_gameTimeString->setText(gameTimeString); + + UnicodeString gameTimeFrameString; + gameTimeFrameString.format(L".%2.2d", frame); + m_gameTimeFrameString->setFont(gameTimeFont); + m_gameTimeFrameString->setText(gameTimeFrameString); + + // TheSuperHackers @info this implicitly offsets the game timer from the right instead of left of the screen + int horizontalTimerOffset = TheDisplay->getWidth() - (Int)m_gameTimePosition.x - m_gameTimeString->getWidth() - m_gameTimeFrameString->getWidth(); + int horizontalFrameOffset = TheDisplay->getWidth() - (Int)m_gameTimePosition.x - m_gameTimeFrameString->getWidth(); + + m_gameTimeString->draw(horizontalTimerOffset, m_gameTimePosition.y, m_gameTimeColor, m_gameTimeDropColor); + m_gameTimeFrameString->draw(horizontalFrameOffset, m_gameTimePosition.y, GameMakeColor(180,180,180,255), m_gameTimeDropColor); +} diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp index 62f6f1ddd7..dcc1a3f351 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DInGameUI.cpp @@ -437,6 +437,8 @@ void W3DInGameUI::draw( void ) postDraw(); TheWindowManager->winRepaint(); + + postWindowDraw(); #ifdef EXTENDED_STATS }