diff --git a/Core/GameEngine/Include/Common/OptionPreferences.h b/Core/GameEngine/Include/Common/OptionPreferences.h index 85aba4228be..d18450c1c79 100644 --- a/Core/GameEngine/Include/Common/OptionPreferences.h +++ b/Core/GameEngine/Include/Common/OptionPreferences.h @@ -38,6 +38,18 @@ typedef UnsignedInt CursorCaptureMode; typedef UnsignedInt ScreenEdgeScrollMode; +// TheSuperHackers @feature How remaining time is shown on build queue and cooldown cameos. +// Purely a client side display preference. +enum BuildTimerDisplayMode CPP_11(: Int) +{ + BuildTimerDisplayMode_None = 0, ///< no numbers, just the existing clock sweep (retail behavior) + BuildTimerDisplayMode_Seconds, ///< always plain seconds, however large + BuildTimerDisplayMode_Auto, ///< seconds under a minute, M:SS above it + + BuildTimerDisplayMode_Count, + BuildTimerDisplayMode_Default = BuildTimerDisplayMode_None +}; + //----------------------------------------------------------------------------- // OptionsPreferences options menu class //----------------------------------------------------------------------------- @@ -76,6 +88,7 @@ class OptionPreferences : public UserPreferences Real getScrollFactor(); Bool getDrawScrollAnchor(); Bool getMoveScrollAnchor(); + BuildTimerDisplayMode getBuildTimerDisplayMode() const; Bool getCursorCaptureEnabledInWindowedGame() const; Bool getCursorCaptureEnabledInWindowedMenu() const; Bool getCursorCaptureEnabledInFullscreenGame() const; diff --git a/Core/GameEngine/Include/GameClient/Gadget.h b/Core/GameEngine/Include/GameClient/Gadget.h index 30c9d45206c..1851c041826 100644 --- a/Core/GameEngine/Include/GameClient/Gadget.h +++ b/Core/GameEngine/Include/GameClient/Gadget.h @@ -431,6 +431,10 @@ typedef struct _PushButtonData void *userData; ///< random additional data we can set const Image *overlayImage; ///< An overlay image (like a veterancy symbol) AsciiString altSound; ///< use an alternative sound if one is set + // TheSuperHackers @feature Remaining time drawn over the cameo, in seconds. Negative means + // nothing to show. Like drawClock this is one shot -- it is cleared after being drawn, so + // whoever sets it must re-set it every frame. + Int countdownSeconds; } PushButtonData; // TabControlData ------------------------------------------------------------ diff --git a/Core/GameEngine/Include/GameClient/GadgetPushButton.h b/Core/GameEngine/Include/GameClient/GadgetPushButton.h index ced1e0c3013..5a2893e329f 100644 --- a/Core/GameEngine/Include/GameClient/GadgetPushButton.h +++ b/Core/GameEngine/Include/GameClient/GadgetPushButton.h @@ -76,6 +76,8 @@ void GadgetButtonEnableCheckLike( GameWindow *g, Bool makeCheckLike, Bool initia void GadgetButtonSetText( GameWindow *g, UnicodeString text ); void GadgetButtonDrawClock( GameWindow *g, Int percent, Color color ); //Darkens the progress void GadgetButtonDrawInverseClock( GameWindow *g, Int percent, Color color ); //Darkens the remaining portion. +// TheSuperHackers @feature Remaining time in seconds, drawn over the cameo. One shot, like the clocks. +void GadgetButtonDrawCountdown( GameWindow *g, Int seconds ); void GadgetButtonDrawOverlayImage( GameWindow *g, const Image *image ); void GadgetButtonSetBorder( GameWindow *g, Color color, Bool drawBorder = TRUE ); void GadgetButtonSetData(GameWindow *g, void *data); diff --git a/Core/GameEngine/Source/Common/OptionPreferences.cpp b/Core/GameEngine/Source/Common/OptionPreferences.cpp index e681ef8b192..39590f40df0 100644 --- a/Core/GameEngine/Source/Common/OptionPreferences.cpp +++ b/Core/GameEngine/Source/Common/OptionPreferences.cpp @@ -216,6 +216,28 @@ Bool OptionPreferences::getRightMouseScrollWithAlternateMouseEnabled() const return FALSE; } +// TheSuperHackers @feature Countdown numbers on build queue and cooldown cameos, read from +// Options.ini as BuildTimerDisplayMode = None | Seconds | Auto (a plain index also works). +BuildTimerDisplayMode OptionPreferences::getBuildTimerDisplayMode() const +{ + OptionPreferences::const_iterator it = find("BuildTimerDisplayMode"); + if (it == end()) + return BuildTimerDisplayMode_Default; + + if (stricmp(it->second.str(), "Auto") == 0) + return BuildTimerDisplayMode_Auto; + if (stricmp(it->second.str(), "Seconds") == 0) + return BuildTimerDisplayMode_Seconds; + if (stricmp(it->second.str(), "None") == 0) + return BuildTimerDisplayMode_None; + + Int mode = atoi(it->second.str()); + if (mode >= 0 && mode < BuildTimerDisplayMode_Count) + return (BuildTimerDisplayMode)mode; + + return BuildTimerDisplayMode_Default; +} + Bool OptionPreferences::getRetaliationModeEnabled() { OptionPreferences::const_iterator it = find("Retaliation"); diff --git a/Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp b/Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp index 5a9099af8a9..b73bd7df448 100644 --- a/Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommand.cpp @@ -40,6 +40,7 @@ #include "Common/Upgrade.h" #include "Common/BuildAssistant.h" #include "GameLogic/GameLogic.h" +#include "Common/OptionPreferences.h" #include "GameLogic/Module/BattlePlanUpdate.h" #include "GameLogic/Module/DozerAIUpdate.h" #include "GameLogic/Module/OverchargeBehavior.h" @@ -794,6 +795,30 @@ void ControlBar::updateContextCommand() GadgetButtonDrawInverseClock(win,produce->getPercentComplete(), m_buildUpClockColor); + // TheSuperHackers @feature Remaining build time on the head queue slot. + if( TheGlobalData->m_buildTimerDisplayMode != BuildTimerDisplayMode_None ) + { + Int totalFrames = 0; + if( produce->getProductionType() == PRODUCTION_UNIT ) + { + if( produce->getProductionObject() ) + totalFrames = produce->getProductionObject()->calcTimeToBuild( obj->getControllingPlayer() ); + } + else if( produce->getProductionUpgrade() ) + { + totalFrames = produce->getProductionUpgrade()->calcTimeToBuild( obj->getControllingPlayer() ); + } + + if( totalFrames > 0 ) + { + Real remainingReal = totalFrames * (100.0f - produce->getPercentComplete()) / 100.0f; + Int remainingFrames = ( remainingReal > 0.0f ) ? REAL_TO_INT_CEIL( remainingReal ) : 0; + // integer ceiling -- see formatBuildTimeForTooltip for why not the float form + GadgetButtonDrawCountdown( win, + ( remainingFrames + LOGICFRAMES_PER_SECOND - 1 ) / LOGICFRAMES_PER_SECOND ); + } + } + } } @@ -1418,6 +1443,19 @@ CommandAvailability ControlBar::getCommandAvailability( const CommandButton *com Int percent = mod->getPercentReady() * 100; GadgetButtonDrawInverseClock( applyToWin, percent, m_buildUpClockColor ); + + // TheSuperHackers @feature Remaining recharge time. getReadyFrame is an absolute + // frame, and already accounts for paused and shared/synced powers. + if( TheGlobalData->m_buildTimerDisplayMode != BuildTimerDisplayMode_None ) + { + UnsignedInt now = TheGameLogic->getFrame(); + UnsignedInt readyFrame = mod->getReadyFrame(); + UnsignedInt remainingFrames = ( readyFrame > now ) ? ( readyFrame - now ) : 0; + // integer ceiling -- see formatBuildTimeForTooltip for why not the float form + GadgetButtonDrawCountdown( applyToWin, + ( remainingFrames + LOGICFRAMES_PER_SECOND - 1 ) / LOGICFRAMES_PER_SECOND ); + } + return COMMAND_NOT_READY; } else if( SpecialAbilityUpdate *spUpdate = obj->findSpecialAbilityUpdate( command->getSpecialPowerTemplate()->getSpecialPowerType() ) ) diff --git a/Core/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp b/Core/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp index 35d9a3e80d8..0c76e61e174 100644 --- a/Core/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/GUICallbacks/ControlBarPopupDescription.cpp @@ -68,6 +68,7 @@ #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine #include "Common/GlobalData.h" +#include "Common/OptionPreferences.h" #include "Common/BuildAssistant.h" #include "Common/Player.h" #include "Common/PlayerList.h" @@ -104,6 +105,20 @@ void ControlBarPopupDescriptionUpdateFunc( WindowLayout *layout, void *param ) if(TheScriptEngine->isGameEnding()) TheControlBar->hideBuildTooltipLayout(); + // TheSuperHackers @fix The unit build time follows the player's power state, but nothing + // marks the control bar dirty when power changes - so an open tooltip kept showing the + // old time. Refresh it about once a second while it is up; with the timer display off + // the tooltip carries no time and stays as cheap as retail. + if( TheGlobalData && TheGlobalData->m_buildTimerDisplayMode != BuildTimerDisplayMode_None ) + { + static UnsignedInt s_framesSinceRefresh = 0; + if( ++s_framesSinceRefresh >= LOGICFRAMES_PER_SECOND ) + { + s_framesSinceRefresh = 0; + TheControlBar->repopulateBuildTooltipLayout(); + } + } + if(theAnimateWindowManager && !TheControlBar->getShowBuildTooltipLayout() && !theAnimateWindowManager->isReversed()) theAnimateWindowManager->reverseAnimateWindow(); else if(!TheControlBar->getShowBuildTooltipLayout() && (!TheGlobalData->m_animateWindows || !useAnimation)) @@ -231,6 +246,39 @@ void ControlBar::showBuildTooltipLayout( GameWindow *cmdButton ) } +// TheSuperHackers @feature Format a build time for the tooltip, using the same Auto/Seconds +// rules as the cameo countdowns (Options.ini: BuildTimerDisplayMode). +// +// Note the value passed in should come from calcTimeToBuild(), which for units already folds +// in the player's current energy penalty -- so a tooltip read while on low power correctly +// shows the slower time, and speeds back up once power is restored. Upgrades have no energy +// penalty in the engine, so their time is the same regardless of power state. +static UnicodeString formatBuildTimeForTooltip( Int buildFrames ) +{ + UnicodeString result = UnicodeString::TheEmptyString; + + if( buildFrames <= 0 ) + return result; + + if( TheGlobalData == nullptr || + TheGlobalData->m_buildTimerDisplayMode == BuildTimerDisplayMode_None ) + return result; + + // Round to nearest, so a template built from a fractional BuildTime reads as the number + // the modder typed rather than always the one above it. Integer math throughout, because + // 1/30 is not exact in float and an exact 8 second build would otherwise come out as + // 8.0000004. The live countdowns deliberately still ceil -- a running timer must not + // show 0 while there is work left. + const Int seconds = ( buildFrames + LOGICFRAMES_PER_SECOND / 2 ) / LOGICFRAMES_PER_SECOND; + + if( TheGlobalData->m_buildTimerDisplayMode == BuildTimerDisplayMode_Auto && seconds >= 60 ) + result.format( L"%d:%2.2d", seconds / 60, seconds % 60 ); // already reads as time + else + result.format( L"%ds", seconds ); + + return result; +} + void ControlBar::repopulateBuildTooltipLayout() { if(!prevWindow || !m_buildToolTipLayout) @@ -248,6 +296,8 @@ void ControlBar::populateBuildTooltipLayout( const CommandButton *commandButton, Player *player = ThePlayerList->getLocalPlayer(); UnicodeString name, cost, descrip; + // TheSuperHackers @feature Build time shown under the cost, formatted like the cameo timers. + UnicodeString buildTimeText; UnicodeString requiresFormat = UnicodeString::TheEmptyString, requiresList; Bool firstRequirement = true; const ProductionPrerequisite *prereq; @@ -415,6 +465,10 @@ void ControlBar::populateBuildTooltipLayout( const CommandButton *commandButton, cost.format( TheGameText->fetch("TOOLTIP:Cost"), costToBuild ); } + // TheSuperHackers @feature calcTimeToBuild folds in the current energy penalty, + // so this tracks the player's power state as it changes. + buildTimeText = formatBuildTimeForTooltip( thingTemplate->calcTimeToBuild( player ) ); + // ask each prerequisite to give us a list of the non satisfied prerequisites for( Int i=0; igetPrereqCount(); i++ ) { @@ -512,6 +566,10 @@ void ControlBar::populateBuildTooltipLayout( const CommandButton *commandButton, cost.format( TheGameText->fetch("TOOLTIP:Cost"), costToBuild ); } + // TheSuperHackers @feature Upgrades carry no energy penalty in the engine, so + // unlike units this time does not change with the player's power state. + buildTimeText = formatBuildTimeForTooltip( upgradeTemplate->calcTimeToBuild( player ) ); + if( missingScience ) { if( !descrip.isEmpty() ) @@ -607,10 +665,24 @@ void ControlBar::populateBuildTooltipLayout( const CommandButton *commandButton, win = TheWindowManager->winGetWindowFromId(m_buildToolTipLayout->getFirstWindow(), TheNameKeyGenerator->nameToKey("ControlBarPopupDescription.wnd:StaticTextCost")); if(win) { - if( costToBuild > 0 ) + // TheSuperHackers @feature Show the build time alongside the cost. Also shown for + // free items, which have no cost line of their own but still take time to build. + // Kept on the cost line, separated by a watch glyph. The description window sits + // directly beneath this one in the .wnd layout, so a second line here overlaps it. + UnicodeString costLine = cost; + if( !buildTimeText.isEmpty() ) + { + if( !costLine.isEmpty() ) + costLine.concat( L" " ); + costLine.concat( (WideChar)0x231A ); // WATCH + costLine.concat( L" " ); + costLine.concat( buildTimeText ); + } + + if( !costLine.isEmpty() ) { win->winHide( FALSE ); - GadgetStaticTextSetText(win, cost); + GadgetStaticTextSetText(win, costLine); } else { diff --git a/Core/GameEngine/Source/GameClient/GUI/Gadget/GadgetPushButton.cpp b/Core/GameEngine/Source/GameClient/GUI/Gadget/GadgetPushButton.cpp index 8818806ed48..810cb13ed9e 100644 --- a/Core/GameEngine/Source/GameClient/GUI/Gadget/GadgetPushButton.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/Gadget/GadgetPushButton.cpp @@ -600,10 +600,34 @@ PushButtonData * getNewPushButtonData() p->userData = nullptr; p->drawBorder = FALSE; p->drawClock = NO_CLOCK; + p->countdownSeconds = -1; p->overlayImage = nullptr; return p; } +// GadgetButtonSetBorder ====================================================== +/** Set to draw the special borders in the game */ +//============================================================================= +// GadgetButtonDrawCountdown ================================================== +/** TheSuperHackers @feature Show remaining time in seconds over the button. Like the clock + * above this is one shot, so it must be re-set every frame while the timer is running. */ +//============================================================================= +void GadgetButtonDrawCountdown( GameWindow *g, Int seconds ) +{ + + if( g == nullptr ) + return; + + PushButtonData *pData = (PushButtonData *)g->winGetUserData(); + if(!pData) + { + pData = getNewPushButtonData(); + } + pData->countdownSeconds = seconds; + g->winSetUserData(pData); + +} + // GadgetButtonSetBorder ====================================================== /** Set to draw the special borders in the game */ //============================================================================= diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DGadget.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DGadget.h index 5979a8a5f4f..eea23a9a517 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DGadget.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DGadget.h @@ -65,6 +65,9 @@ /////////////////////////////////////////////////////////////////////////////// extern void W3DGadgetPushButtonDraw( GameWindow *window, WinInstanceData *instData ); +// TheSuperHackers @feature Free the countdown text's display string; must run before +// TheDisplayStringManager is destroyed. +extern void W3DGadgetPushButtonFreeCountdownString( void ); extern void W3DGadgetPushButtonImageDraw( GameWindow *window, WinInstanceData *instData ); extern void W3DGadgetCheckBoxDraw( GameWindow *window, WinInstanceData *instData ); extern void W3DGadgetCheckBoxImageDraw( GameWindow *window, WinInstanceData *instData ); diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp index 2b89eb1e564..8e4f353366b 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp @@ -53,6 +53,12 @@ #include "GameClient/GameWindowManager.h" #include "GameClient/GadgetPushButton.h" #include "GameClient/Display.h" +// TheSuperHackers @feature for the command bar countdown text +#include "Common/GlobalData.h" +#include "Common/OptionPreferences.h" +#include "GameClient/DisplayStringManager.h" +#include "GameClient/GameFont.h" +#include "GameClient/GlobalLanguage.h" #include "W3DDevice/GameClient/W3DGameWindow.h" #include "W3DDevice/GameClient/W3DDisplay.h" #include "W3DDevice/GameClient/W3DGadget.h" @@ -78,6 +84,91 @@ void W3DGadgetPushButtonImageDrawOne(GameWindow *window, WinInstanceData *instDa // drawButtonText ============================================================= /** Draw button text to the screen */ //============================================================================= +// TheSuperHackers @feature Countdown text over a cameo (Options.ini: BuildTimerDisplayMode). +// One shared string, rebuilt only when the displayed value changes -- at one tick per second +// that is far less often than we are drawn. Returned to the manager by +// W3DGadgetPushButtonFreeCountdownString before the manager is torn down. +static DisplayString *s_countdownString = nullptr; +static Int s_countdownLastSeconds = -1; +static Int s_countdownLastMode = -1; + +//============================================================================= +/** Return the countdown's display string to the manager. Must run before + * TheDisplayStringManager is destroyed, which asserts on any string still + * registered -- the W3DDisplayStringManager destructor calls this. */ +//============================================================================= +void W3DGadgetPushButtonFreeCountdownString( void ) +{ + if( s_countdownString != nullptr && TheDisplayStringManager != nullptr ) + TheDisplayStringManager->freeDisplayString( s_countdownString ); + + s_countdownString = nullptr; + s_countdownLastSeconds = -1; + s_countdownLastMode = -1; +} + +// drawButtonCountdown ======================================================== +/** Draw the remaining time for whatever this button is counting down -- a queued unit or + * upgrade, or a special power recharging. Drawn after the clock sweep so the darkening + * does not swallow the digits. */ +//============================================================================= +static void drawButtonCountdown( GameWindow *window, Int seconds ) +{ + if( seconds < 0 ) + return; + + if( !TheGlobalData || TheGlobalData->m_buildTimerDisplayMode == BuildTimerDisplayMode_None ) + return; + + if( TheDisplayStringManager == nullptr ) + return; + + if( s_countdownString == nullptr ) + { + s_countdownString = TheDisplayStringManager->newDisplayString(); + if( s_countdownString == nullptr ) + return; + + Int pointSize = 10; + if( TheGlobalLanguageData ) + pointSize = TheGlobalLanguageData->adjustFontSize( pointSize ); + s_countdownString->setFont( TheFontLibrary->getFont( AsciiString( "Arial" ), pointSize, TRUE ) ); + } + + // only rebuild the sentence when the displayed value actually changes, which at one + // tick per second is far less often than we are drawn + if( s_countdownLastSeconds != seconds || s_countdownLastMode != TheGlobalData->m_buildTimerDisplayMode ) + { + UnicodeString text; + if( TheGlobalData->m_buildTimerDisplayMode == BuildTimerDisplayMode_Auto && seconds >= 60 ) + text.format( L"%d:%2.2d", seconds / 60, seconds % 60 ); + else + text.format( L"%d", seconds ); + + s_countdownString->setText( text ); + s_countdownLastSeconds = seconds; + s_countdownLastMode = TheGlobalData->m_buildTimerDisplayMode; + } + + ICoord2D origin, size; + window->winGetScreenPosition( &origin.x, &origin.y ); + window->winGetSize( &size.x, &size.y ); + + Int width, height; + s_countdownString->getSize( &width, &height ); + + // centered along the bottom, clear of the top left corner decorations + const Int pad = 1; + const Int textX = origin.x + (size.x / 2) - (width / 2); + const Int textY = origin.y + size.y - height - 2; + + TheDisplay->drawFillRect( textX - pad, textY - pad, + width + pad * 2, height + pad * 2, GameMakeColor( 0, 0, 0, 128 ) ); + + s_countdownString->draw( textX, textY, + GameMakeColor( 255, 255, 255, 255 ), GameMakeColor( 0, 0, 0, 255 ) ); +} + static void drawButtonText( GameWindow *window, WinInstanceData *instData ) { ICoord2D origin, size, textPos; @@ -434,6 +525,15 @@ void W3DGadgetPushButtonImageDrawOne( GameWindow *window, window->winSetUserData(pData); } + // TheSuperHackers @feature Countdown text, after the clock so the darkening does not + // swallow the digits. One shot, like the clock above. + if( pData->countdownSeconds >= 0 ) + { + drawButtonCountdown( window, pData->countdownSeconds ); + pData->countdownSeconds = -1; + window->winSetUserData(pData); + } + if( pData->drawBorder && pData->colorBorder != GAME_COLOR_UNDEFINED ) { @@ -693,6 +793,15 @@ void W3DGadgetPushButtonImageDrawThree(GameWindow *window, WinInstanceData *inst window->winSetUserData(pData); } + // TheSuperHackers @feature Countdown text, after the clock so the darkening does not + // swallow the digits. One shot, like the clock above. + if( pData->countdownSeconds >= 0 ) + { + drawButtonCountdown( window, pData->countdownSeconds ); + pData->countdownSeconds = -1; + window->winSetUserData(pData); + } + if( pData->drawBorder && pData->colorBorder != GAME_COLOR_UNDEFINED ) { TheDisplay->drawOpenRect(start.x - 1, start.y - 1, size.x + 2, size.y + 2, 1, pData->colorBorder); diff --git a/Generals/Code/GameEngine/Include/Common/GlobalData.h b/Generals/Code/GameEngine/Include/Common/GlobalData.h index 0c8e8820660..cacd1152fb8 100644 --- a/Generals/Code/GameEngine/Include/Common/GlobalData.h +++ b/Generals/Code/GameEngine/Include/Common/GlobalData.h @@ -141,6 +141,9 @@ class GlobalData : public SubsystemInterface Bool m_useAlternateMouse; Bool m_useRightMouseScrollWithAlternateMouse; // TheSuperHackers @feature User option for RMB scroll in Alternate Mouse mode. Bool m_clientRetaliationModeEnabled; + // TheSuperHackers @feature Countdown numbers on build queue and cooldown cameos. + // Holds a BuildTimerDisplayMode; stored as Int to avoid pulling OptionPreferences.h in here. + Int m_buildTimerDisplayMode; Bool m_doubleClickAttackMove; Bool m_rightMouseAlwaysScrolls; Int m_jpegQuality; // TheSuperHackers @feature Quality for JPEG screenshots. diff --git a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp index f7720c351a2..5f100d40032 100644 --- a/Generals/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/Generals/Code/GameEngine/Source/Common/GlobalData.cpp @@ -1047,6 +1047,7 @@ GlobalData::GlobalData() m_useRightMouseScrollWithAlternateMouse = TRUE; #endif m_clientRetaliationModeEnabled = TRUE; //On by default. + m_buildTimerDisplayMode = BuildTimerDisplayMode_Default; m_doubleClickAttackMove = FALSE; } @@ -1199,6 +1200,7 @@ void GlobalData::parseGameDataDefinition( INI* ini ) TheWritableGlobalData->m_useRightMouseScrollWithAlternateMouse = optionPref.getRightMouseScrollWithAlternateMouseEnabled(); TheWritableGlobalData->m_clientRetaliationModeEnabled = optionPref.getRetaliationModeEnabled(); TheWritableGlobalData->m_doubleClickAttackMove = optionPref.getDoubleClickAttackMoveEnabled(); + TheWritableGlobalData->m_buildTimerDisplayMode = optionPref.getBuildTimerDisplayMode(); TheWritableGlobalData->m_jpegQuality = optionPref.getJpegQuality(); TheWritableGlobalData->m_keyboardScrollFactor = optionPref.getScrollFactor(); TheWritableGlobalData->m_drawScrollAnchor = optionPref.getDrawScrollAnchor(); diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp index d67d6282d30..6c22b364b9f 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp @@ -36,6 +36,8 @@ #include "GameClient/DrawGroupInfo.h" #include "GameClient/GlobalLanguage.h" #include "W3DDevice/GameClient/W3DDisplayStringManager.h" +// TheSuperHackers @feature for the countdown string teardown +#include "W3DDevice/GameClient/W3DGadget.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS @@ -56,6 +58,11 @@ W3DDisplayStringManager::W3DDisplayStringManager() //------------------------------------------------------------------------------------------------- W3DDisplayStringManager::~W3DDisplayStringManager() { + // TheSuperHackers @feature The countdown text's shared string must come back to the + // manager while it is still alive; the base class destructor asserts on any string + // left registered. + W3DGadgetPushButtonFreeCountdownString(); + for (Int i = 0; i < MAX_GROUPS; ++i) { if (m_groupNumeralStrings[i]) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 89a5fa08f9d..a709cb31204 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -142,6 +142,9 @@ class GlobalData : public SubsystemInterface Bool m_useAlternateMouse; Bool m_useRightMouseScrollWithAlternateMouse; // TheSuperHackers @feature User option for RMB scroll in Alternate Mouse mode. Bool m_clientRetaliationModeEnabled; + // TheSuperHackers @feature Countdown numbers on build queue and cooldown cameos. + // Holds a BuildTimerDisplayMode; stored as Int to avoid pulling OptionPreferences.h in here. + Int m_buildTimerDisplayMode; Bool m_doubleClickAttackMove; Bool m_rightMouseAlwaysScrolls; Int m_jpegQuality; // TheSuperHackers @feature Quality for JPEG screenshots. diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index e862cd149d5..33ccb617aca 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -1062,6 +1062,7 @@ GlobalData::GlobalData() m_useRightMouseScrollWithAlternateMouse = TRUE; #endif m_clientRetaliationModeEnabled = TRUE; //On by default. + m_buildTimerDisplayMode = BuildTimerDisplayMode_Default; m_doubleClickAttackMove = FALSE; } @@ -1206,6 +1207,7 @@ void GlobalData::parseGameDataDefinition( INI* ini ) TheWritableGlobalData->m_useRightMouseScrollWithAlternateMouse = optionPref.getRightMouseScrollWithAlternateMouseEnabled(); TheWritableGlobalData->m_clientRetaliationModeEnabled = optionPref.getRetaliationModeEnabled(); TheWritableGlobalData->m_doubleClickAttackMove = optionPref.getDoubleClickAttackMoveEnabled(); + TheWritableGlobalData->m_buildTimerDisplayMode = optionPref.getBuildTimerDisplayMode(); TheWritableGlobalData->m_jpegQuality = optionPref.getJpegQuality(); TheWritableGlobalData->m_keyboardScrollFactor = optionPref.getScrollFactor(); TheWritableGlobalData->m_drawScrollAnchor = optionPref.getDrawScrollAnchor(); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp index 34d1c0e0046..591eaddbd31 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayStringManager.cpp @@ -36,6 +36,8 @@ #include "GameClient/DrawGroupInfo.h" #include "GameClient/GlobalLanguage.h" #include "W3DDevice/GameClient/W3DDisplayStringManager.h" +// TheSuperHackers @feature for the countdown string teardown +#include "W3DDevice/GameClient/W3DGadget.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS @@ -56,6 +58,11 @@ W3DDisplayStringManager::W3DDisplayStringManager() //------------------------------------------------------------------------------------------------- W3DDisplayStringManager::~W3DDisplayStringManager() { + // TheSuperHackers @feature The countdown text's shared string must come back to the + // manager while it is still alive; the base class destructor asserts on any string + // left registered. + W3DGadgetPushButtonFreeCountdownString(); + for (Int i = 0; i < MAX_GROUPS; ++i) { if (m_groupNumeralStrings[i])