//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "hudelement.h" #include "iclientmode.h" #include #include #include #include #include #include #include #include #include #include "c_tf_playerresource.h" #include "tf_playermodelpanel.h" #include "econ_item_description.h" #include "hud_numericdisplay.h" #include "c_team.h" #include "c_tf_player.h" #include "tf_shareddefs.h" #include "tf_hud_playerstatus.h" #include "tf_gamerules.h" #include "tf_logic_halloween_2014.h" #include "tf_logic_player_destruction.h" #include "tf_wheel_of_doom.h" #include "confirm_dialog.h" using namespace vgui; ConVar cl_hud_playerclass_use_playermodel( "cl_hud_playerclass_use_playermodel", "1", FCVAR_ARCHIVE, "Use player model in player class HUD." ); #ifdef STAGING_ONLY ConVar cl_hud_playerclass_playermodel_lod( "cl_hud_playerclass_playermodel_lod", "0", FCVAR_ARCHIVE, "Adjust lod on player model in the player class HUD." ); #endif // STAGING_ONLY ConVar cl_hud_playerclass_playermodel_showed_confirm_dialog( "cl_hud_playerclass_playermodel_showed_confirm_dialog", "0", FCVAR_ARCHIVE | FCVAR_HIDDEN ); extern ConVar tf_max_health_boost; static const char *g_szBlueClassImages[] = { "", "../hud/class_scoutblue", "../hud/class_sniperblue", "../hud/class_soldierblue", "../hud/class_demoblue", "../hud/class_medicblue", "../hud/class_heavyblue", "../hud/class_pyroblue", "../hud/class_spyblue", "../hud/class_engiblue", "../hud/class_scoutblue", }; static const char *g_szRedClassImages[] = { "", "../hud/class_scoutred", "../hud/class_sniperred", "../hud/class_soldierred", "../hud/class_demored", "../hud/class_medicred", "../hud/class_heavyred", "../hud/class_pyrored", "../hud/class_spyred", "../hud/class_engired", "../hud/class_scoutred", }; enum { HUD_HEALTH_NO_ANIM = 0, HUD_HEALTH_BONUS_ANIM, HUD_HEALTH_DYING_ANIM, }; DECLARE_BUILD_FACTORY( CTFClassImage ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFHudPlayerClass::CTFHudPlayerClass( Panel *parent, const char *name ) : EditablePanel( parent, name ) { m_pClassImage = NULL; m_pClassImageBG = NULL; m_pSpyImage = NULL; m_pSpyOutlineImage = NULL; m_pPlayerModelPanel = NULL; m_pPlayerModelPanelBG = NULL; m_pCarryingWeaponPanel = NULL; m_pCarryingLabel = NULL; m_pCarryingOwnerLabel = NULL; m_pCarryingBG = NULL; m_nTeam = TEAM_UNASSIGNED; m_nClass = TF_CLASS_UNDEFINED; m_nDisguiseTeam = TEAM_UNASSIGNED; m_nDisguiseClass = TF_CLASS_UNDEFINED; m_hDisguiseWeapon = NULL; m_flNextThink = 0.0f; m_nKillStreak = 0; #ifdef STAGING_ONLY m_nLOD = -1; #endif // STAGING_ONLY m_bUsePlayerModel = cl_hud_playerclass_use_playermodel.GetBool(); ListenForGameEvent( "localplayer_changedisguise" ); ListenForGameEvent( "post_inventory_application" ); ListenForGameEvent( "localplayer_pickup_weapon" ); for ( int i = 0; i < TF_CLASS_COUNT_ALL; i++ ) { // The materials are given to vgui via the SetImage() function, which prepends // the "vgui/", so we need to precache them with the same. if ( g_szBlueClassImages[i] && g_szBlueClassImages[i][0] ) { PrecacheMaterial( VarArgs( "vgui/%s", g_szBlueClassImages[i] ) ); PrecacheMaterial( VarArgs( "vgui/%s_cloak", g_szBlueClassImages[i] ) ); PrecacheMaterial( VarArgs( "vgui/%s_halfcloak", g_szBlueClassImages[i] ) ); } if ( g_szRedClassImages[i] && g_szRedClassImages[i][0] ) { PrecacheMaterial( VarArgs( "vgui/%s", g_szRedClassImages[i] ) ); PrecacheMaterial( VarArgs( "vgui/%s_cloak", g_szRedClassImages[i] ) ); PrecacheMaterial( VarArgs( "vgui/%s_halfcloak", g_szRedClassImages[i] ) ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerClass::Reset() { m_flNextThink = gpGlobals->curtime + 0.05f; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "HudSpyDisguiseHide" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerClass::ApplySchemeSettings( IScheme *pScheme ) { // load control settings... LoadControlSettings( "resource/UI/HudPlayerClass.res" ); m_nTeam = TEAM_UNASSIGNED; m_nClass = TF_CLASS_UNDEFINED; m_nDisguiseTeam = TEAM_UNASSIGNED; m_nDisguiseClass = TF_CLASS_UNDEFINED; m_hDisguiseWeapon = NULL; m_flNextThink = 0.0f; m_nCloakLevel = 0; m_nLoadoutPosition = LOADOUT_POSITION_PRIMARY; m_pClassImage = FindControl( "PlayerStatusClassImage", false ); m_pClassImageBG = FindControl( "PlayerStatusClassImageBG", false ); m_pSpyImage = FindControl( "PlayerStatusSpyImage", false ); m_pSpyOutlineImage = FindControl( "PlayerStatusSpyOutlineImage", false ); m_pPlayerModelPanel = FindControl( "classmodelpanel", false ); m_pPlayerModelPanelBG = FindControl( "classmodelpanelBG", false ); m_pCarryingWeaponPanel = FindControl< EditablePanel >( "CarryingWeapon", false ); if ( m_pCarryingWeaponPanel ) { m_pCarryingLabel = m_pCarryingWeaponPanel->FindControl< CExLabel >( "CarryingLabel" ); m_pCarryingOwnerLabel = m_pCarryingWeaponPanel->FindControl< Label >( "OwnerLabel" ); m_pCarryingBG = m_pCarryingWeaponPanel->FindControl< CTFImagePanel >( "CarryingBackground" ); } BaseClass::ApplySchemeSettings( pScheme ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerClass::OnThink() { if ( m_flNextThink > gpGlobals->curtime ) return; m_flNextThink = gpGlobals->curtime + 0.5f; C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pPlayer ) return; bool bTeamChange = false; // set our background colors if ( m_nTeam != pPlayer->GetTeamNumber() ) { bTeamChange = true; m_nTeam = pPlayer->GetTeamNumber(); } int nCloakLevel = 0; bool bCloakChange = false; float flInvis = pPlayer->GetPercentInvisible(); if ( flInvis > 0.9 ) { nCloakLevel = 2; } else if ( flInvis > 0.1 ) { nCloakLevel = 1; } if ( nCloakLevel != m_nCloakLevel ) { m_nCloakLevel = nCloakLevel; bCloakChange = true; } bool bLoadoutPositionChange = false; int nLoadoutSlot = pPlayer->GetActiveTFWeapon() ? pPlayer->GetActiveTFWeapon()->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( m_nClass ) : LOADOUT_POSITION_PRIMARY; if ( m_nLoadoutPosition != nLoadoutSlot ) { m_nLoadoutPosition = nLoadoutSlot; bLoadoutPositionChange = true; } bool bPlayerClassModeChange = false; if ( m_bUsePlayerModel != cl_hud_playerclass_use_playermodel.GetBool() ) { m_bUsePlayerModel = cl_hud_playerclass_use_playermodel.GetBool(); bPlayerClassModeChange = true; } #ifdef STAGING_ONLY if ( m_nLOD != cl_hud_playerclass_playermodel_lod.GetInt() && m_pPlayerModelPanel ) { m_nLOD = cl_hud_playerclass_playermodel_lod.GetInt(); m_pPlayerModelPanel->SetLOD( m_nLOD ); } #endif // STAGING_ONLY bool bForceEyeUpdate = false; // set our class image if ( m_nClass != pPlayer->GetPlayerClass()->GetClassIndex() || bTeamChange || bCloakChange || bLoadoutPositionChange || bPlayerClassModeChange || ( m_nClass == TF_CLASS_SPY && ( m_nDisguiseClass != pPlayer->m_Shared.GetDisguiseClass() || m_nDisguiseTeam != pPlayer->m_Shared.GetDisguiseTeam() || m_hDisguiseWeapon != pPlayer->m_Shared.GetDisguiseWeapon() ) ) ) { bForceEyeUpdate = true; m_nClass = pPlayer->GetPlayerClass()->GetClassIndex(); if ( m_nClass == TF_CLASS_SPY && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) { if ( !pPlayer->m_Shared.InCond( TF_COND_DISGUISING ) ) { m_nDisguiseTeam = pPlayer->m_Shared.GetDisguiseTeam(); m_nDisguiseClass = pPlayer->m_Shared.GetDisguiseClass(); m_hDisguiseWeapon = pPlayer->m_Shared.GetDisguiseWeapon(); } } else { m_nDisguiseTeam = TEAM_UNASSIGNED; m_nDisguiseClass = TF_CLASS_UNDEFINED; m_hDisguiseWeapon = NULL; } if ( m_bUsePlayerModel && m_pPlayerModelPanel && m_pPlayerModelPanelBG ) { m_pPlayerModelPanel->SetVisible( true ); m_pPlayerModelPanelBG->SetVisible( true ); UpdateModelPanel(); } else if ( m_pClassImage && m_pSpyImage ) { if ( m_pPlayerModelPanel ) m_pPlayerModelPanel->SetVisible( false ); if ( m_pPlayerModelPanelBG ) m_pPlayerModelPanelBG->SetVisible( false ); m_pClassImage->SetVisible( true ); m_pClassImageBG->SetVisible( true ); int iCloakState = 0; if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) ) { iCloakState = m_nCloakLevel; } if ( m_nDisguiseTeam != TEAM_UNASSIGNED || m_nDisguiseClass != TF_CLASS_UNDEFINED ) { m_pSpyImage->SetVisible( true ); m_pClassImage->SetClass( m_nDisguiseTeam, m_nDisguiseClass, iCloakState ); } else { m_pSpyImage->SetVisible( false ); m_pClassImage->SetClass( m_nTeam, m_nClass, iCloakState ); } } } if ( m_pCarryingWeaponPanel ) { // Don't show if we're disguised (the panels overlap) bool bShowCarryingWeaponPanel = m_nDisguiseClass == TF_CLASS_UNDEFINED; if ( pPlayer->GetActiveTFWeapon() && pPlayer->GetActiveTFWeapon()->GetAttributeContainer() ) { CEconItemView* pItem = pPlayer->GetActiveTFWeapon()->GetAttributeContainer()->GetItem(); if ( pItem ) { CSteamID playerSteamID; pPlayer->GetSteamID( &playerSteamID ); // We're holding a weapon we dont own! if ( playerSteamID.GetAccountID() != pItem->GetAccountID() && m_pCarryingLabel ) { locchar_t wszLocString [128]; // Construct and set the weapon's name g_pVGuiLocalize->ConstructString_safe( wszLocString, L"%s1", 1, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pItem->GetItemDefinition(), pItem->GetItemQuality() ).GetFullName() ); m_pCarryingWeaponPanel->SetDialogVariable( "carrying", wszLocString ); // Get and set the rarity color of the weapon const char* pszColorName = GetItemSchema()->GetRarityColor( pItem->GetItemDefinition()->GetRarity() ); pszColorName = pszColorName ? pszColorName : "TanLight"; if ( pszColorName ) { m_pCarryingLabel->SetColorStr( pszColorName ); } bool bHasOwner = false; locchar_t wszPlayerName [128]; CBasePlayer *pOwner = GetPlayerByAccountID( pItem->GetAccountID() ); // Bots will not work here, so don't fill this out if there's no owner if ( pOwner ) { // Fill out the actual owner's name locchar_t wszStolenString[128]; g_pVGuiLocalize->ConvertANSIToUnicode( pOwner->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) ); g_pVGuiLocalize->ConstructString_safe( wszStolenString, g_pVGuiLocalize->Find( "TF_WhoDropped" ), 1, wszPlayerName ); m_pCarryingOwnerLabel->SetText( wszStolenString ); bHasOwner = true; } else { m_pCarryingOwnerLabel->SetText( "" ); } int nMaxWide = 0, nMaxTall = 0; // Resize the panel to just be the width of whichever label is longer int nTall, nWide; m_pCarryingLabel->SizeToContents(); m_pCarryingLabel->GetContentSize( nWide, nTall ); nMaxWide = Max( nMaxWide, nWide ); nMaxTall = Max( nMaxTall, nTall ); m_pCarryingOwnerLabel->SizeToContents(); m_pCarryingOwnerLabel->GetContentSize( nWide, nTall ); nMaxWide = Max( nMaxWide, nWide ); nMaxTall = Max( nMaxTall, nTall ); m_pCarryingBG->SetWide( nMaxWide + ( m_pCarryingLabel->GetXPos() * 2 ) ); m_pCarryingBG->SetTall( bHasOwner ? m_pCarryingOwnerLabel->GetYPos() + m_pCarryingOwnerLabel->GetTall() + YRES( 2 ) : m_pCarryingLabel->GetYPos() + m_pCarryingLabel->GetTall() + YRES( 2 ) ); } else { bShowCarryingWeaponPanel = false; } } } else { bShowCarryingWeaponPanel = false; } if ( CTFPlayerDestructionLogic::GetRobotDestructionLogic() && ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetType() == CTFPlayerDestructionLogic::TYPE_PLAYER_DESTRUCTION ) ) { if ( pPlayer->HasTheFlag() ) { bShowCarryingWeaponPanel = false; } } m_pCarryingWeaponPanel->SetVisible( bShowCarryingWeaponPanel ); } if ( m_bUsePlayerModel && m_pPlayerModelPanel ) { bool bPlaySparks = false; int iKillStreak = pPlayer->m_Shared.GetStreak( CTFPlayerShared::kTFStreak_Kills ); if ( iKillStreak != m_nKillStreak && iKillStreak > 0 ) { bPlaySparks = true; } m_nKillStreak = iKillStreak; m_pPlayerModelPanel->SetEyeGlowEffect( pPlayer->GetEyeGlowEffect(), pPlayer->GetEyeGlowColor( false ), pPlayer->GetEyeGlowColor( true ), bForceEyeUpdate, bPlaySparks ); } } static void HudPlayerClassUsePlayerModelDialogCallback( bool bConfirmed, void *pContext ) { cl_hud_playerclass_use_playermodel.SetValue( bConfirmed ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerClass::UpdateModelPanel() { if ( !m_bUsePlayerModel ) { return; } C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pPlayer || !pPlayer->IsAlive() ) { return; } if ( !cl_hud_playerclass_playermodel_showed_confirm_dialog.GetBool() ) { // only show this message one time ShowConfirmDialog( "#GameUI_HudPlayerClassUsePlayerModelDialogTitle", "#GameUI_HudPlayerClassUsePlayerModelDialogMessage", "#GameUI_HudPlayerClassUsePlayerModelDialogConfirm", "#GameUI_HudPlayerClassUsePlayerModelDialogCancel", &HudPlayerClassUsePlayerModelDialogCallback ); cl_hud_playerclass_playermodel_showed_confirm_dialog.SetValue( true ); } // hide old UI if ( m_pSpyImage ) m_pSpyImage->SetVisible( false ); if ( m_pClassImage ) m_pClassImage->SetVisible( false ); if ( m_pClassImageBG ) m_pClassImageBG->SetVisible( false ); if ( m_pPlayerModelPanel && m_pPlayerModelPanel->IsVisible() ) { int nClass; int nTeam; int nItemSlot = m_nLoadoutPosition; CEconItemView *pWeapon = NULL; bool bDisguised = pPlayer->m_Shared.InCond( TF_COND_DISGUISED ); if ( bDisguised ) { nClass = pPlayer->m_Shared.GetDisguiseClass(); nTeam = pPlayer->m_Shared.GetDisguiseTeam(); if ( pPlayer->m_Shared.GetDisguiseWeapon() ) { CAttributeContainer *pCont = pPlayer->m_Shared.GetDisguiseWeapon()->GetAttributeContainer(); pWeapon = pCont ? pCont->GetItem() : NULL; if ( pWeapon ) { nItemSlot = pWeapon->GetStaticData()->GetLoadoutSlot( nClass ); } } } else { nClass = pPlayer->GetPlayerClass()->GetClassIndex(); nTeam = pPlayer->GetTeamNumber(); CTFWeaponBase *pEnt = dynamic_cast< CTFWeaponBase* >( pPlayer->GetEntityForLoadoutSlot( nItemSlot ) ); if ( pEnt ) { pWeapon = pEnt->GetAttributeContainer()->GetItem(); } } bool bIsRobot = false; int iRobot = 0; CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iRobot, appear_as_mvm_robot ); bIsRobot = iRobot ? true : false; m_pPlayerModelPanel->ClearCarriedItems(); m_pPlayerModelPanel->SetToPlayerClass( nClass, bIsRobot ); m_pPlayerModelPanel->SetTeam( nTeam ); if ( pWeapon ) { m_pPlayerModelPanel->AddCarriedItem( pWeapon ); } for ( int wbl = pPlayer->GetNumWearables()-1; wbl >= 0; wbl-- ) { C_TFWearable *pItem = dynamic_cast( pPlayer->GetWearable( wbl ) ); if ( !pItem ) continue; if ( pItem->IsViewModelWearable() ) continue; if ( pItem->IsDisguiseWearable() && !bDisguised ) continue; if ( !pItem->IsDisguiseWearable() && bDisguised ) continue; CAttributeContainer *pCont = pItem->GetAttributeContainer(); CEconItemView *pEconItemView = pCont ? pCont->GetItem() : NULL; if ( pEconItemView && pEconItemView->IsValid() ) { m_pPlayerModelPanel->AddCarriedItem( pEconItemView ); } } m_pPlayerModelPanel->HoldItemInSlot( nItemSlot ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerClass::FireGameEvent( IGameEvent * event ) { const char* pszEventName = event->GetName(); if ( FStrEq( "localplayer_changedisguise", pszEventName ) ) { if ( m_pSpyImage && m_pSpyOutlineImage ) { bool bFadeIn = event->GetBool( "disguised", false ); if ( bFadeIn ) { m_pSpyImage->SetAlpha( 0 ); } else { m_pSpyImage->SetAlpha( 255 ); } m_pSpyOutlineImage->SetAlpha( 0 ); m_pSpyImage->SetVisible( true ); m_pSpyOutlineImage->SetVisible( true ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( bFadeIn ? "HudSpyDisguiseFadeIn" : "HudSpyDisguiseFadeOut" ); } UpdateModelPanel(); } else if ( FStrEq( "post_inventory_application", pszEventName ) ) { // Force a refresh. if this is for the local player int iUserID = event->GetInt( "userid" ); C_TFPlayer* pPlayer = ToTFPlayer( C_TFPlayer::GetLocalPlayer() ); if ( pPlayer && pPlayer->GetUserID() == iUserID ) { UpdateModelPanel(); } } else if ( FStrEq( "localplayer_pickup_weapon", pszEventName ) ) { UpdateModelPanel(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFHealthPanel::CTFHealthPanel( Panel *parent, const char *name ) : vgui::Panel( parent, name ) { m_flHealth = 1.0f; m_iMaterialIndex = surface()->DrawGetTextureId( "hud/health_color" ); if ( m_iMaterialIndex == -1 ) // we didn't find it, so create a new one { m_iMaterialIndex = surface()->CreateNewTextureID(); surface()->DrawSetTextureFile( m_iMaterialIndex, "hud/health_color", true, false ); } m_iDeadMaterialIndex = surface()->DrawGetTextureId( "hud/health_dead" ); if ( m_iDeadMaterialIndex == -1 ) // we didn't find it, so create a new one { m_iDeadMaterialIndex = surface()->CreateNewTextureID(); surface()->DrawSetTextureFile( m_iDeadMaterialIndex, "hud/health_dead", true, false ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHealthPanel::Paint() { BaseClass::Paint(); int x, y, w, h; GetBounds( x, y, w, h ); Vertex_t vert[4]; float uv1 = 0.0f; float uv2 = 1.0f; int xpos = 0, ypos = 0; if ( m_flHealth <= 0 ) { // Draw the dead material surface()->DrawSetTexture( m_iDeadMaterialIndex ); vert[0].Init( Vector2D( xpos, ypos ), Vector2D( uv1, uv1 ) ); vert[1].Init( Vector2D( xpos + w, ypos ), Vector2D( uv2, uv1 ) ); vert[2].Init( Vector2D( xpos + w, ypos + h ), Vector2D( uv2, uv2 ) ); vert[3].Init( Vector2D( xpos, ypos + h ), Vector2D( uv1, uv2 ) ); surface()->DrawSetColor( Color(255,255,255,255) ); } else { float flDamageY = h * ( 1.0f - m_flHealth ); // blend in the red "damage" part surface()->DrawSetTexture( m_iMaterialIndex ); Vector2D uv11( uv1, uv2 - m_flHealth ); Vector2D uv21( uv2, uv2 - m_flHealth ); Vector2D uv22( uv2, uv2 ); Vector2D uv12( uv1, uv2 ); vert[0].Init( Vector2D( xpos, flDamageY ), uv11 ); vert[1].Init( Vector2D( xpos + w, flDamageY ), uv21 ); vert[2].Init( Vector2D( xpos + w, ypos + h ), uv22 ); vert[3].Init( Vector2D( xpos, ypos + h ), uv12 ); surface()->DrawSetColor( GetFgColor() ); } surface()->DrawTexturedPolygon( 4, vert ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFHudPlayerHealth::CTFHudPlayerHealth( Panel *parent, const char *name ) : EditablePanel( parent, name ) { m_pHealthImage = new CTFHealthPanel( this, "PlayerStatusHealthImage" ); m_pHealthImageBG = new ImagePanel( this, "PlayerStatusHealthImageBG" ); m_pHealthBonusImage = new ImagePanel( this, "PlayerStatusHealthBonusImage" ); m_pBuildingHealthImageBG = new ImagePanel( this, "BuildingStatusHealthImageBG" ); m_pBleedImage = new ImagePanel( this, "PlayerStatusBleedImage" ); m_pHookBleedImage = new ImagePanel( this, "PlayerStatusHookBleedImage" ); m_pMarkedForDeathImage = new ImagePanel( this, "PlayerStatusMarkedForDeathImage" ); m_pMarkedForDeathImageSilent = new ImagePanel( this, "PlayerStatusMarkedForDeathSilentImage" ); m_pMilkImage = new ImagePanel( this, "PlayerStatusMilkImage" ); m_pWheelOfDoomImage = new ImagePanel( this, "PlayerStatus_WheelOfDoom" ); m_flNextThink = 0.0f; m_nBonusHealthOrigX = -1; m_nBonusHealthOrigY = -1; m_nBonusHealthOrigW = -1; m_nBonusHealthOrigH = -1; // Vaccinator m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_UBER_BULLET_RESIST, BUFF_CLASS_BULLET_RESIST, new ImagePanel( this, "PlayerStatus_MedicUberBulletResistImage" ), "../HUD/defense_buff_bullet_blue", "../HUD/defense_buff_bullet_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_UBER_BLAST_RESIST, BUFF_CLASS_BLAST_RESIST, new ImagePanel( this, "PlayerStatus_MedicUberBlastResistImage" ), "../HUD/defense_buff_explosion_blue", "../HUD/defense_buff_explosion_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_UBER_FIRE_RESIST, BUFF_CLASS_FIRE_RESIST, new ImagePanel( this, "PlayerStatus_MedicUberFireResistImage" ), "../HUD/defense_buff_fire_blue", "../HUD/defense_buff_fire_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_SMALL_BULLET_RESIST, BUFF_CLASS_BULLET_RESIST, new ImagePanel( this, "PlayerStatus_MedicSmallBulletResistImage" ), "../HUD/defense_buff_bullet_blue", "../HUD/defense_buff_bullet_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_SMALL_BLAST_RESIST, BUFF_CLASS_BLAST_RESIST, new ImagePanel( this, "PlayerStatus_MedicSmallBlastResistImage" ), "../HUD/defense_buff_explosion_blue", "../HUD/defense_buff_explosion_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_MEDIGUN_SMALL_FIRE_RESIST, BUFF_CLASS_FIRE_RESIST, new ImagePanel( this, "PlayerStatus_MedicSmallFireResistImage" ), "../HUD/defense_buff_fire_blue", "../HUD/defense_buff_fire_red" ) ); // Soldier buffs m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_OFFENSEBUFF, BUFF_CLASS_SOLDIER_OFFENSE, new ImagePanel( this, "PlayerStatus_SoldierOffenseBuff" ), "../Effects/soldier_buff_offense_blue", "../Effects/soldier_buff_offense_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_DEFENSEBUFF, BUFF_CLASS_SOLDIER_DEFENSE, new ImagePanel( this, "PlayerStatus_SoldierDefenseBuff" ), "../Effects/soldier_buff_defense_blue", "../Effects/soldier_buff_defense_red" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_REGENONDAMAGEBUFF, BUFF_CLASS_SOLDIER_HEALTHONHIT, new ImagePanel( this, "PlayerStatus_SoldierHealOnHitBuff" ), "../Effects/soldier_buff_healonhit_blue", "../Effects/soldier_buff_healonhit_red" ) ); // Powerup Rune status m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_STRENGTH, RUNE_CLASS_STRENGTH, new ImagePanel( this, "PlayerStatus_RuneStrength" ), "../Effects/powerup_strength_hud", "../Effects/powerup_strength_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_HASTE, RUNE_CLASS_HASTE, new ImagePanel( this, "PlayerStatus_RuneHaste" ), "../Effects/powerup_haste_hud", "../Effects/powerup_haste_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_REGEN, RUNE_CLASS_REGEN, new ImagePanel( this, "PlayerStatus_RuneRegen" ), "../Effects/powerup_regen_hud", "../Effects/powerup_regen_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_RESIST, RUNE_CLASS_RESIST, new ImagePanel( this, "PlayerStatus_RuneResist" ), "../Effects/powerup_resist_hud", "../Effects/powerup_resist_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_VAMPIRE, RUNE_CLASS_VAMPIRE, new ImagePanel( this, "PlayerStatus_RuneVampire" ), "../Effects/powerup_vampire_hud", "../Effects/powerup_vampire_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_REFLECT, RUNE_CLASS_REFLECT, new ImagePanel( this, "PlayerStatus_RuneReflect" ), "../Effects/powerup_reflect_hud", "../Effects/powerup_reflect_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_PRECISION, RUNE_CLASS_PRECISION, new ImagePanel( this, "PlayerStatus_RunePrecision" ), "../Effects/powerup_precision_hud", "../Effects/powerup_precision_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_AGILITY, RUNE_CLASS_AGILITY, new ImagePanel( this, "PlayerStatus_RuneAgility" ), "../Effects/powerup_agility_hud", "../Effects/powerup_agility_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_KNOCKOUT, RUNE_CLASS_KNOCKOUT, new ImagePanel( this, "PlayerStatus_RuneKnockout" ), "../Effects/powerup_knockout_hud", "../Effects/powerup_knockout_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_KING, RUNE_CLASS_KING, new ImagePanel( this, "PlayerStatus_RuneKing" ), "../Effects/powerup_king_hud", "../Effects/powerup_king_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_PLAGUE, RUNE_CLASS_PLAGUE, new ImagePanel( this, "PlayerStatus_RunePlague" ), "../Effects/powerup_plague_hud", "../Effects/powerup_plague_hud" ) ); m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_RUNE_SUPERNOVA, RUNE_CLASS_SUPERNOVA, new ImagePanel( this, "PlayerStatus_RuneSupernova" ), "../Effects/powerup_supernova_hud", "../Effects/powerup_supernova_hud" ) ); #ifdef STAGING_ONLY // Spy Mark m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_TRANQ_MARKED, DEBUFF_CLASS_SPY_MARKED, new ImagePanel( this, "PlayerStatus_SpyMarked" ), "../Effects/tranq_debuff", "../Effects/tranq_debuff" ) ); #endif // STAGING_ONLY // Parachute m_vecBuffInfo.AddToTail( new CTFBuffInfo( TF_COND_PARACHUTE_DEPLOYED, BUFF_CLASS_PARACHUTE, new ImagePanel( this, "PlayerStatus_Parachute" ), "../HUD/hud_parachute_active", "../HUD/hud_parachute_active" ) ); m_iAnimState = HUD_HEALTH_NO_ANIM; m_bAnimate = true; } CTFHudPlayerHealth::~CTFHudPlayerHealth() { m_vecBuffInfo.PurgeAndDeleteElements(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerHealth::Reset() { m_flNextThink = gpGlobals->curtime + 0.05f; m_nHealth = -1; m_bBuilding = false; m_iAnimState = HUD_HEALTH_NO_ANIM; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerHealth::ApplySchemeSettings( IScheme *pScheme ) { // load control settings... LoadControlSettings( GetResFilename() ); if ( m_pHealthBonusImage ) { m_pHealthBonusImage->GetBounds( m_nBonusHealthOrigX, m_nBonusHealthOrigY, m_nBonusHealthOrigW, m_nBonusHealthOrigH ); } m_flNextThink = 0.0f; BaseClass::ApplySchemeSettings( pScheme ); m_pBuildingHealthImageBG->SetVisible( m_bBuilding ); m_pPlayerLevelLabel = dynamic_cast( FindChildByName( "PlayerStatusPlayerLevel" ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerHealth::SetHealth( int iNewHealth, int iMaxHealth, int iMaxBuffedHealth ) { // set our health m_nHealth = iNewHealth; m_nMaxHealth = iMaxHealth; m_pHealthImage->SetHealth( (float)(m_nHealth) / (float)(m_nMaxHealth) ); if ( m_pHealthImage ) { m_pHealthImage->SetFgColor( Color( 255, 255, 255, 255 ) ); } if ( m_nHealth <= 0 ) { if ( m_pHealthImageBG->IsVisible() ) { m_pHealthImageBG->SetVisible( false ); } if ( m_pBuildingHealthImageBG->IsVisible() ) { m_pBuildingHealthImageBG->SetVisible( false ); } HideHealthBonusImage(); } else { if ( !m_pHealthImageBG->IsVisible() ) { m_pHealthImageBG->SetVisible( true ); } m_pBuildingHealthImageBG->SetVisible( m_bBuilding ); // are we getting a health bonus? if ( m_nHealth > m_nMaxHealth ) { if ( m_pHealthBonusImage && m_nBonusHealthOrigW != -1 ) { if ( !m_pHealthBonusImage->IsVisible() ) { m_pHealthBonusImage->SetVisible( true ); } if ( m_bAnimate && m_iAnimState != HUD_HEALTH_BONUS_ANIM ) { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthDyingPulseStop" ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthBonusPulse" ); m_iAnimState = HUD_HEALTH_BONUS_ANIM; } m_pHealthBonusImage->SetDrawColor( Color( 255, 255, 255, 255 ) ); // scale the flashing image based on how much health bonus we currently have float flBoostMaxAmount = ( iMaxBuffedHealth ) - m_nMaxHealth; float flPercent = MIN( ( m_nHealth - m_nMaxHealth ) / flBoostMaxAmount, 1.0f ); int nPosAdj = RoundFloatToInt( flPercent * m_nHealthBonusPosAdj ); int nSizeAdj = 2 * nPosAdj; m_pHealthBonusImage->SetBounds( m_nBonusHealthOrigX - nPosAdj, m_nBonusHealthOrigY - nPosAdj, m_nBonusHealthOrigW + nSizeAdj, m_nBonusHealthOrigH + nSizeAdj ); } } // are we close to dying? else if ( m_nHealth < m_nMaxHealth * m_flHealthDeathWarning ) { if ( m_pHealthBonusImage && m_nBonusHealthOrigW != -1 ) { if ( !m_pHealthBonusImage->IsVisible() ) { m_pHealthBonusImage->SetVisible( true ); } if ( m_bAnimate && m_iAnimState != HUD_HEALTH_DYING_ANIM ) { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthBonusPulseStop" ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthDyingPulse" ); m_iAnimState = HUD_HEALTH_DYING_ANIM; } m_pHealthBonusImage->SetDrawColor( m_clrHealthDeathWarningColor ); // scale the flashing image based on how much health bonus we currently have float flBoostMaxAmount = m_nMaxHealth * m_flHealthDeathWarning; float flPercent = ( flBoostMaxAmount - m_nHealth ) / flBoostMaxAmount; int nPosAdj = RoundFloatToInt( flPercent * m_nHealthBonusPosAdj ); int nSizeAdj = 2 * nPosAdj; m_pHealthBonusImage->SetBounds( m_nBonusHealthOrigX - nPosAdj, m_nBonusHealthOrigY - nPosAdj, m_nBonusHealthOrigW + nSizeAdj, m_nBonusHealthOrigH + nSizeAdj ); } if ( m_pHealthImage ) { m_pHealthImage->SetFgColor( m_clrHealthDeathWarningColor ); } } // turn it off else { HideHealthBonusImage(); } } // set our health display value if ( m_nHealth > 0 ) { SetDialogVariable( "Health", m_nHealth ); } else { SetDialogVariable( "Health", "" ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerHealth::SetLevel( int nLevel ) { if ( m_pPlayerLevelLabel ) { bool bVisible = ( nLevel >= 0 ) ? true : false; if ( bVisible ) { m_pPlayerLevelLabel->SetText( CFmtStr( "%d", nLevel ) ); } if ( m_pPlayerLevelLabel->IsVisible() != bVisible ) { m_pPlayerLevelLabel->SetVisible( bVisible ); } } }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerHealth::HideHealthBonusImage( void ) { if ( m_pHealthBonusImage && m_pHealthBonusImage->IsVisible() ) { if ( m_nBonusHealthOrigW != -1 ) { m_pHealthBonusImage->SetBounds( m_nBonusHealthOrigX, m_nBonusHealthOrigY, m_nBonusHealthOrigW, m_nBonusHealthOrigH ); } m_pHealthBonusImage->SetVisible( false ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthBonusPulseStop" ); g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudHealthDyingPulseStop" ); m_iAnimState = HUD_HEALTH_NO_ANIM; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- static void SetPlayerHealthImagePanelVisibility( CTFPlayer *pPlayer, ETFCond eCond, vgui::ImagePanel *pImagePanel, int& nXOffset, const Color& colorIfVisible ) { Assert( pImagePanel != NULL ); if ( pPlayer->m_Shared.InCond( eCond ) && !pImagePanel->IsVisible() ) { pImagePanel->SetVisible( true ); pImagePanel->SetDrawColor( colorIfVisible ); // Reposition ourselves and increase the offset if we are active int x,y; pImagePanel->GetPos( x, y ); pImagePanel->SetPos( nXOffset, y ); nXOffset += 100.f; } } void CTFBuffInfo::Update( CTFPlayer *pPlayer ) { Assert( m_pImagePanel != NULL && pPlayer != NULL ); if ( pPlayer->m_Shared.InCond( m_eCond ) ) { if( m_pzsBlueImage && m_pzsBlueImage[0] && m_pzsRedImage && m_pzsRedImage[0] ) { if( pPlayer->GetTeamNumber() == TF_TEAM_BLUE ) m_pImagePanel->SetImage( m_pzsBlueImage ); else m_pImagePanel->SetImage( m_pzsRedImage ); } } } void CTFHudPlayerHealth::OnThink() { if ( m_flNextThink < gpGlobals->curtime ) { C_TFPlayer *pPlayer = ToTFPlayer( C_BasePlayer::GetLocalPlayer() ); if ( pPlayer ) { SetHealth( pPlayer->GetHealth(), pPlayer->GetMaxHealth(), pPlayer->m_Shared.GetMaxBuffedHealth() ); int color_offset = ((int)(gpGlobals->realtime*10)) % 5; int color_fade = 160 + (color_offset*10); // Find our starting point, just above the health '+' int nXOffset,y; m_pHealthImage->GetPos( nXOffset, y ); // Nudge over a bit to get centered nXOffset += 25; // Turn all the panels off, and below conditionally turn them on FOR_EACH_VEC( m_vecBuffInfo, i ) { m_vecBuffInfo[ i ]->m_pImagePanel->SetVisible( false ); } CUtlVector m_vecActiveClasses; // Cycle through all the buffs and update them FOR_EACH_VEC( m_vecBuffInfo, i ) { // Skip if this class of buff is already being drawn if( m_vecActiveClasses.Find( m_vecBuffInfo[i]->m_eClass ) != m_vecActiveClasses.InvalidIndex() ) continue; m_vecBuffInfo[i]->Update( pPlayer ); SetPlayerHealthImagePanelVisibility( pPlayer, m_vecBuffInfo[i]->m_eCond, m_vecBuffInfo[i]->m_pImagePanel, nXOffset, Color( 255, 255, 255, color_fade ) ); // This class of buff is now active. if( m_vecBuffInfo[i]->m_pImagePanel->IsVisible() ) { m_vecActiveClasses.AddToTail( m_vecBuffInfo[i]->m_eClass ); } } // Turn all the panels off, and below conditionally turn them on m_pBleedImage->SetVisible( false ); m_pHookBleedImage->SetVisible( false ); m_pMilkImage->SetVisible( false ); m_pMarkedForDeathImage->SetVisible( false ); m_pMarkedForDeathImageSilent->SetVisible( false ); // Old method for goofy color manipulation int nBloodX = nXOffset; SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_BLEEDING, m_pBleedImage, nXOffset, Color( color_fade, 0, 0, 255 ) ); SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_GRAPPLINGHOOK_BLEEDING, m_pHookBleedImage, nBloodX, Color( 255, 255, 255, 255 ) ); // draw this on top of bleeding SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_MAD_MILK, m_pMilkImage, nXOffset, Color( color_fade, color_fade, color_fade, 255 ) ); SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_MARKEDFORDEATH, m_pMarkedForDeathImage, nXOffset, Color( 255 - color_fade, 245 - color_fade, 245 - color_fade, 255 ) ); SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_MARKEDFORDEATH_SILENT, m_pMarkedForDeathImageSilent, nXOffset, Color( 125 - color_fade, 255 - color_fade, 255 - color_fade, 255 ) ); SetPlayerHealthImagePanelVisibility( pPlayer, TF_COND_PASSTIME_PENALTY_DEBUFF, m_pMarkedForDeathImageSilent, nXOffset, Color( 125 - color_fade, 255 - color_fade, 255 - color_fade, 255 ) ); UpdateHalloweenStatus(); } m_flNextThink = gpGlobals->curtime + 0.05f; } } void CTFHudPlayerHealth::UpdateHalloweenStatus( void ) { if ( TFGameRules()->IsHalloweenEffectStatusActive() ) { int status = TFGameRules()->GetHalloweenEffectStatus(); if ( status == EFFECT_WHAMMY ) { m_pWheelOfDoomImage->SetImage( "..\\HUD\\death_wheel_whammy" ); } else { m_pWheelOfDoomImage->SetImage( VarArgs( "..\\HUD\\death_wheel_%d", status - 1 ) ); } float timeLeft = TFGameRules()->GetHalloweenEffectTimeLeft(); const float warnExpireTime = 3.0f; const float blinkInterval = 0.25f; if ( timeLeft < warnExpireTime ) { int blink = (int)( timeLeft / blinkInterval ); m_pWheelOfDoomImage->SetVisible( blink & 0x1 ); } else { m_pWheelOfDoomImage->SetVisible( true ); } } else { m_pWheelOfDoomImage->SetVisible( false ); } } DECLARE_HUDELEMENT( CTFHudPlayerStatus ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFHudPlayerStatus::CTFHudPlayerStatus( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudPlayerStatus" ) { Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); m_pHudPlayerClass = new CTFHudPlayerClass( this, "HudPlayerClass" ); m_pHudPlayerHealth = new CTFHudPlayerHealth( this, "HudPlayerHealth" ); SetHiddenBits( HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerStatus::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); // HACK: Work around the scheme application order failing // to reload the player class hud element's scheme in minmode. static ConVarRef cl_hud_minmode( "cl_hud_minmode", true ); if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() ) { m_pHudPlayerClass->InvalidateLayout( false, true ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFHudPlayerStatus::ShouldDraw( void ) { CTFPlayer *pTFPlayer = CTFPlayer::GetLocalTFPlayer(); if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ) return false; if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() ) return false; if ( TFGameRules() && TFGameRules()->ShowMatchSummary() ) return false; return CHudElement::ShouldDraw(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFHudPlayerStatus::Reset() { if ( m_pHudPlayerClass ) { m_pHudPlayerClass->Reset(); } if ( m_pHudPlayerHealth ) { m_pHudPlayerHealth->Reset(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFClassImage::SetClass( int iTeam, int iClass, int iCloakstate ) { char szImage[128]; szImage[0] = '\0'; if ( iTeam == TF_TEAM_BLUE ) { Q_strncpy( szImage, g_szBlueClassImages[ iClass ], sizeof(szImage) ); } else { Q_strncpy( szImage, g_szRedClassImages[ iClass ], sizeof(szImage) ); } switch( iCloakstate ) { case 2: Q_strncat( szImage, "_cloak", sizeof(szImage), COPY_ALL_CHARACTERS ); break; case 1: Q_strncat( szImage, "_halfcloak", sizeof(szImage), COPY_ALL_CHARACTERS ); break; default: break; } if ( Q_strlen( szImage ) > 0 ) { SetImage( szImage ); } }