diff --git a/sp/src/game/client/c_point_commentary_node.cpp b/sp/src/game/client/c_point_commentary_node.cpp index d65896b5af1..e547b0ce7ae 100644 --- a/sp/src/game/client/c_point_commentary_node.cpp +++ b/sp/src/game/client/c_point_commentary_node.cpp @@ -92,8 +92,6 @@ class CHudCommentary : public CHudElement, public vgui::Panel #ifdef MAPBASE virtual void PerformLayout(); void ResolveBounds( int width, int height ); - - virtual void LevelShutdown(); #endif private: @@ -118,10 +116,6 @@ class CHudCommentary : public CHudElement, public vgui::Panel vgui::Label *m_pFootnoteLabel; vgui::HFont m_hSmallFont; - // HACKHACK: Needed as a failsafe to prevent desync - int m_iCCDefaultY; - float m_flCCAnimTime; - bool m_bShouldRepositionSubtitles; #endif @@ -909,9 +903,6 @@ CHudCommentary::CHudCommentary( const char *name ) : vgui::Panel( NULL, "HudComm m_pImage->SetShouldScaleImage( true ); m_pFootnoteLabel = new vgui::Label( this, "HudCommentaryFootnoteLabel", L"Commentary footnote" ); - - m_iCCDefaultY = 0; - m_flCCAnimTime = 0.0f; #endif } @@ -955,12 +946,9 @@ void CHudCommentary::Paint() #ifdef MAPBASE // Reset close caption element if needed - if (pHudCloseCaption->IsUsingCommentaryDimensions()) + if (pHudCloseCaption->IsUsingCustomDimensions( ToHandle() )) { - // Run this animation command instead of setting the position directly - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); - - pHudCloseCaption->SetUsingCommentaryDimensions( false ); + pHudCloseCaption->StopUsingCustomDimensions(); } #endif } @@ -978,12 +966,9 @@ void CHudCommentary::Paint() // Reset close caption element if needed CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + if (pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions(ToHandle())) { - // Run this animation command instead of setting the position directly - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); - - pHudCloseCaption->SetUsingCommentaryDimensions( false ); + pHudCloseCaption->StopUsingCustomDimensions(); } #endif @@ -1306,30 +1291,6 @@ void CHudCommentary::ResolveBounds( int width, int height ) SetBounds( xPos, yPos, width, height ); } - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CHudCommentary::LevelShutdown( void ) -{ - if (m_iCCDefaultY != 0) - { - CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) - { - int ccX, ccY; - pHudCloseCaption->GetPos( ccX, ccY ); - - if (m_iCCDefaultY != ccY) - { - DevMsg( "CHudCommentary had to reset misaligned CC element Y (%i) to default Y (%i)\n", ccY, m_iCCDefaultY ); - pHudCloseCaption->SetPos( ccX, m_iCCDefaultY ); - } - - pHudCloseCaption->SetUsingCommentaryDimensions( false ); - } - } -} #endif //----------------------------------------------------------------------------- @@ -1351,9 +1312,6 @@ void CHudCommentary::VidInit( void ) { SetAlpha(0); StopCommentary(); -#ifdef MAPBASE - m_iCCDefaultY = 0; -#endif } //----------------------------------------------------------------------------- @@ -1677,12 +1635,9 @@ void CHudCommentary::StopCommentary( void ) #ifdef MAPBASE // Reset close caption element if needed CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + if (pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions(ToHandle())) { - // Run this animation command instead of setting the position directly - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); - - pHudCloseCaption->SetUsingCommentaryDimensions( false ); + pHudCloseCaption->StopUsingCustomDimensions(); } #endif } @@ -1748,12 +1703,9 @@ void CHudCommentary::FixupCommentaryLabels( const char *pszPrintName, const char // Reset close caption element if it's still using commentary dimensions // (fixes problems with switching from node to node) CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if (pHudCloseCaption && pHudCloseCaption->IsUsingCommentaryDimensions()) + if (pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions(ToHandle())) { - // Run this animation command instead of setting the position directly - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY, 0.0f, 0.4f, vgui::AnimationController::INTERPOLATOR_ACCEL ); - - pHudCloseCaption->SetUsingCommentaryDimensions( false ); + pHudCloseCaption->StopUsingCustomDimensions(); } } @@ -1767,41 +1719,29 @@ void CHudCommentary::RepositionAndFollowCloseCaption( int yOffset ) // Place underneath the close caption element CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); - if (pHudCloseCaption /*&& !pHudCloseCaption->IsUsingCommentaryDimensions()*/) + if (pHudCloseCaption /*&& !pHudCloseCaption->IsUsingCustomDimensions()*/) { int ccX, ccY; pHudCloseCaption->GetPos( ccX, ccY ); - // Save the default position in case we need to do a hard reset - // (this usually happens when players begin commentary before the CC element's return animation command is finished) - if (m_iCCDefaultY == 0) - { - m_iCCDefaultY = ccY; - } + int ccDefX, ccDefY; + pHudCloseCaption->GetDefaultPos( ccDefX, ccDefY ); - if (!pHudCloseCaption->IsUsingCommentaryDimensions()) + if (!pHudCloseCaption->IsUsingCustomDimensions( ToHandle() )) { - if (m_iCCDefaultY != ccY /*&& !pHudCloseCaption->IsUsingCommentaryDimensions()*/) + if (ccDefY != ccY /*&& !pHudCloseCaption->IsUsingCustomDimensions()*/) { - DevMsg( "CHudCommentary had to reset misaligned CC element Y (%i) to default Y (%i)\n", ccY, m_iCCDefaultY ); - ccY = m_iCCDefaultY; + ccY = ccDefY; } ccY -= m_iTypeAudioT; - // Run this animation command instead of setting the position directly - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", ccY - yOffset, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_DEACCEL ); - //pHudCloseCaption->SetPos( ccX, ccY ); - m_flCCAnimTime = gpGlobals->curtime + 0.2f; - - pHudCloseCaption->SetUsingCommentaryDimensions( true ); + pHudCloseCaption->StartUsingCustomDimensions( ToHandle(), -1, ccY - yOffset ); } - else if (gpGlobals->curtime > m_flCCAnimTime && ccY != m_iCCDefaultY - m_iTypeAudioT - yOffset) + else if (!pHudCloseCaption->IsMovingToCustomDimensions() && ccY != ccDefY - m_iTypeAudioT - yOffset) { - DevMsg( "CHudCommentary had to correct misaligned CC element offset (%i != %i)\n", m_iCCDefaultY - ccY, yOffset ); - - g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( pHudCloseCaption, "YPos", m_iCCDefaultY - m_iTypeAudioT - yOffset, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_DEACCEL ); - m_flCCAnimTime = gpGlobals->curtime + 0.2f; + DevMsg( "CHudCommentary had to correct misaligned CC element offset (%i != %i)\n", ccDefY - ccY, yOffset ); + pHudCloseCaption->StartUsingCustomDimensions( ToHandle(), -1, ccDefY - m_iTypeAudioT - yOffset ); } SetPos( ccX, ccY + pHudCloseCaption->GetTall() + commentary_audio_element_below_cc_margin.GetInt() ); diff --git a/sp/src/game/client/hud_closecaption.cpp b/sp/src/game/client/hud_closecaption.cpp index 5fdfdfa1258..6fd4d0e32e4 100644 --- a/sp/src/game/client/hud_closecaption.cpp +++ b/sp/src/game/client/hud_closecaption.cpp @@ -22,6 +22,9 @@ #include "filesystem.h" #include "datacache/idatacache.h" #include "SoundEmitterSystem/isoundemittersystembase.h" +#ifdef MAPBASE +#include +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -839,6 +842,12 @@ CHudCloseCaption::CHudCloseCaption( const char *pElementName ) m_bLocked = false; m_bVisibleDueToDirect = false; +#ifdef MAPBASE + m_hCustomDimensionsOwner = vgui::INVALID_PANEL; + m_flCustomDimensionsAnimTime = 0.0f; + m_nDefaultX = m_nDefaultY = 0; +#endif + SetPaintBorderEnabled( false ); SetPaintBackgroundEnabled( false ); @@ -890,6 +899,17 @@ void CHudCloseCaption::LevelInit( void ) // Wipe any stale pending work items... ClearAsyncWork(); + +#ifdef MAPBASE + if ( IsUsingCustomDimensions() ) + { + // Reset position immediately + SetPos( m_nDefaultX, m_nDefaultY ); + m_hCustomDimensionsOwner = vgui::INVALID_PANEL; + } + + m_nDefaultX = m_nDefaultY = 0; +#endif } static ConVar cc_minvisibleitems( "cc_minvisibleitems", "1", 0, "Minimum number of caption items to show." ); @@ -3160,3 +3180,71 @@ void CHudCloseCaption::FindSound( char const *pchANSI ) delete[] block; } } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCloseCaption::GetDefaultPos( int &x, int &y ) +{ + GetPos( x, y ); + + if ( m_nDefaultX != 0 ) + x = m_nDefaultX; + + if ( m_nDefaultY != 0 ) + y = m_nDefaultY; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCloseCaption::StartUsingCustomDimensions( vgui::HPanel hOwner, int nNewX, int nNewY, float flAnimTime ) +{ + int x, y; + GetPos( x, y ); + + if ( m_hCustomDimensionsOwner == vgui::INVALID_PANEL && m_nDefaultY == 0 ) + { + m_nDefaultX = x; + m_nDefaultY = y; + } + + // Run animation commands instead of setting the position directly + if ( x != nNewX && nNewX != -1 ) + { + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "XPos", nNewX, 0.0f, flAnimTime, vgui::AnimationController::INTERPOLATOR_DEACCEL ); + } + + if ( y != nNewY && nNewY != -1 ) + { + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "YPos", nNewY, 0.0f, flAnimTime, vgui::AnimationController::INTERPOLATOR_DEACCEL ); + } + + m_hCustomDimensionsOwner = hOwner; + m_flCustomDimensionsAnimTime = gpGlobals->curtime + flAnimTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudCloseCaption::StopUsingCustomDimensions( float flAnimTime ) +{ + int x, y; + GetPos( x, y ); + + // Run animation commands instead of setting the position directly + if ( x != m_nDefaultX ) + { + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "XPos", m_nDefaultX, 0.0f, flAnimTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + if ( y != m_nDefaultY ) + { + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "YPos", m_nDefaultY, 0.0f, flAnimTime, vgui::AnimationController::INTERPOLATOR_SIMPLESPLINE ); + } + + m_hCustomDimensionsOwner = vgui::INVALID_PANEL; + m_flCustomDimensionsAnimTime = gpGlobals->curtime + flAnimTime; +} +#endif diff --git a/sp/src/game/client/hud_closecaption.h b/sp/src/game/client/hud_closecaption.h index 8688e60e049..a1097e3a189 100644 --- a/sp/src/game/client/hud_closecaption.h +++ b/sp/src/game/client/hud_closecaption.h @@ -139,8 +139,15 @@ class CHudCloseCaption : public CHudElement, public vgui::Panel void FindSound( char const *pchANSI ); #ifdef MAPBASE - inline bool IsUsingCommentaryDimensions() const { return m_bUsingCommentaryDimensions; } - inline void SetUsingCommentaryDimensions( bool bToggle ) { m_bUsingCommentaryDimensions = bToggle; } + // Other HUD elements may need to take the caption element's place + inline bool IsUsingCustomDimensions() const { return m_hCustomDimensionsOwner != vgui::INVALID_PANEL; } + inline bool IsUsingCustomDimensions( vgui::HPanel hOwner ) const { return m_hCustomDimensionsOwner == hOwner; } + inline bool IsMovingToCustomDimensions() const { return m_flCustomDimensionsAnimTime > gpGlobals->curtime; } + inline float GetBackgroundAlpha() const { return m_flBackgroundAlpha; } + + void GetDefaultPos( int &x, int &y ); + void StartUsingCustomDimensions( vgui::HPanel hOwner, int nNewX, int nNewY, float flAnimTime = 0.2f ); + void StopUsingCustomDimensions( float flAnimTime = 0.4f ); #endif public: @@ -224,7 +231,9 @@ class CHudCloseCaption : public CHudElement, public vgui::Panel CUtlSymbol m_CurrentLanguage; #ifdef MAPBASE - bool m_bUsingCommentaryDimensions; + vgui::HPanel m_hCustomDimensionsOwner; + float m_flCustomDimensionsAnimTime; + int m_nDefaultX, m_nDefaultY; #endif }; diff --git a/sp/src/game/client/menu.cpp b/sp/src/game/client/menu.cpp index b11eb43b312..dfaa145d6c0 100644 --- a/sp/src/game/client/menu.cpp +++ b/sp/src/game/client/menu.cpp @@ -22,7 +22,19 @@ #include #include +#ifdef MAPBASE +#include "hud_closecaption.h" + +ConVar hud_menu_complex_max_pixels( "hud_menu_complex_max_pixels", "800" ); +ConVar hud_menu_center_border_x( "hud_menu_center_border_x", "64" ); +ConVar hud_menu_center_cc_margin( "hud_menu_center_cc_margin", "4" ); +ConVar hud_menu_big_text_margin( "hud_menu_big_text_margin", "12" ); + +#define MENU_CC_SPEED_REVERT 1.0f +#define MAX_MENU_STRING 1024 +#else #define MAX_MENU_STRING 512 +#endif wchar_t g_szMenuString[MAX_MENU_STRING]; char g_szPrelocalisedMenuString[MAX_MENU_STRING]; @@ -167,6 +179,16 @@ bool CHudMenu::ShouldDraw( void ) g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MenuClose" ); m_bMenuTakesInput = false; m_bPlayingFadeout = true; + + if ( IsCentered() ) + { + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions( MENU_CC_SPEED_REVERT ); + } + } } else #endif @@ -196,6 +218,22 @@ void CHudMenu::PaintString( const wchar_t *text, int textlen, vgui::HFont& font, for ( int ch = 0; ch < textlen; ch++ ) { +#ifdef MAPBASE + if ( text[ch] == '\n' ) + { + // Line break + y += vgui::surface()->GetFontTall( font ); + vgui::surface()->DrawSetTextPos( x, y ); + continue; + } + else if ( ch == 3 && text[1] == '.' && text[2] == ' ' ) + { + // If this is a menu item (e.g. '1. Item') and we've drawn the number part, + // store the text pos so that line breaks are indented + vgui::surface()->DrawGetTextPos( x, y ); + } +#endif + vgui::surface()->DrawUnicodeChar( text[ch] ); } } @@ -214,6 +252,10 @@ void CHudMenu::Paint() Color menuColor = m_MenuColor; Color itemColor = m_ItemColor; + Color boxColor = m_BoxColor; + + vgui::HFont hItemFont = m_hItemFont; + vgui::HFont hItemFontPulsing = m_hItemFontPulsing; int c = m_Processed.Count(); @@ -222,9 +264,44 @@ void CHudMenu::Paint() int y = ( ScreenHeight() - tall ) * 0.5f; - DrawBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); + int borderX = m_nBorder / 2; + int borderY = borderX; + +#ifdef MAPBASE + if ( IsCentered() ) + { + y = m_nCenterY; + borderX += (hud_menu_center_border_x.GetInt() / 2); + + if ( m_nCenterX == 0 ) + { + // Center with existing menu width + x = ( ScreenWidth() - wide ) * 0.5f; + wide += hud_menu_center_border_x.GetInt(); + } + else + { + x = m_nCenterX + (hud_menu_center_border_x.GetInt()/2); + wide = m_nCenterWide /*- (hud_menu_center_border_x.GetInt()/2)*/; + } + } - //DrawTexturedBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); + if ( IsUsingBigFonts() ) + { + if ( m_hItemFontBig != vgui::INVALID_FONT && m_hItemFontBigPulsing != vgui::INVALID_FONT ) + { + hItemFont = m_hItemFontBig; + hItemFontPulsing = m_hItemFontBigPulsing; + } + } + + if ( m_flBoxAlphaOverride != 0.0f ) + boxColor[3] = m_flBoxAlphaOverride; +#endif + + DrawBox( x - borderX, y - borderY, wide, tall, boxColor, m_flSelectionAlphaOverride / 255.0f ); + + //DrawTexturedBox( x - borderX, y - borderY, wide, tall, boxColor, m_flSelectionAlphaOverride / 255.0f ); menuColor[3] = menuColor[3] * ( m_flSelectionAlphaOverride / 255.0f ); itemColor[3] = itemColor[3] * ( m_flSelectionAlphaOverride / 255.0f ); @@ -263,10 +340,40 @@ void CHudMenu::Paint() drawLen *= m_flTextScan; } - vgui::surface()->DrawSetTextFont( isItem ? m_hItemFont : m_hTextFont ); + vgui::surface()->DrawSetTextFont( isItem ? hItemFont : m_hTextFont ); + + int itemX = x; + +#ifdef MAPBASE + if ( IsCentered() ) + { + if ( !isItem ) + { + // Center title + itemX = ( ScreenWidth() - line->pixels ) * 0.5f; + + PaintString( &g_szMenuString[ line->startchar ], drawLen, + isItem ? hItemFont : m_hTextFont, itemX, y ); + } + else + { + // Paint the numbers with the title color + vgui::surface()->DrawSetTextColor( menuColor ); + PaintString( &g_szMenuString[ line->startchar ], 3, + hItemFont, itemX, y ); + vgui::surface()->DrawSetTextColor( itemColor ); + + int nTextX, nTextY; + vgui::surface()->DrawGetTextPos( nTextX, nTextY ); + PaintString( &g_szMenuString[ line->startchar + 3 ], drawLen - 3, + isItem ? hItemFont : m_hTextFont, nTextX, nTextY ); + } + } + else +#endif PaintString( &g_szMenuString[ line->startchar ], drawLen, - isItem ? m_hItemFont : m_hTextFont, x, y ); + isItem ? hItemFont : m_hTextFont, itemX, y ); if ( canblur ) { @@ -275,7 +382,7 @@ void CHudMenu::Paint() { if (fl >= 1.0f) { - PaintString( &g_szMenuString[ line->startchar ], drawLen, m_hItemFontPulsing, x, y ); + PaintString( &g_szMenuString[ line->startchar ], drawLen, hItemFontPulsing, itemX, y ); } else { @@ -283,7 +390,7 @@ void CHudMenu::Paint() Color col = clr; col[3] *= fl; vgui::surface()->DrawSetTextColor(col); - PaintString( &g_szMenuString[ line->startchar ], drawLen, m_hItemFontPulsing, x, y ); + PaintString( &g_szMenuString[ line->startchar ], drawLen, hItemFontPulsing, itemX, y ); } } } @@ -326,6 +433,18 @@ void CHudMenu::SelectMenuItem( int menu_item ) m_bMenuTakesInput = false; m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose"); + +#ifdef MAPBASE + if ( IsCentered() ) + { + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions( MENU_CC_SPEED_REVERT ); + } + } +#endif } } @@ -418,6 +537,52 @@ void CHudMenu::ProcessText( void ) int pixels = 0; vgui::HFont font = isItem ? m_hItemFont : m_hTextFont; +#ifdef MAPBASE + if ( IsUsingBigFonts() && font == m_hItemFont) + font = m_hItemFontBig; + + // We use a more complex algorithm to support breaking lines when word wrapping is enabled + int nMaxPixels = ( IsFillingWidth() ? m_nCenterWide - hud_menu_center_border_x.GetInt() : hud_menu_complex_max_pixels.GetInt() ) - (m_nBorder*2); + int nHighestPixels = 0, nItemPixels = 0, nNumLines = 1, iLastSpace = 0; + for ( int ch = 0; ch < l->length; ch++ ) + { + pixels += vgui::surface()->GetCharacterWidth( font, g_szMenuString[ ch + l->startchar ] ); + + if ( ShouldWordWrap() ) + { + if ( pixels > nMaxPixels && iLastSpace != 0 ) + { + // Replace last space with a newline and increment lines + g_szMenuString[iLastSpace] = '\n'; + nNumLines++; + if ( nNumLines == 2 ) + { + // Account for indent + nMaxPixels -= nItemPixels; + } + + if ( pixels > nHighestPixels ) + nHighestPixels = pixels; + pixels = 0; + } + else if ( ch == 3 && isItem ) + { + // If this is a menu item (e.g. '1. Item') and we've drawn the number part, + // store how many pixels it is so that line breaks are indented properly + nItemPixels = pixels; + } + } + + if ( g_szMenuString[ ch + l->startchar ] == ' ' ) + iLastSpace = ch + l->startchar; + } + + l->pixels = nHighestPixels == 0 ? pixels : nHighestPixels; + l->height = vgui::surface()->GetFontTall( font ) * nNumLines; + + if ( IsUsingBigFonts() && i+1 != c ) // All but the last + l->height += hud_menu_big_text_margin.GetInt(); +#else for ( int ch = 0; ch < l->length; ch++ ) { pixels += vgui::surface()->GetCharacterWidth( font, g_szMenuString[ ch + l->startchar ] ); @@ -425,9 +590,11 @@ void CHudMenu::ProcessText( void ) l->pixels = pixels; l->height = vgui::surface()->GetFontTall( font ); - if ( pixels > m_nMaxPixels ) +#endif + + if ( l->pixels > m_nMaxPixels ) { - m_nMaxPixels = pixels; + m_nMaxPixels = l->pixels; } m_nHeight += l->height; } @@ -441,6 +608,15 @@ void CHudMenu::HideMenu( void ) m_bMenuTakesInput = false; m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose"); + +#ifdef MAPBASE + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions( MENU_CC_SPEED_REVERT ); + } +#endif } //----------------------------------------------------------------------------- @@ -460,6 +636,15 @@ void CHudMenu::ShowMenu( const char * menuName, int validSlots ) #ifdef MAPBASE m_bMapDefinedMenu = false; m_bPlayingFadeout = false; + m_nMenuFlags = 0; + m_flBoxAlphaOverride = 0.0f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions(); + } #endif Q_strncpy( g_szPrelocalisedMenuString, menuName, sizeof( g_szPrelocalisedMenuString ) ); @@ -492,6 +677,16 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV ) #ifdef MAPBASE m_bMapDefinedMenu = false; m_bPlayingFadeout = false; + + m_nMenuFlags = 0; + m_flBoxAlphaOverride = 0.0f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions(); + } #endif g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpen"); @@ -607,6 +802,16 @@ void CHudMenu::MsgFunc_ShowMenu( bf_read &msg) #ifdef MAPBASE m_bMapDefinedMenu = false; m_bPlayingFadeout = false; + + m_nMenuFlags = 0; + m_flBoxAlphaOverride = 0.0f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions(); + } #endif } @@ -627,6 +832,7 @@ void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) m_bitsValidSlots = (short)msg.ReadWord(); float DisplayTime = msg.ReadFloat(); int NeedMore = msg.ReadByte(); + m_nMenuFlags = msg.ReadByte(); m_nBorder = hud_menu_complex_border.GetInt(); m_bMapDefinedMenu = true; @@ -657,7 +863,26 @@ void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) if ( !NeedMore ) { - g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpenFlash"); + if ( IsFillingWidth() ) + { + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption ) + { + // Need to get this before we process the text + m_nCenterWide = pHudCloseCaption->GetWide(); + } + } + + const char *pszAnimName = "MenuOpenSlow"; + if ( ShouldMenuFlash() ) + { + if ( IsCentered() ) + pszAnimName = "MenuOpenFlashTitle"; + else + pszAnimName = "MenuOpenFlash"; + } + + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( pszAnimName ); m_nSelectedItem = -1; // we have the whole string, so we can localise it now @@ -672,7 +897,7 @@ void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) if (!*pszToken || *pszToken == ' ') continue; - wchar_t wszMenuItem[128]; + wchar_t wszMenuItem[256]; const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pszToken ); if (wLocalizedItem) @@ -708,6 +933,23 @@ void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) } ProcessText(); + + if ( IsCentered() ) + { + int tall = m_nHeight + m_nBorder; + RepositionAndFollowCloseCaption( tall ); + } + else + { + m_flBoxAlphaOverride = 0.0f; + + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if ( pHudCloseCaption && pHudCloseCaption->IsUsingCustomDimensions( ToHandle() ) ) + { + // Reset if it's still using custom dimensions we defined + pHudCloseCaption->StopUsingCustomDimensions(); + } + } } m_bMenuDisplayed = true; @@ -726,6 +968,41 @@ void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) m_fWaitingForMore = NeedMore; } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMenu::RepositionAndFollowCloseCaption( int yOffset ) +{ + // Invert the Y axis + //SetPos( m_iTypeAudioX, ScreenHeight() - m_iTypeAudioY ); + + // Place underneath the close caption element + CHudCloseCaption *pHudCloseCaption = (CHudCloseCaption *)GET_HUDELEMENT( CHudCloseCaption ); + if (pHudCloseCaption /*&& !pHudCloseCaption->IsUsingCustomDimensions()*/) + { + int ccX, ccY; + pHudCloseCaption->GetDefaultPos( ccX, ccY ); + + //if (!pHudCloseCaption->IsUsingCustomDimensions( ToHandle() )) + { + pHudCloseCaption->StartUsingCustomDimensions( ToHandle(), -1, ccY - yOffset - hud_menu_center_cc_margin.GetInt() ); + } + + m_nCenterY = ccY + pHudCloseCaption->GetTall() - yOffset + m_nBorder/2; + + if ( IsFillingWidth() ) + { + m_nCenterX = ccX + m_nBorder/2; + } + else + { + m_nCenterX = 0; + } + + m_flBoxAlphaOverride = pHudCloseCaption->GetBackgroundAlpha(); + } +} #endif //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/menu.h b/sp/src/game/client/menu.h index 80f14eae70a..fd17f7102c4 100644 --- a/sp/src/game/client/menu.h +++ b/sp/src/game/client/menu.h @@ -33,6 +33,7 @@ class CHudMenu : public CHudElement, public vgui::Panel void MsgFunc_ShowMenu( bf_read &msg ); #ifdef MAPBASE void MsgFunc_ShowMenuComplex( bf_read &msg ); + void RepositionAndFollowCloseCaption( int yOffset ); #endif void HideMenu( void ); void ShowMenu( const char * menuName, int keySlot ); @@ -41,6 +42,14 @@ class CHudMenu : public CHudElement, public vgui::Panel bool IsMenuOpen( void ); void SelectMenuItem( int menu_item ); +#ifdef MAPBASE + bool IsCentered() const { return m_nMenuFlags & MenuFlags_Centered; } + bool IsFillingWidth() const { return m_nMenuFlags & MenuFlags_FillWidth; } + bool IsUsingBigFonts() const { return m_nMenuFlags & MenuFlags_BigFonts; } + bool ShouldMenuFlash() const { return !(m_nMenuFlags & MenuFlags_NoFlash); } + bool ShouldWordWrap() const { return m_nMenuFlags & MenuFlags_WordWrap; } +#endif + private: virtual void OnThink(); virtual void Paint(); @@ -80,6 +89,22 @@ class CHudMenu : public CHudElement, public vgui::Panel // Indicates this menu is defined by game_menu bool m_bMapDefinedMenu; bool m_bPlayingFadeout; + + // Menu flags + enum MenuFlags_t + { + MenuFlags_Centered = (1 << 0), // Centered in caption area + MenuFlags_FillWidth = (1 << 1), // Box takes up entire caption width (centered only) + MenuFlags_BigFonts = (1 << 2), // Use big fonts + MenuFlags_NoFlash = (1 << 3), // Don't flash when opening + MenuFlags_WordWrap = (1 << 4), // Wrap menu items when they get too long + }; + + int m_nMenuFlags; + + // Centered menu + int m_nCenterX, m_nCenterY, m_nCenterWide; + float m_flBoxAlphaOverride; #endif CPanelAnimationVar( float, m_flOpenCloseTime, "OpenCloseTime", "1" ); @@ -94,6 +119,11 @@ class CHudMenu : public CHudElement, public vgui::Panel CPanelAnimationVar( vgui::HFont, m_hItemFont, "ItemFont", "MenuItemFont" ); CPanelAnimationVar( vgui::HFont, m_hItemFontPulsing, "ItemFontPulsing", "MenuItemFontPulsing" ); +#ifdef MAPBASE + CPanelAnimationVar( vgui::HFont, m_hItemFontBig, "ItemFontBig", "MenuItemFontBig" ); + CPanelAnimationVar( vgui::HFont, m_hItemFontBigPulsing, "ItemFontBigPulsing", "MenuItemFontBigPulsing" ); +#endif + CPanelAnimationVar( Color, m_MenuColor, "MenuColor", "MenuColor" ); CPanelAnimationVar( Color, m_ItemColor, "MenuItemColor", "ItemColor" ); CPanelAnimationVar( Color, m_BoxColor, "MenuBoxColor", "MenuBoxBg" ); diff --git a/sp/src/game/server/maprules.cpp b/sp/src/game/server/maprules.cpp index 7422ed836c0..9b5caee0fa3 100644 --- a/sp/src/game/server/maprules.cpp +++ b/sp/src/game/server/maprules.cpp @@ -841,6 +841,8 @@ BEGIN_DATADESC( CGameMenu ) DEFINE_KEYFIELD( m_iszTitle, FIELD_STRING, "Title" ), + DEFINE_KEYFIELD( m_bSkipEmptyCases, FIELD_BOOLEAN, "SkipEmptyCases" ), + DEFINE_KEYFIELD( m_iszOption[0], FIELD_STRING, "Case01" ), DEFINE_KEYFIELD( m_iszOption[1], FIELD_STRING, "Case02" ), DEFINE_KEYFIELD( m_iszOption[2], FIELD_STRING, "Case03" ), @@ -857,6 +859,17 @@ BEGIN_DATADESC( CGameMenu ) DEFINE_INPUTFUNC( FIELD_VOID, "HideMenu", InputHideMenu ), DEFINE_INPUTFUNC( FIELD_VOID, "__DoRestore", InputDoRestore ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase01", InputSetCase01 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase02", InputSetCase02 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase03", InputSetCase03 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase04", InputSetCase04 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase05", InputSetCase05 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase06", InputSetCase06 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase07", InputSetCase07 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase08", InputSetCase08 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase09", InputSetCase09 ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCase10", InputSetCase10 ), + // Outputs DEFINE_OUTPUT( m_OnCase[0], "OnCase01" ), DEFINE_OUTPUT( m_OnCase[1], "OnCase02" ), @@ -1007,23 +1020,62 @@ void CGameMenu::ShowMenu( CRecipientFilter &filter, float flDisplayTime ) // Insert newline even if there's no string V_strncat( szString, "\n", sizeof( szString ) ); - // Populate the options - for (int i = 0; i < MAX_MENU_OPTIONS; i++) + if ( m_bSkipEmptyCases ) { - if (m_iszOption[i] != NULL_STRING) + // We use a slightly different routine here since there can be no empty options + for (int i = 0; i < MAX_MENU_OPTIONS; i++) { - nBitsValidSlots |= (1 << i); + if (m_iszOption[i] != NULL_STRING) + { + if ( i == LAST_MENU_OPTION ) + { + // If there is a 0th option, fill up everything in between + for ( int j = nBitsValidSlots; j < LAST_MENU_OPTION; j++ ) + V_strncat( szString, " \n", sizeof( szString ) ); + } + else + { + // Corresponding slot does not matter + // Converted to bitmask farther below + nBitsValidSlots++; + } - V_strncat( szString, STRING( m_iszOption[i] ), sizeof( szString ) ); + V_strncat( szString, STRING( m_iszOption[i] ), sizeof( szString ) ); + V_strncat( szString, "\n", sizeof( szString ) ); + } } - else + + // Now make nBitsValidSlots reflect the number of options + int nNumCases = nBitsValidSlots; + nBitsValidSlots = 0; + + for ( int i = 0; i < nNumCases; i++ ) + nBitsValidSlots |= (1 << i); + + // Case 10 is a special case since it's normally used for canceling + if ( m_iszOption[LAST_MENU_OPTION] != NULL_STRING ) + nBitsValidSlots |= (1 << LAST_MENU_OPTION); + } + else + { + // Populate the options + for (int i = 0; i < MAX_MENU_OPTIONS; i++) { - // Insert space to tell menu code to skip - V_strncat( szString, " ", sizeof( szString ) ); - } + if (m_iszOption[i] != NULL_STRING) + { + nBitsValidSlots |= (1 << i); - // Insert newline even if there's no string - V_strncat( szString, "\n", sizeof( szString ) ); + V_strncat( szString, STRING( m_iszOption[i] ), sizeof( szString ) ); + } + else + { + // Insert space to tell menu code to skip + V_strncat( szString, " ", sizeof( szString ) ); + } + + // Insert newline even if there's no string + V_strncat( szString, "\n", sizeof( szString ) ); + } } if (nBitsValidSlots <= 0 && m_iszTitle == NULL_STRING) @@ -1032,10 +1084,14 @@ void CGameMenu::ShowMenu( CRecipientFilter &filter, float flDisplayTime ) return; } + // Menu flags are just spawnflags without ALLPLAYERS + int nMenuFlags = m_spawnflags >> 1; + UserMessageBegin( filter, "ShowMenuComplex" ); WRITE_WORD( nBitsValidSlots ); WRITE_FLOAT( flDisplayTime ); WRITE_BYTE( 0 ); + WRITE_BYTE( nMenuFlags ); WRITE_STRING( szString ); MessageEnd(); @@ -1102,6 +1158,24 @@ void CGameMenu::MenuSelected( int nSlot, CBaseEntity *pActivator ) return; } + if ( m_bSkipEmptyCases && nSlot != LAST_MENU_OPTION ) + { + // Ascertain true slot based on which valid one this corresponds to + int nNumValidSlots = 0; + for (int i = 0; i < MAX_MENU_OPTIONS; i++) + { + if (m_iszOption[i] != NULL_STRING) + { + nNumValidSlots++; + if ( nSlot == nNumValidSlots ) + { + nSlot = i+1; + break; + } + } + } + } + m_OnCase[nSlot-1].FireOutput( pActivator, this ); m_OutValue.Set( nSlot, pActivator, this ); diff --git a/sp/src/game/server/maprules.h b/sp/src/game/server/maprules.h index 38b9e6dc342..50e814b5723 100644 --- a/sp/src/game/server/maprules.h +++ b/sp/src/game/server/maprules.h @@ -11,8 +11,14 @@ #ifdef MAPBASE #define MAX_MENU_OPTIONS 10 +#define LAST_MENU_OPTION 9 -#define SF_GAMEMENU_ALLPLAYERS 0x0001 +#define SF_GAMEMENU_ALLPLAYERS (1 << 0) +#define SF_GAMEMENU_CENTERED (1 << 1) +#define SF_GAMEMENU_CENTER_FILL (1 << 2) +#define SF_GAMEMENU_BIG_FONTS (1 << 3) +#define SF_GAMEMENU_NO_FLASH (1 << 4) +#define SF_GAMEMENU_WORD_WRAP (1 << 5) //----------------------------------------------------------------------------- // Purpose: Displays a custom number menu for player(s) @@ -42,6 +48,17 @@ class CGameMenu : public CLogicalEntity, public IGameMenuAutoList void InputShowMenu( inputdata_t &inputdata ); void InputHideMenu( inputdata_t &inputdata ); + void InputSetCase01( inputdata_t &inputdata ) { m_iszOption[0] = inputdata.value.StringID(); } + void InputSetCase02( inputdata_t &inputdata ) { m_iszOption[1] = inputdata.value.StringID(); } + void InputSetCase03( inputdata_t &inputdata ) { m_iszOption[2] = inputdata.value.StringID(); } + void InputSetCase04( inputdata_t &inputdata ) { m_iszOption[3] = inputdata.value.StringID(); } + void InputSetCase05( inputdata_t &inputdata ) { m_iszOption[4] = inputdata.value.StringID(); } + void InputSetCase06( inputdata_t &inputdata ) { m_iszOption[5] = inputdata.value.StringID(); } + void InputSetCase07( inputdata_t &inputdata ) { m_iszOption[6] = inputdata.value.StringID(); } + void InputSetCase08( inputdata_t &inputdata ) { m_iszOption[7] = inputdata.value.StringID(); } + void InputSetCase09( inputdata_t &inputdata ) { m_iszOption[8] = inputdata.value.StringID(); } + void InputSetCase10( inputdata_t &inputdata ) { m_iszOption[9] = inputdata.value.StringID(); } + private: CUtlVector m_ActivePlayers; @@ -49,6 +66,8 @@ class CGameMenu : public CLogicalEntity, public IGameMenuAutoList float m_flDisplayTime; + bool m_bSkipEmptyCases; // Gaps between cases are filled in (e.g. if case02 is empty and case03 is not, then case03 acts like case02) + string_t m_iszTitle; string_t m_iszOption[MAX_MENU_OPTIONS];