//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Item pickup history displayed onscreen when items are picked up. // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "history_resource.h" #include "hud_macros.h" #include #include #include #include "iclientmode.h" #include "vgui_controls/AnimationController.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; extern ConVar hud_drawhistory_time; DECLARE_HUDELEMENT( CHudHistoryResource ); DECLARE_HUD_MESSAGE( CHudHistoryResource, ItemPickup ); DECLARE_HUD_MESSAGE( CHudHistoryResource, AmmoDenied ); //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CHudHistoryResource::CHudHistoryResource( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudHistoryResource" ) { vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); m_bDoNotDraw = true; m_wcsAmmoFullMsg[0] = 0; m_bNeedsDraw = false; SetHiddenBits( HIDEHUD_MISCSTATUS ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudHistoryResource::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); SetPaintBackgroundEnabled( false ); // lookup text to display for ammo full message wchar_t *wcs = g_pVGuiLocalize->Find("#hl2_AmmoFull"); if (wcs) { wcsncpy(m_wcsAmmoFullMsg, wcs, sizeof(m_wcsAmmoFullMsg) / sizeof(wchar_t)); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudHistoryResource::Init( void ) { HOOK_HUD_MESSAGE( CHudHistoryResource, ItemPickup ); HOOK_HUD_MESSAGE( CHudHistoryResource, AmmoDenied ); Reset(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudHistoryResource::Reset( void ) { m_PickupHistory.RemoveAll(); m_iCurrentHistorySlot = 0; m_bDoNotDraw = true; } //----------------------------------------------------------------------------- // Purpose: these kept only for hl1-port compatibility //----------------------------------------------------------------------------- void CHudHistoryResource::SetHistoryGap( int iNewHistoryGap ) { } //----------------------------------------------------------------------------- // Purpose: adds an element to the history //----------------------------------------------------------------------------- void CHudHistoryResource::AddToHistory( C_BaseCombatWeapon *weapon ) { // don't draw exhaustable weapons (grenades) since they'll have an ammo pickup icon as well if ( weapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE ) return; int iId = weapon->entindex(); // don't show the same weapon twice for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].iId == iId ) { // it's already in list return; } } AddIconToHistory( HISTSLOT_WEAP, iId, weapon, 0, NULL ); } //----------------------------------------------------------------------------- // Purpose: Add a new entry to the pickup history //----------------------------------------------------------------------------- void CHudHistoryResource::AddToHistory( int iType, int iId, int iCount ) { // Ignore adds with no count if ( iType == HISTSLOT_AMMO ) { if ( !iCount ) return; #if defined( CSTRIKE_DLL ) // don't leave blank gaps for ammo we're not going to display const FileWeaponInfo_t *pWpnInfo = gWR.GetWeaponFromAmmo( iId ); if ( pWpnInfo && ( pWpnInfo->iMaxClip1 >= 0 || pWpnInfo->iMaxClip2 >= 0 ) ) { if ( !pWpnInfo->iconSmall ) return; } #endif // clear out any ammo pickup denied icons, since we can obviously pickup again for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].type == HISTSLOT_AMMODENIED && m_PickupHistory[i].iId == iId ) { // kill the old entry m_PickupHistory[i].DisplayTime = 0.0f; // change the pickup to be in this entry m_iCurrentHistorySlot = i; break; } } } AddIconToHistory( iType, iId, NULL, iCount, NULL ); } //----------------------------------------------------------------------------- // Purpose: Add a new entry to the pickup history //----------------------------------------------------------------------------- void CHudHistoryResource::AddToHistory( int iType, const char *szName, int iCount ) { if ( iType != HISTSLOT_ITEM ) return; // Get the item's icon CHudTexture *i = gHUD.GetIcon( szName ); if ( i == NULL ) return; AddIconToHistory( iType, 1, NULL, iCount, i ); } //----------------------------------------------------------------------------- // Purpose: adds a history icon //----------------------------------------------------------------------------- void CHudHistoryResource::AddIconToHistory( int iType, int iId, C_BaseCombatWeapon *weapon, int iCount, CHudTexture *icon ) { m_bNeedsDraw = true; // Check to see if the pic would have to be drawn too high. If so, start again from the bottom if ( (m_flHistoryGap * (m_iCurrentHistorySlot+1)) > GetTall() ) { m_iCurrentHistorySlot = 0; } // If the history resource is appearing, slide the hint message element down if ( m_iCurrentHistorySlot == 0 ) { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HintMessageLower" ); } // ensure the size m_PickupHistory.EnsureCount(m_iCurrentHistorySlot + 1); // default to just writing to the first slot HIST_ITEM *freeslot = &m_PickupHistory[m_iCurrentHistorySlot]; if ( iType == HISTSLOT_AMMODENIED && freeslot->DisplayTime ) { // don't override existing pickup icons with denied icons return; } freeslot->iId = iId; freeslot->icon = icon; freeslot->type = iType; freeslot->m_hWeapon = weapon; freeslot->iCount = iCount; if (iType == HISTSLOT_AMMODENIED) { freeslot->DisplayTime = gpGlobals->curtime + (hud_drawhistory_time.GetFloat() / 2.0f); } else { freeslot->DisplayTime = gpGlobals->curtime + hud_drawhistory_time.GetFloat(); } ++m_iCurrentHistorySlot; } //----------------------------------------------------------------------------- // Purpose: Handle an item pickup event from the server //----------------------------------------------------------------------------- void CHudHistoryResource::MsgFunc_ItemPickup( bf_read &msg ) { char szName[1024]; msg.ReadString( szName, sizeof(szName) ); // Add the item to the history AddToHistory( HISTSLOT_ITEM, szName ); } //----------------------------------------------------------------------------- // Purpose: ammo denied message //----------------------------------------------------------------------------- void CHudHistoryResource::MsgFunc_AmmoDenied( bf_read &msg ) { int iAmmo = msg.ReadShort(); // see if there are any existing ammo items of that type for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].type == HISTSLOT_AMMO && m_PickupHistory[i].iId == iAmmo ) { // it's already in the list as a pickup, ignore return; } } // see if there are any denied ammo icons, if so refresh their timer for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].type == HISTSLOT_AMMODENIED && m_PickupHistory[i].iId == iAmmo ) { // it's already in the list, refresh m_PickupHistory[i].DisplayTime = gpGlobals->curtime + (hud_drawhistory_time.GetFloat() / 2.0f); m_bNeedsDraw = true; return; } } // add into the list AddToHistory( HISTSLOT_AMMODENIED, iAmmo, 0 ); } //----------------------------------------------------------------------------- // Purpose: If there aren't any items in the history, clear it out. //----------------------------------------------------------------------------- void CHudHistoryResource::CheckClearHistory( void ) { for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].type ) return; } m_iCurrentHistorySlot = 0; // Slide the hint message element back up g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HintMessageRaise" ); } //----------------------------------------------------------------------------- // Purpose: Save CPU cycles by letting the HUD system early cull // costly traversal. Called per frame, return true if thinking and // painting need to occur. //----------------------------------------------------------------------------- bool CHudHistoryResource::ShouldDraw( void ) { #ifdef TF_CLIENT_DLL return false; #else return ( ( m_iCurrentHistorySlot > 0 || m_bNeedsDraw ) && CHudElement::ShouldDraw() ); #endif } //----------------------------------------------------------------------------- // Purpose: Draw the pickup history //----------------------------------------------------------------------------- void CHudHistoryResource::Paint( void ) { if ( m_bDoNotDraw ) { // this is to not draw things until the first rendered m_bDoNotDraw = false; return; } // set when drawing should occur // will be set if valid drawing does occur m_bNeedsDraw = false; int wide, tall; GetSize( wide, tall ); for ( int i = 0; i < m_PickupHistory.Count(); i++ ) { if ( m_PickupHistory[i].type ) { m_PickupHistory[i].DisplayTime = MIN( m_PickupHistory[i].DisplayTime, gpGlobals->curtime + hud_drawhistory_time.GetFloat() ); if ( m_PickupHistory[i].DisplayTime <= gpGlobals->curtime ) { // pic drawing time has expired memset( &m_PickupHistory[i], 0, sizeof(HIST_ITEM) ); CheckClearHistory(); continue; } float elapsed = m_PickupHistory[i].DisplayTime - gpGlobals->curtime; float scale = elapsed * 80; Color clr = gHUD.m_clrNormal; clr[3] = MIN( scale, 255 ); bool bUseAmmoFullMsg = false; // get the icon and number to draw const CHudTexture *itemIcon = NULL; const CHudTexture *itemAmmoIcon = NULL; int iAmount = 0; bool bHalfHeight = true; switch ( m_PickupHistory[i].type ) { case HISTSLOT_AMMO: { // Get the weapon we belong to #ifndef HL2MP const FileWeaponInfo_t *pWpnInfo = gWR.GetWeaponFromAmmo( m_PickupHistory[i].iId ); if ( pWpnInfo && ( pWpnInfo->iMaxClip1 >= 0 || pWpnInfo->iMaxClip2 >= 0 ) ) { // The weapon will be the main icon, and the ammo the smaller itemIcon = pWpnInfo->iconSmall; itemAmmoIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId ); } else #endif // HL2MP { itemIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId ); itemAmmoIcon = NULL; } #ifdef CSTRIKE_DLL // show grenades as the weapon icon if ( pWpnInfo && pWpnInfo->iFlags & ITEM_FLAG_EXHAUSTIBLE ) { itemIcon = pWpnInfo->iconActive; itemAmmoIcon = NULL; bHalfHeight = false; } #endif iAmount = m_PickupHistory[i].iCount; } break; case HISTSLOT_AMMODENIED: { itemIcon = gWR.GetAmmoIconFromWeapon( m_PickupHistory[i].iId ); iAmount = 0; bUseAmmoFullMsg = true; // display as red clr = gHUD.m_clrCaution; clr[3] = MIN( scale, 255 ); } break; case HISTSLOT_WEAP: { C_BaseCombatWeapon *pWeapon = m_PickupHistory[i].m_hWeapon; if ( !pWeapon ) return; if ( !pWeapon->HasAmmo() ) { // if the weapon doesn't have ammo, display it as red clr = gHUD.m_clrCaution; clr[3] = MIN( scale, 255 ); } itemIcon = pWeapon->GetSpriteInactive(); bHalfHeight = false; } break; case HISTSLOT_ITEM: { if ( !m_PickupHistory[i].iId ) continue; itemIcon = m_PickupHistory[i].icon; bHalfHeight = false; } break; default: // unknown history type Assert( 0 ); break; } if ( !itemIcon ) continue; if ( clr[3] ) { // valid drawing will occur m_bNeedsDraw = true; } int ypos = tall - (m_flHistoryGap * (i + 1)); int xpos = wide - itemIcon->Width() - m_flIconInset; #ifndef HL2MP // Adjust for a half-height icon if ( bHalfHeight ) { ypos += itemIcon->Height() / 2; } #endif // HL2MP itemIcon->DrawSelf( xpos, ypos, clr ); if ( itemAmmoIcon ) { itemAmmoIcon->DrawSelf( xpos - ( itemAmmoIcon->Width() * 1.25f ), ypos, clr ); } if ( iAmount ) { wchar_t text[16]; _snwprintf( text, sizeof( text ) / sizeof(wchar_t), L"%i", m_PickupHistory[i].iCount ); // offset the number to sit properly next to the icon ypos -= ( surface()->GetFontTall( m_hNumberFont ) - itemIcon->Height() ) / 2; vgui::surface()->DrawSetTextFont( m_hNumberFont ); vgui::surface()->DrawSetTextColor( clr ); vgui::surface()->DrawSetTextPos( wide - m_flTextInset, ypos ); vgui::surface()->DrawUnicodeString( text ); } else if ( bUseAmmoFullMsg ) { // offset the number to sit properly next to the icon ypos -= ( surface()->GetFontTall( m_hTextFont ) - itemIcon->Height() ) / 2; vgui::surface()->DrawSetTextFont( m_hTextFont ); vgui::surface()->DrawSetTextColor( clr ); vgui::surface()->DrawSetTextPos( wide - m_flTextInset, ypos ); vgui::surface()->DrawUnicodeString( m_wcsAmmoFullMsg ); } } } }