Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Core/GameEngine/Include/Common/OptionPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "WW3D2/texturefilter.h"

#include "Common/UserPreferences.h"
#include "GameClient/Color.h"

typedef UnsignedInt CursorCaptureMode;
typedef UnsignedInt ScreenEdgeScrollMode;
Expand Down Expand Up @@ -76,6 +77,10 @@ class OptionPreferences : public UserPreferences
Real getScrollFactor();
Bool getDrawScrollAnchor();
Bool getMoveScrollAnchor();
Bool getKeyboardOverlayEnabled() const;
Color getKeyboardOverlayColor() const;
Bool getKeyboardOverlayBackdropEnabled() const;
Color getKeyboardOverlayBackdropColor() const;
Bool getCursorCaptureEnabledInWindowedGame() const;
Bool getCursorCaptureEnabledInWindowedMenu() const;
Bool getCursorCaptureEnabledInFullscreenGame() const;
Expand Down Expand Up @@ -131,4 +136,8 @@ class OptionPreferences : public UserPreferences
Bool getShowMoneyPerMinute() const;

Real getGameWindowTransitionSpeedMultiplier() const;

private:
// TheSuperHackers @feature Read one 0-255 colour channel, clamped, with a fallback.
UnsignedByte getColorChannel(const char *keyName, UnsignedByte defaultValue) const;
};
5 changes: 5 additions & 0 deletions Core/GameEngine/Include/GameClient/HotKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class HotKeyManager : public SubsystemInterface
AsciiString searchHotKey( const AsciiString& label);
AsciiString searchHotKey( const UnicodeString& uStr );

// TheSuperHackers @feature Which hotkey actually got registered to this window, if any.
// Deliberately not derived from the window's label -- addHotKey drops keys that collide
// with an earlier button, so only this reports the key that will really work.
AsciiString getHotKeyForWindow( const GameWindow *win ) const;

private:
typedef std::map<AsciiString, HotKey> HotKeyMap;
HotKeyMap m_hotKeyMap;
Expand Down
65 changes: 65 additions & 0 deletions Core/GameEngine/Source/Common/OptionPreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,71 @@ Bool OptionPreferences::getRightMouseScrollWithAlternateMouseEnabled() const
return FALSE;
}

// TheSuperHackers @feature Draw each command bar cameo's keyboard hotkey over the cameo.
// Options.ini: KeyboardOverlay = Yes
Bool OptionPreferences::getKeyboardOverlayEnabled() const
{
OptionPreferences::const_iterator it = find("KeyboardOverlay");
if (it == end())
return FALSE;

if (stricmp(it->second.str(), "yes") == 0) {
return TRUE;
}
return FALSE;
}

UnsignedByte OptionPreferences::getColorChannel(const char *keyName, UnsignedByte defaultValue) const
{
OptionPreferences::const_iterator it = find(AsciiString(keyName));
if (it == end())
return defaultValue;

Int value = atoi(it->second.str());
if (value < 0)
value = 0;
else if (value > 255)
value = 255;

return (UnsignedByte)value;
}

// TheSuperHackers @feature Colour of the command bar hotkey overlay letters.
// Options.ini: KeyboardOverlayRed / KeyboardOverlayGreen / KeyboardOverlayBlue,
// each 0-255, defaulting to white. A missing or out of range channel falls
// back to full brightness rather than silently drawing an invisible letter.
Color OptionPreferences::getKeyboardOverlayColor() const
{
return GameMakeColor(
getColorChannel("KeyboardOverlayRed", 255),
getColorChannel("KeyboardOverlayGreen", 255),
getColorChannel("KeyboardOverlayBlue", 255),
255);
}

// TheSuperHackers @feature Translucent plate drawn behind the hotkey letter.
Bool OptionPreferences::getKeyboardOverlayBackdropEnabled() const
{
OptionPreferences::const_iterator it = find("KeyboardOverlayBackdrop");
if (it == end())
return TRUE; // on by default -- it is what makes the letter readable

if (stricmp(it->second.str(), "yes") == 0) {
return TRUE;
}
return FALSE;
}

Color OptionPreferences::getKeyboardOverlayBackdropColor() const
{
// black at 50% opacity, matching the darkened plate look
return GameMakeColor(
getColorChannel("KeyboardOverlayBackdropRed", 0),
getColorChannel("KeyboardOverlayBackdropGreen", 0),
getColorChannel("KeyboardOverlayBackdropBlue", 0),
getColorChannel("KeyboardOverlayBackdropOpacity", 128));
}

Bool OptionPreferences::getRetaliationModeEnabled()
{
OptionPreferences::const_iterator it = find("Retaliation");
Expand Down
18 changes: 18 additions & 0 deletions Core/GameEngine/Source/GameClient/MessageStream/HotKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@ void HotKeyManager::addHotKey( GameWindow *win, const AsciiString& keyIn)
m_hotKeyMap[key] = newHK;
}

//-----------------------------------------------------------------------------
// TheSuperHackers @feature Reverse lookup for the hotkey overlay on command bar cameos.
//-----------------------------------------------------------------------------
AsciiString HotKeyManager::getHotKeyForWindow( const GameWindow *win ) const
{
if( win == nullptr )
return AsciiString::TheEmptyString;

// the map only ever holds the currently displayed commands, so this stays tiny
for( HotKeyMap::const_iterator it = m_hotKeyMap.begin(); it != m_hotKeyMap.end(); ++it )
{
if( it->second.m_win == win )
return it->second.m_key;
}

return AsciiString::TheEmptyString;
}

//-----------------------------------------------------------------------------
Bool HotKeyManager::executeHotKey( const AsciiString& keyIn )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
///////////////////////////////////////////////////////////////////////////////

extern void W3DGadgetPushButtonDraw( GameWindow *window, WinInstanceData *instData );
// TheSuperHackers @feature Free the hotkey overlay's display strings; must run before
// TheDisplayStringManager is destroyed.
extern void W3DGadgetPushButtonFreeHotKeyStrings( void );
extern void W3DGadgetPushButtonImageDraw( GameWindow *window, WinInstanceData *instData );
extern void W3DGadgetCheckBoxDraw( GameWindow *window, WinInstanceData *instData );
extern void W3DGadgetCheckBoxImageDraw( GameWindow *window, WinInstanceData *instData );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetPushButton.h"
#include "GameClient/Display.h"
// TheSuperHackers @feature for the command bar hotkey overlay
#include "Common/GlobalData.h"
#include "GameClient/DisplayStringManager.h"
#include "GameClient/GameFont.h"
#include "GameClient/GlobalLanguage.h"
#include "GameClient/HotKey.h"
#include "W3DDevice/GameClient/W3DGameWindow.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DGadget.h"
Expand All @@ -78,6 +84,106 @@ void W3DGadgetPushButtonImageDrawOne(GameWindow *window, WinInstanceData *instDa
// drawButtonText =============================================================
/** Draw button text to the screen */
//=============================================================================
// TheSuperHackers @feature Command bar hotkey overlay (Options.ini: KeyboardOverlay).
// One display string per letter, so that drawing many cameos in a row does not
// rebuild sentence geometry over and over. There are only ever a handful of
// distinct hotkeys on screen, so this stays small. Returned to the manager by
// W3DGadgetPushButtonFreeHotKeyStrings before the manager is torn down.
static DisplayString *s_hotKeyStrings[ 256 ] = { nullptr };

//=============================================================================
/** Return the overlay's display strings to the manager. Must run before
* TheDisplayStringManager is destroyed, which asserts on any string still
* registered -- the W3DDisplayStringManager destructor calls this. */
//=============================================================================
void W3DGadgetPushButtonFreeHotKeyStrings( void )
{
for( Int i = 0; i < 256; ++i )
{
if( s_hotKeyStrings[ i ] != nullptr && TheDisplayStringManager != nullptr )
TheDisplayStringManager->freeDisplayString( s_hotKeyStrings[ i ] );

s_hotKeyStrings[ i ] = nullptr;
}
}

// drawButtonHotKeyOverlay ====================================================
/** Draw the keyboard hotkey letter over a command bar cameo, so the player can
* learn the shortcuts without hunting through tooltips.
*
* The letter comes from what actually got registered in the hotkey manager, not
* from the button's label -- colliding hotkeys are dropped at registration, and
* drawing those would advertise a key that does nothing. */
//=============================================================================
static void drawButtonHotKeyOverlay( GameWindow *window )
{
if( !TheGlobalData || !TheGlobalData->m_keyboardOverlayEnabled )
return;

if( TheHotKeyManager == nullptr || TheDisplayStringManager == nullptr )
return;

// only cameo style buttons opt into overlay states, so this leaves menu buttons alone
if( !BitIsSet( window->winGetStatus(), WIN_STATUS_USE_OVERLAY_STATES ) )
return;

AsciiString hotKey = TheHotKeyManager->getHotKeyForWindow( window );
if( hotKey.isEmpty() )
return;

// Only a single byte printable key can be shown faithfully. A localized mnemonic outside
// ASCII arrives as a multi byte sequence here, and drawing its first byte would show a
// wrong or garbled shortcut - better to draw nothing for those.
if( hotKey.getLength() != 1 || !isprint( (unsigned char)hotKey.getCharAt( 0 ) ) )
return;
Comment on lines +137 to +138

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Localized overlays disappear

When a localized command has a functional non-ASCII mnemonic, its registered UTF-8 key occupies multiple bytes, so this guard returns without drawing it and the teaching overlay silently omits that shortcut.

Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DPushButton.cpp
Line: 137-138

Comment:
**Localized overlays disappear**

When a localized command has a functional non-ASCII mnemonic, its registered UTF-8 key occupies multiple bytes, so this guard returns without drawing it and the teaching overlay silently omits that shortcut.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.


const UnsignedByte index = (UnsignedByte)hotKey.getCharAt( 0 );
DisplayString *hotKeyString = s_hotKeyStrings[ index ];

if( hotKeyString == nullptr )
{
hotKeyString = TheDisplayStringManager->newDisplayString();
if( hotKeyString == nullptr )
return;

Int pointSize = 10;
if( TheGlobalLanguageData )
pointSize = TheGlobalLanguageData->adjustFontSize( pointSize );
hotKeyString->setFont( TheFontLibrary->getFont( AsciiString( "Arial" ), pointSize, TRUE ) );

// the manager stores keys lowercased, but shortcuts read better as capitals
UnicodeString text;
WideChar upper = (WideChar)toupper( (Int)index );
Comment thread
greptile-apps[bot] marked this conversation as resolved.
text.concat( upper );
hotKeyString->setText( text );

s_hotKeyStrings[ index ] = hotKeyString;
}

ICoord2D origin;
window->winGetScreenPosition( &origin.x, &origin.y );

// tuck it into the top left of the cameo, where no existing decoration lives
const Int inset = 2;
const Int textX = origin.x + inset;
const Int textY = origin.y + inset;

// Optional plate behind the letter, so it stays readable over busy cameo art.
if( TheGlobalData->m_keyboardOverlayBackdrop )
{
Int width, height;
hotKeyString->getSize( &width, &height );

const Int pad = 1;
TheDisplay->drawFillRect( textX - pad, textY - pad,
width + pad * 2, height + pad * 2,
TheGlobalData->m_keyboardOverlayBackdropColor );
}

hotKeyString->draw( textX, textY,
TheGlobalData->m_keyboardOverlayColor, GameMakeColor( 0, 0, 0, 255 ) );
}

static void drawButtonText( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, textPos;
Expand Down Expand Up @@ -481,6 +587,10 @@ void W3DGadgetPushButtonImageDrawOne( GameWindow *window,
}
}
}

// TheSuperHackers @feature Draw the hotkey letter last, so it stays readable on top
// of the hilite and pushed overlays.
drawButtonHotKeyOverlay( window );
}


Expand Down
5 changes: 5 additions & 0 deletions Generals/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ 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 Show each command bar cameo's hotkey letter on the cameo.
Bool m_keyboardOverlayEnabled;
Color m_keyboardOverlayColor;
Bool m_keyboardOverlayBackdrop;
Color m_keyboardOverlayBackdropColor;
Bool m_doubleClickAttackMove;
Bool m_rightMouseAlwaysScrolls;
Int m_jpegQuality; // TheSuperHackers @feature Quality for JPEG screenshots.
Expand Down
8 changes: 8 additions & 0 deletions Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,10 @@ GlobalData::GlobalData()
m_useRightMouseScrollWithAlternateMouse = TRUE;
#endif
m_clientRetaliationModeEnabled = TRUE; //On by default.
m_keyboardOverlayEnabled = FALSE;
m_keyboardOverlayColor = GameMakeColor( 255, 255, 255, 255 );
m_keyboardOverlayBackdrop = TRUE;
m_keyboardOverlayBackdropColor = GameMakeColor( 0, 0, 0, 128 );
m_doubleClickAttackMove = FALSE;

}
Expand Down Expand Up @@ -1199,6 +1203,10 @@ void GlobalData::parseGameDataDefinition( INI* ini )
TheWritableGlobalData->m_useRightMouseScrollWithAlternateMouse = optionPref.getRightMouseScrollWithAlternateMouseEnabled();
TheWritableGlobalData->m_clientRetaliationModeEnabled = optionPref.getRetaliationModeEnabled();
TheWritableGlobalData->m_doubleClickAttackMove = optionPref.getDoubleClickAttackMoveEnabled();
TheWritableGlobalData->m_keyboardOverlayEnabled = optionPref.getKeyboardOverlayEnabled();
TheWritableGlobalData->m_keyboardOverlayColor = optionPref.getKeyboardOverlayColor();
TheWritableGlobalData->m_keyboardOverlayBackdrop = optionPref.getKeyboardOverlayBackdropEnabled();
TheWritableGlobalData->m_keyboardOverlayBackdropColor = optionPref.getKeyboardOverlayBackdropColor();
TheWritableGlobalData->m_jpegQuality = optionPref.getJpegQuality();
TheWritableGlobalData->m_keyboardScrollFactor = optionPref.getScrollFactor();
TheWritableGlobalData->m_drawScrollAnchor = optionPref.getDrawScrollAnchor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "GameClient/DrawGroupInfo.h"
#include "GameClient/GlobalLanguage.h"
#include "W3DDevice/GameClient/W3DDisplayStringManager.h"
// TheSuperHackers @feature for the hotkey overlay string teardown
#include "W3DDevice/GameClient/W3DGadget.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
Expand All @@ -56,6 +58,11 @@ W3DDisplayStringManager::W3DDisplayStringManager()
//-------------------------------------------------------------------------------------------------
W3DDisplayStringManager::~W3DDisplayStringManager()
{
// TheSuperHackers @feature The hotkey overlay's shared strings must come back to the
// manager while it is still alive; the base class destructor asserts on any string
// left registered.
W3DGadgetPushButtonFreeHotKeyStrings();

for (Int i = 0; i < MAX_GROUPS; ++i)
{
if (m_groupNumeralStrings[i])
Expand Down
5 changes: 5 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ 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 Show each command bar cameo's hotkey letter on the cameo.
Bool m_keyboardOverlayEnabled;
Color m_keyboardOverlayColor;
Bool m_keyboardOverlayBackdrop;
Color m_keyboardOverlayBackdropColor;
Bool m_doubleClickAttackMove;
Bool m_rightMouseAlwaysScrolls;
Int m_jpegQuality; // TheSuperHackers @feature Quality for JPEG screenshots.
Expand Down
8 changes: 8 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,10 @@ GlobalData::GlobalData()
m_useRightMouseScrollWithAlternateMouse = TRUE;
#endif
m_clientRetaliationModeEnabled = TRUE; //On by default.
m_keyboardOverlayEnabled = FALSE;
m_keyboardOverlayColor = GameMakeColor( 255, 255, 255, 255 );
m_keyboardOverlayBackdrop = TRUE;
m_keyboardOverlayBackdropColor = GameMakeColor( 0, 0, 0, 128 );
m_doubleClickAttackMove = FALSE;

}
Expand Down Expand Up @@ -1206,6 +1210,10 @@ void GlobalData::parseGameDataDefinition( INI* ini )
TheWritableGlobalData->m_useRightMouseScrollWithAlternateMouse = optionPref.getRightMouseScrollWithAlternateMouseEnabled();
TheWritableGlobalData->m_clientRetaliationModeEnabled = optionPref.getRetaliationModeEnabled();
TheWritableGlobalData->m_doubleClickAttackMove = optionPref.getDoubleClickAttackMoveEnabled();
TheWritableGlobalData->m_keyboardOverlayEnabled = optionPref.getKeyboardOverlayEnabled();
TheWritableGlobalData->m_keyboardOverlayColor = optionPref.getKeyboardOverlayColor();
TheWritableGlobalData->m_keyboardOverlayBackdrop = optionPref.getKeyboardOverlayBackdropEnabled();
TheWritableGlobalData->m_keyboardOverlayBackdropColor = optionPref.getKeyboardOverlayBackdropColor();
TheWritableGlobalData->m_jpegQuality = optionPref.getJpegQuality();
TheWritableGlobalData->m_keyboardScrollFactor = optionPref.getScrollFactor();
TheWritableGlobalData->m_drawScrollAnchor = optionPref.getDrawScrollAnchor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "GameClient/DrawGroupInfo.h"
#include "GameClient/GlobalLanguage.h"
#include "W3DDevice/GameClient/W3DDisplayStringManager.h"
// TheSuperHackers @feature for the hotkey overlay string teardown
#include "W3DDevice/GameClient/W3DGadget.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
Expand All @@ -56,6 +58,11 @@ W3DDisplayStringManager::W3DDisplayStringManager()
//-------------------------------------------------------------------------------------------------
W3DDisplayStringManager::~W3DDisplayStringManager()
{
// TheSuperHackers @feature The hotkey overlay's shared strings must come back to the
// manager while it is still alive; the base class destructor asserts on any string
// left registered.
W3DGadgetPushButtonFreeHotKeyStrings();

for (Int i = 0; i < MAX_GROUPS; ++i)
{
if (m_groupNumeralStrings[i])
Expand Down