/*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ // // Ammo.cpp // // implementation of CHudAmmo class // #include "extdll.h" #include "utils.h" #include "hud.h" #include "hud_ammohistory.h" #include "pm_shared.h" WEAPON *gpActiveSel; // NULL means off, 1 means just the menu bar, otherwise WEAPON *gpLastSel; // Last weapon menu selection client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ); WeaponsResource gWR; int g_weaponselect = 0; void WeaponsResource :: LoadAllWeaponSprites( void ) { for( int i = 0; i < MAX_WEAPONS; i++ ) { if ( rgWeapons[i].iId ) LoadWeaponSprites( &rgWeapons[i] ); } } int WeaponsResource :: CountAmmo( int iId ) { if ( iId < 0 ) return 0; return riAmmo[iId]; } int WeaponsResource :: HasAmmo( WEAPON *p ) { if( !p ) return FALSE; // weapons with no max ammo can always be selected if( p->iMax1 == -1 ) return TRUE; return (p->iAmmoType == -1) || p->iClip > 0 || CountAmmo(p->iAmmoType) || CountAmmo(p->iAmmo2Type) || ( p->iFlags & WEAPON_FLAGS_SELECTONEMPTY ); } void WeaponsResource :: LoadWeaponSprites( WEAPON *pWeapon ) { int i, iRes; if (ActualWidth < 640) iRes = 320; else iRes = 640; char sz[128]; if ( !pWeapon ) return; memset( &pWeapon->rcActive, 0, sizeof( wrect_t )); memset( &pWeapon->rcInactive, 0, sizeof( wrect_t )); memset( &pWeapon->rcAmmo, 0, sizeof( wrect_t )); memset( &pWeapon->rcAmmo2, 0, sizeof( wrect_t )); pWeapon->hInactive = 0; pWeapon->hActive = 0; pWeapon->hAmmo = 0; pWeapon->hAmmo2 = 0; sprintf( sz, "sprites/%s.txt", pWeapon->szName ); client_sprite_t *pList = SPR_GetList( sz, &i ); if( !pList ) return; client_sprite_t *p; p = GetSpriteList( pList, "crosshair", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hCrosshair = SPR_Load(sz); pWeapon->rcCrosshair = p->rc; } else pWeapon->hCrosshair = 0; p = GetSpriteList( pList, "autoaim", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hAutoaim = SPR_Load(sz); pWeapon->rcAutoaim = p->rc; } else pWeapon->hAutoaim = 0; p = GetSpriteList( pList, "zoom", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hZoomedCrosshair = SPR_Load(sz); pWeapon->rcZoomedCrosshair = p->rc; } else { pWeapon->hZoomedCrosshair = pWeapon->hCrosshair; // default to non-zoomed crosshair pWeapon->rcZoomedCrosshair = pWeapon->rcCrosshair; } p = GetSpriteList( pList, "zoom_autoaim", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hZoomedAutoaim = SPR_Load(sz); pWeapon->rcZoomedAutoaim = p->rc; } else { pWeapon->hZoomedAutoaim = pWeapon->hZoomedCrosshair; // default to zoomed crosshair pWeapon->rcZoomedAutoaim = pWeapon->rcZoomedCrosshair; } p = GetSpriteList( pList, "weapon", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hInactive = SPR_Load(sz); pWeapon->rcInactive = p->rc; gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } else { pWeapon->hInactive = gHUD.m_hHudError; pWeapon->rcInactive = gHUD.GetSpriteRect( gHUD.m_HUD_error ); gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } p = GetSpriteList( pList, "weapon_s", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hActive = SPR_Load(sz); pWeapon->rcActive = p->rc; } else { pWeapon->hActive = gHUD.m_hHudError; pWeapon->rcActive = gHUD.GetSpriteRect( gHUD.m_HUD_error ); } p = GetSpriteList( pList, "ammo", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hAmmo = SPR_Load(sz); pWeapon->rcAmmo = p->rc; gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } else pWeapon->hAmmo = 0; p = GetSpriteList( pList, "ammo2", iRes, i ); if( p ) { sprintf(sz, "sprites/%s.spr", p->szSprite); pWeapon->hAmmo2 = SPR_Load(sz); pWeapon->rcAmmo2 = p->rc; gHR.iHistoryGap = max( gHR.iHistoryGap, pWeapon->rcActive.bottom - pWeapon->rcActive.top ); } else pWeapon->hAmmo2 = 0; // in original Half-Life this was a leak FREE( pList ); } // Returns the first weapon for a given slot. WEAPON *WeaponsResource :: GetFirstPos( int iSlot ) { WEAPON *pret = NULL; for (int i = 0; i < MAX_WEAPON_POSITIONS; i++) { if( rgSlots[iSlot][i] && HasAmmo( rgSlots[iSlot][i] )) { pret = rgSlots[iSlot][i]; break; } } return pret; } WEAPON* WeaponsResource :: GetNextActivePos( int iSlot, int iSlotPos ) { if ( iSlotPos >= MAX_WEAPON_POSITIONS || iSlot >= MAX_WEAPON_SLOTS ) return NULL; WEAPON *p = gWR.rgSlots[ iSlot ][ iSlotPos+1 ]; if ( !p || !gWR.HasAmmo(p) ) return GetNextActivePos( iSlot, iSlotPos + 1 ); return p; } int giBucketHeight, giBucketWidth, giABHeight, giABWidth; // Ammo Bar width and height HSPRITE ghsprBuckets; // Sprite for top row of weapons menu DECLARE_MESSAGE( m_Ammo, CurWeapon ); // Current weapon and clip DECLARE_MESSAGE( m_Ammo, WeaponList ); // new weapon type DECLARE_MESSAGE( m_Ammo, AmmoX ); // update known ammo type's count DECLARE_MESSAGE( m_Ammo, AmmoPickup ); // flashes an ammo pickup record DECLARE_MESSAGE( m_Ammo, WeapPickup ); // flashes a weapon pickup record DECLARE_MESSAGE( m_Ammo, HideWeapon ); // hides the weapon, ammo, and crosshair displays temporarily DECLARE_MESSAGE( m_Ammo, ItemPickup ); DECLARE_MESSAGE( m_AmmoSecondary, SecAmmoVal ); DECLARE_MESSAGE( m_AmmoSecondary, SecAmmoIcon ); DECLARE_COMMAND( m_Ammo, Slot1 ); DECLARE_COMMAND( m_Ammo, Slot2 ); DECLARE_COMMAND( m_Ammo, Slot3 ); DECLARE_COMMAND( m_Ammo, Slot4 ); DECLARE_COMMAND( m_Ammo, Slot5 ); DECLARE_COMMAND( m_Ammo, Slot6 ); DECLARE_COMMAND( m_Ammo, Slot7 ); DECLARE_COMMAND( m_Ammo, Slot8 ); DECLARE_COMMAND( m_Ammo, Slot9 ); DECLARE_COMMAND( m_Ammo, Slot10 ); DECLARE_COMMAND( m_Ammo, Close ); DECLARE_COMMAND( m_Ammo, NextWeapon ); DECLARE_COMMAND( m_Ammo, PrevWeapon ); // width of ammo fonts #define AMMO_SMALL_WIDTH 10 #define AMMO_LARGE_WIDTH 20 #define HISTORY_DRAW_TIME "5" int CHudAmmo::Init( void ) { gHUD.AddHudElem(this); HOOK_MESSAGE( CurWeapon ); HOOK_MESSAGE( WeaponList ); HOOK_MESSAGE( AmmoPickup ); HOOK_MESSAGE( WeapPickup ); HOOK_MESSAGE( ItemPickup ); HOOK_MESSAGE( HideWeapon ); HOOK_MESSAGE( AmmoX ); HOOK_COMMAND( "slot1", Slot1 ); HOOK_COMMAND( "slot2", Slot2 ); HOOK_COMMAND( "slot3", Slot3 ); HOOK_COMMAND( "slot4", Slot4 ); HOOK_COMMAND( "slot5", Slot5 ); HOOK_COMMAND( "slot6", Slot6 ); HOOK_COMMAND( "slot7", Slot7 ); HOOK_COMMAND( "slot8", Slot8 ); HOOK_COMMAND( "slot9", Slot9 ); HOOK_COMMAND( "slot10", Slot10 ); HOOK_COMMAND( "cancelselect", Close ); HOOK_COMMAND( "invnext", NextWeapon ); HOOK_COMMAND( "invprev", PrevWeapon ); Reset(); CVAR_REGISTER( "hud_drawhistory_time", HISTORY_DRAW_TIME, 0, "weapons pickup history show time" ); CVAR_REGISTER( "hud_fastswitch", "0", FCVAR_ARCHIVE, "controls whether or not weapons can be selected in one keypress" ); m_iFlags |= HUD_ACTIVE; //!!! gWR.Init(); gHR.Init(); return 1; } void CHudAmmo :: Reset( void ) { m_fFade = 0; m_iFlags |= HUD_ACTIVE; //!!! gpActiveSel = NULL; gHUD.m_iHideHUDDisplay = 0; gWR.Reset(); gHR.Reset(); static wrect_t nullrc; SetCrosshair( 0, nullrc, 0, 0, 0 ); // reset crosshair m_pWeapon = NULL;// reset last weapon } int CHudAmmo :: VidInit( void ) { // Load sprites for buckets (top row of weapon menu) m_HUD_bucket0 = gHUD.GetSpriteIndex( "bucket1" ); m_HUD_selection = gHUD.GetSpriteIndex( "selection" ); ghsprBuckets = gHUD.GetSprite(m_HUD_bucket0); giBucketWidth = gHUD.GetSpriteRect(m_HUD_bucket0).right - gHUD.GetSpriteRect(m_HUD_bucket0).left; giBucketHeight = gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top; gHR.iHistoryGap = max( gHR.iHistoryGap, gHUD.GetSpriteRect(m_HUD_bucket0).bottom - gHUD.GetSpriteRect(m_HUD_bucket0).top); // If we've already loaded weapons, let's get new sprites gWR.LoadAllWeaponSprites(); giABWidth = 20; giABHeight = 4; return 1; } // // Think: // Used for selection of weapon menu item. // void CHudAmmo :: Think( void ) { if( gHUD.m_fPlayerDead ) return; if( gHUD.m_iWeaponBits != gWR.iOldWeaponBits ) { gWR.iOldWeaponBits = gHUD.m_iWeaponBits; for (int i = MAX_WEAPONS-1; i > 0; i-- ) { WEAPON *p = gWR.GetWeapon(i); if( p ) { if( gHUD.m_iWeaponBits & ( 1 << p->iId )) gWR.PickupWeapon( p ); else gWR.DropWeapon( p ); } } } if( Q_rint( gHUD.m_flFOV ) != gWR.iOldFOV ) { if( m_pWeapon ) { // update crosshairs if( gHUD.m_flFOV >= 90 ) { // normal crosshairs if( m_pWeapon->iOnTarget && m_pWeapon->hAutoaim ) SetCrosshair(m_pWeapon->hAutoaim, m_pWeapon->rcAutoaim, 255, 255, 255 ); else SetCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 ); } else { // zoomed crosshairs if( m_pWeapon->iOnTarget && m_pWeapon->hZoomedAutoaim ) SetCrosshair(m_pWeapon->hZoomedAutoaim, m_pWeapon->rcZoomedAutoaim, 255, 255, 255 ); else SetCrosshair( m_pWeapon->hZoomedCrosshair, m_pWeapon->rcZoomedCrosshair, 255, 255, 255 ); } } gWR.iOldFOV = gHUD.m_flFOV; } if( !gpActiveSel ) return; // has the player selected one? if( gHUD.m_iKeyBits & IN_ATTACK ) { if( gpActiveSel != (WEAPON *)1 ) { SERVER_COMMAND( gpActiveSel->szName ); g_weaponselect = gpActiveSel->iId; } gpLastSel = gpActiveSel; gpActiveSel = NULL; gHUD.m_iKeyBits &= ~IN_ATTACK; CL_PlaySound( "common/wpn_select.wav", 1.0f ); } } // // Helper function to return a Ammo pointer from id // HSPRITE* WeaponsResource :: GetAmmoPicFromWeapon( int iAmmoId, wrect_t& rect ) { for ( int i = 0; i < MAX_WEAPONS; i++ ) { if ( rgWeapons[i].iAmmoType == iAmmoId ) { rect = rgWeapons[i].rcAmmo; return &rgWeapons[i].hAmmo; } else if ( rgWeapons[i].iAmmo2Type == iAmmoId ) { rect = rgWeapons[i].rcAmmo2; return &rgWeapons[i].hAmmo2; } } return NULL; } // Menu Selection Code void WeaponsResource :: SelectSlot( int iSlot, int fAdvance, int iDirection ) { if( gHUD.m_Menu.m_fMenuDisplayed && (fAdvance == FALSE) && (iDirection == 1) ) { // menu is overriding slot use commands gHUD.m_Menu.SelectMenuItem( iSlot + 1 ); // slots are one off the key numbers return; } if( iSlot > MAX_WEAPON_SLOTS ) return; if( gHUD.m_fPlayerDead || gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS|HIDEHUD_ALL )) return; if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return; if(!(gHUD.m_iWeaponBits & ~(1<<(WEAPON_SUIT)))) return; WEAPON *p = NULL; bool fastSwitch = CVAR_GET_FLOAT( "hud_fastswitch" ) != 0; if((gpActiveSel == NULL) || (gpActiveSel == (WEAPON *)1) || (iSlot != gpActiveSel->iSlot)) { CL_PlaySound( "common/wpn_hudon.wav", 1.0f ); p = GetFirstPos( iSlot ); if( p && fastSwitch ) // check for fast weapon switch mode { // if fast weapon switch is on, then weapons can be selected in a single keypress // but only if there is only one item in the bucket WEAPON *p2 = GetNextActivePos( p->iSlot, p->iSlotPos ); if( !p2 ) { // only one active item in bucket, so change directly to weapon SERVER_COMMAND( p->szName ); g_weaponselect = p->iId; return; } } } else { CL_PlaySound( "common/wpn_moveselect.wav", 1.0f ); if( gpActiveSel ) p = GetNextActivePos( gpActiveSel->iSlot, gpActiveSel->iSlotPos ); if( !p ) p = GetFirstPos( iSlot ); } if( !p ) // no selection found { // just display the weapon list, unless fastswitch is on just ignore it if( !fastSwitch ) gpActiveSel = (WEAPON *)1; else gpActiveSel = NULL; } else gpActiveSel = p; } //------------------------------------------------------------------------ // Message Handlers //------------------------------------------------------------------------ // // AmmoX -- Update the count of a known type of ammo // int CHudAmmo::MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf) { BEGIN_READ( pszName, iSize, pbuf ); int iIndex = READ_BYTE(); int iCount = READ_BYTE(); gWR.SetAmmo( iIndex, abs(iCount) ); return 1; } int CHudAmmo::MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); int iIndex = READ_BYTE(); int iCount = READ_BYTE(); // Add ammo to the history gHR.AddToHistory( HISTSLOT_AMMO, iIndex, abs(iCount) ); END_READ(); return 1; } int CHudAmmo::MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); int iIndex = READ_BYTE(); // Add the weapon to the history gHR.AddToHistory( HISTSLOT_WEAP, iIndex ); END_READ(); return 1; } int CHudAmmo::MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); const char *szName = READ_STRING(); // Add the weapon to the history gHR.AddToHistory( HISTSLOT_ITEM, szName ); END_READ(); return 1; } int CHudAmmo::MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); gHUD.m_iHideHUDDisplay = READ_BYTE(); if ( IsSpectateOnly()) return 1; if((m_pWeapon == NULL) || (gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ))) { static wrect_t nullrc; gpActiveSel = NULL; SetCrosshair( 0, nullrc, 0, 0, 0 ); } else { SetCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 ); } END_READ(); return 1; } // // CurWeapon: Update hud state with the current weapon and clip count. Ammo // counts are updated with AmmoX. Server assures that the Weapon ammo type // numbers match a real ammo type. // int CHudAmmo::MsgFunc_CurWeapon( const char *pszName, int iSize, void *pbuf ) { static wrect_t nullrc; int fOnTarget = FALSE; BEGIN_READ( pszName, iSize, pbuf ); int iState = READ_BYTE(); int iId = READ_CHAR(); int iClip = READ_CHAR(); // detect if we're also on target if( iState > 1 ) { fOnTarget = TRUE; } if( iId < 1 ) { SetCrosshair(0, nullrc, 0, 0, 0); m_pWeapon = NULL; return 0; } // Is player dead??? if((iId == -1) && (iClip == -1)) { gHUD.m_fPlayerDead = TRUE; gpActiveSel = NULL; return 1; } gHUD.m_fPlayerDead = FALSE; WEAPON *pWeapon = gWR.GetWeapon( iId ); if( !pWeapon ) return 0; if( iClip < -1 ) pWeapon->iClip = abs( iClip ); else pWeapon->iClip = iClip; if( iState == 0 ) // we're not the current weapon, so update no more return 1; m_pWeapon = pWeapon; m_pWeapon->iOnTarget = fOnTarget; // update crosshairs if( gHUD.m_flFOV >= 90 ) { // normal crosshairs if( m_pWeapon->iOnTarget && m_pWeapon->hAutoaim ) SetCrosshair(m_pWeapon->hAutoaim, m_pWeapon->rcAutoaim, 255, 255, 255 ); else SetCrosshair( m_pWeapon->hCrosshair, m_pWeapon->rcCrosshair, 255, 255, 255 ); } else { // zoomed crosshairs if( m_pWeapon->iOnTarget && m_pWeapon->hZoomedAutoaim ) SetCrosshair(m_pWeapon->hZoomedAutoaim, m_pWeapon->rcZoomedAutoaim, 255, 255, 255 ); else SetCrosshair( m_pWeapon->hZoomedCrosshair, m_pWeapon->rcZoomedCrosshair, 255, 255, 255 ); } m_fFade = 200.0f; //!!! m_iFlags |= HUD_ACTIVE; END_READ(); return 1; } // // WeaponList -- Tells the hud about a new weapon type. // int CHudAmmo::MsgFunc_WeaponList( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); WEAPON Weapon; strcpy( Weapon.szName, READ_STRING() ); Weapon.iAmmoType = (int)READ_CHAR(); Weapon.iMax1 = READ_BYTE(); if( Weapon.iMax1 == 255 ) Weapon.iMax1 = -1; Weapon.iAmmo2Type = READ_CHAR(); Weapon.iMax2 = READ_BYTE(); if( Weapon.iMax2 == 255 ) Weapon.iMax2 = -1; Weapon.iSlot = READ_CHAR(); Weapon.iSlotPos = READ_CHAR(); Weapon.iId = READ_CHAR(); Weapon.iFlags = READ_BYTE(); Weapon.iClip = 0; gWR.AddWeapon( &Weapon ); END_READ(); return 1; } //------------------------------------------------------------------------ // Command Handlers //------------------------------------------------------------------------ // Slot button pressed void CHudAmmo::SlotInput( int iSlot ) { gWR.SelectSlot( iSlot, FALSE, 1 ); } void CHudAmmo::UserCmd_Slot1( void ) { SlotInput( 0 ); } void CHudAmmo::UserCmd_Slot2( void ) { SlotInput( 1 ); } void CHudAmmo::UserCmd_Slot3( void ) { SlotInput( 2 ); } void CHudAmmo::UserCmd_Slot4( void ) { SlotInput( 3 ); } void CHudAmmo::UserCmd_Slot5( void ) { SlotInput( 4 ); } void CHudAmmo::UserCmd_Slot6( void ) { SlotInput( 5 ); } void CHudAmmo::UserCmd_Slot7( void ) { SlotInput( 6 ); } void CHudAmmo::UserCmd_Slot8( void ) { SlotInput( 7 ); } void CHudAmmo::UserCmd_Slot9( void ) { SlotInput( 8 ); } void CHudAmmo::UserCmd_Slot10( void ) { SlotInput( 9 ); } void CHudAmmo::UserCmd_Close( void ) { if( gpActiveSel ) { gpLastSel = gpActiveSel; gpActiveSel = NULL; CL_PlaySound( "common/wpn_hudoff.wav", 1.0f ); } else CLIENT_COMMAND( "escape\n" ); } // Selects the next item in the weapon menu void CHudAmmo::UserCmd_NextWeapon( void ) { if( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS|HIDEHUD_ALL))) return; if( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) gpActiveSel = m_pWeapon; int pos = 0; int slot = 0; if( gpActiveSel ) { pos = gpActiveSel->iSlotPos + 1; slot = gpActiveSel->iSlot; } for( int loop = 0; loop <= 1; loop++ ) { for( ; slot < MAX_WEAPON_SLOTS; slot++ ) { for( ; pos < MAX_WEAPON_POSITIONS; pos++ ) { WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); if( wsp && gWR.HasAmmo( wsp )) { gpActiveSel = wsp; return; } } pos = 0; } slot = 0; // start looking from the first slot again } gpActiveSel = NULL; } // Selects the previous item in the menu void CHudAmmo :: UserCmd_PrevWeapon( void ) { if( gHUD.m_fPlayerDead || (gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS|HIDEHUD_ALL))) return; if( !gpActiveSel || gpActiveSel == (WEAPON*)1 ) gpActiveSel = m_pWeapon; int pos = MAX_WEAPON_POSITIONS-1; int slot = MAX_WEAPON_SLOTS-1; if( gpActiveSel ) { pos = gpActiveSel->iSlotPos - 1; slot = gpActiveSel->iSlot; } for( int loop = 0; loop <= 1; loop++ ) { for( ; slot >= 0; slot-- ) { for( ; pos >= 0; pos-- ) { WEAPON *wsp = gWR.GetWeaponSlot( slot, pos ); if( wsp && gWR.HasAmmo( wsp )) { gpActiveSel = wsp; return; } } pos = MAX_WEAPON_POSITIONS-1; } slot = MAX_WEAPON_SLOTS-1; } gpActiveSel = NULL; } //------------------------------------------------------------------------- // Drawing code //------------------------------------------------------------------------- int CHudAmmo::Draw( float flTime ) { int a, x, y, r, g, b; int AmmoWidth; if(!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)))) return 1; if((gHUD.m_iHideHUDDisplay & (HIDEHUD_WEAPONS|HIDEHUD_ALL))) return 1; // Draw Weapon Menu DrawWList( flTime ); // Draw ammo pickup history gHR.DrawAmmoHistory( flTime ); if(!( m_iFlags & HUD_ACTIVE )) return 0; if( !m_pWeapon ) { return 0; } WEAPON *pw = m_pWeapon; // shorthand // SPR_Draw Ammo if((pw->iAmmoType < 0) && (pw->iAmmo2Type < 0)) return 0; int iFlags = DHN_DRAWZERO; // draw 0 values AmmoWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left; a = (int) max( MIN_ALPHA, m_fFade ); if( m_fFade > 0 ) m_fFade -= (gHUD.m_flTimeDelta * 20); UnpackRGB( r,g,b, gHUD.m_iHUDColor ); ScaleColors(r, g, b, a ); // Does this weapon have a clip? y = ScreenHeight - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2; // Does weapon have any ammo at all? if( m_pWeapon->iAmmoType > 0 ) { int iIconWidth = m_pWeapon->rcAmmo.right - m_pWeapon->rcAmmo.left; if( pw->iClip >= 0 ) { // room for the number and the '|' and the current ammo x = ScreenWidth - (8 * AmmoWidth) - iIconWidth; x = gHUD.DrawHudNumber(x, y, iFlags|DHN_3DIGITS, pw->iClip, r, g, b ); wrect_t rc; rc.top = 0; rc.left = 0; rc.right = AmmoWidth; rc.bottom = 100; int iBarWidth = AmmoWidth/10; x += AmmoWidth/2; UnpackRGB(r,g,b, gHUD.m_iHUDColor); // draw the | bar FillRGBA( x, y, iBarWidth, gHUD.m_iFontHeight, r, g, b, a ); x += iBarWidth + AmmoWidth / 2; // GL Seems to need this ScaleColors( r, g, b, a ); x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); } else { // SPR_Draw a bullets only line x = ScreenWidth - 4 * AmmoWidth - iIconWidth; x = gHUD.DrawHudNumber(x, y, iFlags | DHN_3DIGITS, gWR.CountAmmo(pw->iAmmoType), r, g, b); } // Draw the ammo Icon int iOffset = (m_pWeapon->rcAmmo.bottom - m_pWeapon->rcAmmo.top) / 8; SPR_Set( m_pWeapon->hAmmo, r, g, b ); SPR_DrawAdditive( 0, x, y - iOffset, &m_pWeapon->rcAmmo ); } // Does weapon have secondary ammo? if( pw->iAmmo2Type > 0 ) { int iIconWidth = m_pWeapon->rcAmmo2.right - m_pWeapon->rcAmmo2.left; // Do we have secondary ammo? if((pw->iAmmo2Type != 0) && (gWR.CountAmmo(pw->iAmmo2Type) > 0)) { y -= gHUD.m_iFontHeight + gHUD.m_iFontHeight/4; x = ScreenWidth - 4 * AmmoWidth - iIconWidth; x = gHUD.DrawHudNumber(x, y, iFlags|DHN_3DIGITS, gWR.CountAmmo(pw->iAmmo2Type), r, g, b); // Draw the ammo Icon SPR_Set(m_pWeapon->hAmmo2, r, g, b); int iOffset = (m_pWeapon->rcAmmo2.bottom - m_pWeapon->rcAmmo2.top)/8; SPR_DrawAdditive(0, x, y - iOffset, &m_pWeapon->rcAmmo2); } } return 1; } // // Draws the ammo bar on the hud // int DrawBar( int x, int y, int width, int height, float f ) { int r, g, b; if( f < 0 ) f = 0; if( f > 1 ) f = 1; if( f ) { int w = f * width; // Always show at least one pixel if we have ammo. if( w <= 0 ) w = 1; UnpackRGB( r, g, b, RGB_GREENISH ); FillRGBA( x, y, w, height, r, g, b, 255 ); x += w; width -= w; } UnpackRGB( r, g, b, gHUD.m_iHUDColor ); FillRGBA( x, y, width, height, r, g, b, 128 ); return (x + width); } void DrawAmmoBar( WEAPON *p, int x, int y, int width, int height ) { if( !p ) return; if( p->iAmmoType != -1 ) { if( !gWR.CountAmmo( p->iAmmoType )) return; float f = (float)gWR.CountAmmo(p->iAmmoType)/(float)p->iMax1; x = DrawBar(x, y, width, height, f); // Do we have secondary ammo too? if( p->iAmmo2Type != -1 ) { f = (float)gWR.CountAmmo(p->iAmmo2Type) / (float)p->iMax2; x += 5; //!!! DrawBar( x, y, width, height, f ); } } } // // Draw Weapon Menu // int CHudAmmo::DrawWList( float flTime ) { int r,g,b,x,y,a,i; if( !gpActiveSel ) return 0; int iActiveSlot; if( gpActiveSel == (WEAPON *)1 ) iActiveSlot = -1; // current slot has no weapons else iActiveSlot = gpActiveSel->iSlot; x = 10; //!!! y = 10; //!!! // Ensure that there are available choices in the active slot if( iActiveSlot > 0 ) { if( !gWR.GetFirstPos( iActiveSlot ) ) { gpActiveSel = (WEAPON *)1; iActiveSlot = -1; } } // Draw top line for( i = 0; i < MAX_WEAPON_SLOTS; i++ ) { int iWidth; UnpackRGB( r,g,b, gHUD.m_iHUDColor ); if( iActiveSlot == i ) a = 255; else a = 192; // not used ??? ScaleColors( r, g, b, 255 ); SPR_Set( gHUD.GetSprite( m_HUD_bucket0 + i ), r, g, b ); // make active slot wide enough to accomodate gun pictures if( i == iActiveSlot ) { WEAPON *p = gWR.GetFirstPos(iActiveSlot); if ( p ) iWidth = p->rcActive.right - p->rcActive.left; else iWidth = giBucketWidth; } else iWidth = giBucketWidth; SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect(m_HUD_bucket0 + i)); x += iWidth + 5; } a = 128; //!!! x = 10; // Draw all of the buckets for( i = 0; i < MAX_WEAPON_SLOTS; i++ ) { y = giBucketHeight + 10; // If this is the active slot, draw the bigger pictures, // otherwise just draw boxes if( i == iActiveSlot ) { WEAPON *p = gWR.GetFirstPos( i ); int iWidth = giBucketWidth; if( p ) iWidth = p->rcActive.right - p->rcActive.left; for( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) { p = gWR.GetWeaponSlot( i, iPos ); if( !p || !p->iId ) continue; UnpackRGB( r, g, b, gHUD.m_iHUDColor ); // if active, then we must have ammo. if( gpActiveSel == p ) { SPR_Set( p->hActive, r, g, b ); SPR_DrawAdditive( 0, x, y, &p->rcActive ); SPR_Set( gHUD.GetSprite(m_HUD_selection), r, g, b ); SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( m_HUD_selection )); } else { // Draw Weapon if Red if no ammo if( gWR.HasAmmo( p )) ScaleColors( r, g, b, 192 ); else { UnpackRGB( r, g, b, RGB_REDISH ); ScaleColors( r, g, b, 128 ); } SPR_Set( p->hInactive, r, g, b ); SPR_DrawAdditive( 0, x, y, &p->rcInactive ); } // Draw Ammo Bar DrawAmmoBar( p, x + giABWidth/2, y, giABWidth, giABHeight ); y += p->rcActive.bottom - p->rcActive.top + 5; } x += iWidth + 5; } else { // Draw Row of weapons. UnpackRGB( r, g, b, gHUD.m_iHUDColor ); for( int iPos = 0; iPos < MAX_WEAPON_POSITIONS; iPos++ ) { WEAPON *p = gWR.GetWeaponSlot( i, iPos ); if( !p || !p->iId ) continue; if( gWR.HasAmmo(p) ) { UnpackRGB( r, g, b, gHUD.m_iHUDColor ); a = 128; } else { UnpackRGB( r, g, b, RGB_REDISH ); a = 96; } FillRGBA( x, y, giBucketWidth, giBucketHeight, r, g, b, a ); y += giBucketHeight + 5; } x += giBucketWidth + 5; } } return 1; } int CHudAmmoSecondary :: Init( void ) { HOOK_MESSAGE( SecAmmoVal ); HOOK_MESSAGE( SecAmmoIcon ); gHUD.AddHudElem( this ); m_HUD_ammoicon = 0; for( int i = 0; i < MAX_SEC_AMMO_VALUES; i++ ) m_iAmmoAmounts[i] = -1; // -1 means don't draw this value Reset(); return 1; } void CHudAmmoSecondary :: Reset( void ) { m_fFade = 0; } int CHudAmmoSecondary :: VidInit( void ) { return 1; } int CHudAmmoSecondary :: Draw( float flTime ) { if(( gHUD.m_iHideHUDDisplay & ( HIDEHUD_WEAPONS | HIDEHUD_ALL ))) return 1; // draw secondary ammo icons above normal ammo readout int a, x, y, r, g, b, AmmoWidth; UnpackRGB( r, g, b, gHUD.m_iHUDColor ); a = (int) max( MIN_ALPHA, m_fFade ); if( m_fFade > 0 ) m_fFade -= (gHUD.m_flTimeDelta * 20); // slowly lower alpha to fade out icons ScaleColors( r, g, b, a ); AmmoWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left; y = ScreenHeight - (gHUD.m_iFontHeight*4); // this is one font height higher than the weapon ammo values x = ScreenWidth - AmmoWidth; if( m_HUD_ammoicon ) { // Draw the ammo icon x -= (gHUD.GetSpriteRect(m_HUD_ammoicon).right - gHUD.GetSpriteRect(m_HUD_ammoicon).left); y -= (gHUD.GetSpriteRect(m_HUD_ammoicon).top - gHUD.GetSpriteRect(m_HUD_ammoicon).bottom); SPR_Set( gHUD.GetSprite(m_HUD_ammoicon), r, g, b ); SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect(m_HUD_ammoicon) ); } else { // move the cursor by the '0' char instead, since we don't have an icon to work with x -= AmmoWidth; y -= (gHUD.GetSpriteRect(gHUD.m_HUD_number_0).top - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).bottom); } // draw the ammo counts, in reverse order, from right to left for( int i = MAX_SEC_AMMO_VALUES-1; i >= 0; i-- ) { if( m_iAmmoAmounts[i] < 0 ) continue; // negative ammo amounts imply that they shouldn't be drawn // half a char gap between the ammo number and the previous pic x -= (AmmoWidth / 2); // draw the number, right-aligned x -= (gHUD.GetNumWidth( m_iAmmoAmounts[i], DHN_DRAWZERO ) * AmmoWidth); gHUD.DrawHudNumber( x, y, DHN_DRAWZERO, m_iAmmoAmounts[i], r, g, b ); if ( i != 0 ) { // draw the divider bar x -= (AmmoWidth / 2); FillRGBA(x, y, (AmmoWidth/10), gHUD.m_iFontHeight, r, g, b, a); } } return 1; } // Message handler for Secondary Ammo Value // accepts one value: // string: sprite name int CHudAmmoSecondary :: MsgFunc_SecAmmoIcon( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); m_HUD_ammoicon = gHUD.GetSpriteIndex( READ_STRING() ); END_READ(); return 1; } // Message handler for Secondary Ammo Icon // Sets an ammo value // takes two values: // byte: ammo index // byte: ammo value int CHudAmmoSecondary :: MsgFunc_SecAmmoVal( const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pszName, iSize, pbuf ); int index = READ_BYTE(); if( index < 0 || index >= MAX_SEC_AMMO_VALUES ) return 1; m_iAmmoAmounts[index] = READ_BYTE(); m_iFlags |= HUD_ACTIVE; // check to see if there is anything left to draw int count = 0; for( int i = 0; i < MAX_SEC_AMMO_VALUES; i++ ) { count += max( 0, m_iAmmoAmounts[i] ); } if( count == 0 ) { // the ammo fields are all empty, so turn off this hud area m_iFlags &= ~HUD_ACTIVE; return 1; } // make the icons light up m_fFade = 200.0f; END_READ(); return 1; } /* ================================= GetSpriteList Finds and returns the matching sprite name 'psz' in the given sprite list 'pList' iCount is the number of items in the pList ================================= */ client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ) { if( !pList ) return NULL; int i = iCount; client_sprite_t *p = pList; while( i-- ) { if ((!strcmp(psz, p->szName)) && (p->iRes == iRes)) return p; p++; } return NULL; }