You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
523 lines
16 KiB
523 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "tf_hud_mediccallers.h" |
|
#include "iclientmode.h" |
|
#include <vgui/ILocalize.h> |
|
#include <vgui/ISurface.h> |
|
#include <vgui/IVGui.h> |
|
#include "view.h" |
|
#include "ivieweffects.h" |
|
#include "viewrender.h" |
|
#include "prediction.h" |
|
#include "GameEventListener.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#define MEDICCALLER_WIDE (XRES(56)) |
|
#define MEDICCALLER_TALL (YRES(48)) |
|
#define MEDICCALLER_ARROW_WIDE (XRES(16)) |
|
#define MEDICCALLER_ARROW_TALL (YRES(24)) |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFMedicCallerPanel::CTFMedicCallerPanel( Panel *parent, const char *name ) : EditablePanel(parent,name) |
|
{ |
|
m_pArrowMaterial = NULL; |
|
m_iDrawArrow = DRAW_ARROW_UP; |
|
m_bOnscreen = false; |
|
m_flPanelScale = 1.0f; |
|
m_bBurning = false; |
|
m_bBleeding = false; |
|
m_nCallerType = CALLER_TYPE_NORMAL; |
|
ListenForGameEvent( "player_calledformedic" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFMedicCallerPanel::~CTFMedicCallerPanel( void ) |
|
{ |
|
if ( m_pArrowMaterial ) |
|
{ |
|
m_pArrowMaterial->DecrementReferenceCount(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Applies scheme settings |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
|
|
LoadControlSettings( GetControlSettingFile() ); |
|
|
|
if ( m_pArrowMaterial ) |
|
{ |
|
m_pArrowMaterial->DecrementReferenceCount(); |
|
} |
|
m_pArrowMaterial = materials->FindMaterial( "HUD/medic_arrow", TEXTURE_GROUP_VGUI ); |
|
m_pArrowMaterial->IncrementReferenceCount(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::PerformLayout( void ) |
|
{ |
|
BaseClass::PerformLayout(); |
|
|
|
int nWide = XRES(100), nTall = YRES(100); |
|
|
|
bool bNormal = ( m_nCallerType == CALLER_TYPE_NORMAL ); |
|
bool bAutoCaller = ( m_nCallerType == CALLER_TYPE_AUTO ); |
|
|
|
// Adjust scale of the panel based on distance to the caller |
|
C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( pLocalTFPlayer && m_hEntity ) |
|
{ |
|
Vector vecDistance = m_hEntity->GetAbsOrigin() - pLocalTFPlayer->GetAbsOrigin(); |
|
m_flPanelScale = RemapValClamped( vecDistance.LengthSqr(), 0.0f, (2000.0f * 2000.0f), 1.0f, 0.5f ); |
|
} |
|
|
|
vgui::Panel *pPanelAuto = FindChildByName( "CallerAuto" ); |
|
if ( pPanelAuto ) |
|
{ |
|
if ( pPanelAuto->IsVisible() != bAutoCaller ) |
|
{ |
|
pPanelAuto->SetVisible( bAutoCaller ); |
|
} |
|
|
|
if ( bAutoCaller ) |
|
{ |
|
pPanelAuto->GetSize( nWide, nTall ); |
|
pPanelAuto->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanelAuto->SetPos( ( GetWide() - pPanelAuto->GetWide() ) * 0.5, ( GetTall() - pPanelAuto->GetTall() ) * 0.5 ); |
|
} |
|
} |
|
|
|
vgui::Panel *pPanel = FindChildByName( "CallerBG" ); |
|
if ( pPanel ) |
|
{ |
|
if ( pPanel->IsVisible() != bNormal ) |
|
{ |
|
pPanel->SetVisible( bNormal ); |
|
} |
|
|
|
if ( bNormal ) |
|
{ |
|
pPanel->GetSize( nWide, nTall ); |
|
pPanel->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanel->SetPos( (GetWide() - pPanel->GetWide()) * 0.5, (GetTall() - pPanel->GetTall()) * 0.5 ); |
|
} |
|
} |
|
|
|
vgui::Panel *pBurningPanel = FindChildByName( "CallerBurning" ); |
|
if ( pBurningPanel ) |
|
{ |
|
bool bVisible = bNormal && m_bBurning; |
|
if ( pBurningPanel->IsVisible() != bVisible ) |
|
{ |
|
pBurningPanel->SetVisible( bVisible ); |
|
} |
|
|
|
if ( bVisible ) |
|
{ |
|
pBurningPanel->GetSize( nWide, nTall ); |
|
pBurningPanel->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pBurningPanel->SetPos( (GetWide() - pBurningPanel->GetWide()) * 0.5, (GetTall() - pBurningPanel->GetTall()) * 0.5 ); |
|
} |
|
} |
|
|
|
vgui::Panel *pBleedingPanel = FindChildByName( "CallerBleeding" ); |
|
if ( pBleedingPanel ) |
|
{ |
|
bool bVisible = bNormal && m_bBleeding; |
|
if ( pBleedingPanel->IsVisible() != bVisible ) |
|
{ |
|
pBleedingPanel->SetVisible( bVisible ); |
|
} |
|
|
|
if ( bVisible ) |
|
{ |
|
pBleedingPanel->GetSize( nWide, nTall ); |
|
pBleedingPanel->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pBleedingPanel->SetPos( (GetWide() - pBleedingPanel->GetWide()) * 0.5, (GetTall() - pBleedingPanel->GetTall()) * 0.5 ); |
|
} |
|
} |
|
|
|
vgui::Panel *pPanelHealth = FindChildByName( "CallerHealth" ); |
|
if ( pPanelHealth ) |
|
{ |
|
if ( pPanelHealth->IsVisible() != bNormal ) |
|
{ |
|
pPanelHealth->SetVisible( bNormal ); |
|
} |
|
|
|
if ( bNormal ) |
|
{ |
|
pPanelHealth->GetSize( nWide, nTall ); |
|
pPanelHealth->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanelHealth->SetPos( (GetWide() - pPanelHealth->GetWide()) * 0.5, (GetTall() - pPanelHealth->GetTall()) * 0.5 ); |
|
pPanelHealth->SetAlpha( 0 ); |
|
} |
|
} |
|
|
|
// Revive block |
|
vgui::Panel *pPanelReviveEasy = FindChildByName( "CallerReviveEasy" ); |
|
if ( pPanelReviveEasy ) |
|
{ |
|
bool bReviveEasy = m_nCallerType == CALLER_TYPE_REVIVE_EASY; |
|
if ( pPanelReviveEasy->IsVisible() != bReviveEasy ) |
|
{ |
|
pPanelReviveEasy->SetVisible( bReviveEasy ); |
|
} |
|
|
|
if ( bReviveEasy ) |
|
{ |
|
pPanelReviveEasy->GetSize( nWide, nTall ); |
|
pPanelReviveEasy->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanelReviveEasy->SetPos( ( GetWide() - pPanelReviveEasy->GetWide() ) * 0.5, ( GetTall() - pPanelReviveEasy->GetTall() ) * 0.5 ); |
|
} |
|
} |
|
vgui::Panel *pPanelReviveMedium = FindChildByName( "CallerReviveMedium" ); |
|
if ( pPanelReviveMedium ) |
|
{ |
|
bool bReviveMedium = m_nCallerType == CALLER_TYPE_REVIVE_MEDIUM; |
|
if ( pPanelReviveMedium->IsVisible() != bReviveMedium ) |
|
{ |
|
pPanelReviveMedium->SetVisible( bReviveMedium ); |
|
} |
|
|
|
if ( bReviveMedium ) |
|
{ |
|
pPanelReviveMedium->GetSize( nWide, nTall ); |
|
pPanelReviveMedium->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanelReviveMedium->SetPos( ( GetWide() - pPanelReviveMedium->GetWide() ) * 0.5, ( GetTall() - pPanelReviveMedium->GetTall() ) * 0.5 ); |
|
} |
|
} |
|
vgui::Panel *pPanelReviveHard = FindChildByName( "CallerReviveHard" ); |
|
if ( pPanelReviveHard ) |
|
{ |
|
bool bReviveHard = m_nCallerType == CALLER_TYPE_REVIVE_HARD; |
|
if ( pPanelReviveHard->IsVisible() != bReviveHard ) |
|
{ |
|
pPanelReviveHard->SetVisible( bReviveHard ); |
|
} |
|
|
|
if ( bReviveHard ) |
|
{ |
|
pPanelReviveHard->GetSize( nWide, nTall ); |
|
pPanelReviveHard->SetSize ( nWide * m_flPanelScale, nTall * m_flPanelScale ); |
|
pPanelReviveHard->SetPos( ( GetWide() - pPanelReviveHard->GetWide() ) * 0.5, ( GetTall() - pPanelReviveHard->GetTall() ) * 0.5 ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::GetCallerPosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ) |
|
{ |
|
// Player Data |
|
Vector playerPosition = MainViewOrigin(); |
|
QAngle playerAngles = MainViewAngles(); |
|
|
|
Vector forward, right, up(0,0,1); |
|
AngleVectors (playerAngles, &forward, NULL, NULL ); |
|
forward.z = 0; |
|
VectorNormalize(forward); |
|
CrossProduct( up, forward, right ); |
|
float front = DotProduct(vecDelta, forward); |
|
float side = DotProduct(vecDelta, right); |
|
*xpos = flRadius * -side; |
|
*ypos = flRadius * -front; |
|
|
|
// Get the rotation (yaw) |
|
*flRotation = atan2(*xpos,*ypos) + M_PI; |
|
*flRotation *= 180 / M_PI; |
|
|
|
float yawRadians = -(*flRotation) * M_PI / 180.0f; |
|
float ca = cos( yawRadians ); |
|
float sa = sin( yawRadians ); |
|
|
|
// Rotate it around the circle |
|
*xpos = (int)((ScreenWidth() / 2) + (flRadius * sa)); |
|
*ypos = (int)((ScreenHeight() / 2) - (flRadius * ca)); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::OnTick( void ) |
|
{ |
|
if ( !m_hEntity || ( m_hEntity->IsPlayer() && !m_hEntity->IsAlive() ) || gpGlobals->curtime > m_flRemoveAt ) |
|
{ |
|
MarkForDeletion(); |
|
return; |
|
} |
|
|
|
// If the local player has started healing this guy, remove it too. |
|
// Also don't draw it if we're dead. |
|
C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( pLocalTFPlayer ) |
|
{ |
|
CBaseEntity *pHealTarget = pLocalTFPlayer->MedicGetHealTarget(); |
|
if ( ( pHealTarget && pHealTarget == m_hEntity ) || ( m_hEntity->IsPlayer() && !pLocalTFPlayer->IsAlive() ) ) |
|
{ |
|
MarkForDeletion(); |
|
return; |
|
} |
|
|
|
if ( m_hEntity->IsPlayer() ) |
|
{ |
|
C_TFPlayer *pTFPlayer = ToTFPlayer( m_hEntity ); |
|
if ( pTFPlayer ) |
|
{ |
|
// If we're pointing to an enemy spy and they are no longer disguised, remove ourselves |
|
if ( pTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && |
|
pTFPlayer->GetTeamNumber() != pLocalTFPlayer->GetTeamNumber() && |
|
!( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTFPlayer->m_Shared.GetDisguiseTeam() == pLocalTFPlayer->GetTeamNumber() ) ) |
|
{ |
|
MarkForDeletion(); |
|
return; |
|
} |
|
|
|
// Updates the state of the caller panel if they are now burning or bleeding, or have stopped while caller panel is still up. |
|
if ( m_nCallerType != CALLER_TYPE_AUTO ) |
|
{ |
|
m_bBurning = pTFPlayer->m_Shared.InCond( TF_COND_BURNING ); |
|
vgui::Panel *pBurningPanel = FindChildByName( "CallerBurning" ); |
|
if ( pBurningPanel && pBurningPanel->IsVisible() != m_bBurning ) |
|
{ |
|
pBurningPanel->SetVisible( m_bBurning ); |
|
} |
|
|
|
vgui::Panel *pBleedingPanel = FindChildByName( "CallerBleeding" ); |
|
m_bBleeding = pTFPlayer->m_Shared.InCond( TF_COND_BLEEDING ); |
|
if ( pBleedingPanel && pBleedingPanel->IsVisible() != m_bBleeding ) |
|
{ |
|
pBleedingPanel->SetVisible( m_bBleeding ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( m_nCallerType == CALLER_TYPE_NORMAL ) |
|
{ |
|
// Tints caller panel based on health remaining. |
|
vgui::Panel *pPanelHealth = FindChildByName( "CallerHealth" ); |
|
if ( pPanelHealth ) |
|
{ |
|
float flHealth = ( float(m_hEntity->GetHealth()) / float(m_hEntity->GetMaxHealth()) ); |
|
int iCallerHurtAlpha = 255 * ( 1 - flHealth ) + 75; |
|
pPanelHealth->SetAlpha( clamp( iCallerHurtAlpha, 0, 255 ) ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::PaintBackground( void ) |
|
{ |
|
// If the local player has started healing this guy, remove it too. |
|
//Also don't draw it if we're dead. |
|
C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( !pLocalTFPlayer ) |
|
return; |
|
|
|
if ( !m_hEntity || m_hEntity->IsDormant() ) |
|
{ |
|
SetAlpha(0); |
|
return; |
|
} |
|
|
|
// Reposition the callout based on our target's position |
|
int iX, iY; |
|
Vector vecTarget = (m_hEntity->GetAbsOrigin() + m_vecOffset); |
|
Vector vecDelta = vecTarget - MainViewOrigin(); |
|
|
|
bool bOnscreen = GetVectorInHudSpace( vecTarget, iX, iY ); // Tested and correct - should NOT be GetVectorInScreenSpace. |
|
|
|
int halfWidth = GetWide() / 2; |
|
if( !bOnscreen || iX < halfWidth || iX > ScreenWidth()-halfWidth ) |
|
{ |
|
// It's off the screen. Position the callout. |
|
VectorNormalize(vecDelta); |
|
float xpos, ypos; |
|
float flRotation; |
|
float flRadius = YRES(100); |
|
GetCallerPosition( vecDelta, flRadius, &xpos, &ypos, &flRotation ); |
|
|
|
iX = xpos; |
|
iY = ypos; |
|
|
|
Vector vCenter = m_hEntity->WorldSpaceCenter( ); |
|
if( MainViewRight().Dot( vCenter - MainViewOrigin() ) > 0 ) |
|
{ |
|
m_iDrawArrow = DRAW_ARROW_RIGHT; |
|
} |
|
else |
|
{ |
|
m_iDrawArrow = DRAW_ARROW_LEFT; |
|
} |
|
|
|
// Move the icon there |
|
SetPos( iX - halfWidth, iY - (GetTall() / 2) ); |
|
SetAlpha( 255 ); |
|
} |
|
else |
|
{ |
|
// On screen |
|
// If our target isn't visible, we draw transparently |
|
trace_t tr; |
|
UTIL_TraceLine( vecTarget, MainViewOrigin(), MASK_OPAQUE, NULL, COLLISION_GROUP_NONE, &tr ); |
|
if ( tr.fraction >= 1.0f ) |
|
{ |
|
m_bOnscreen = true; |
|
SetAlpha( 0 ); |
|
return; |
|
} |
|
|
|
m_iDrawArrow = DRAW_ARROW_UP; |
|
SetAlpha( 92 ); |
|
SetPos( iX - halfWidth, iY - (GetTall() / 2) ); |
|
} |
|
|
|
m_bOnscreen = false; |
|
BaseClass::PaintBackground(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::Paint( void ) |
|
{ |
|
// Don't draw if our target is visible. The particle effect will be doing it for us. |
|
if ( m_bOnscreen ) |
|
return; |
|
|
|
BaseClass::Paint(); |
|
|
|
if ( m_iDrawArrow == DRAW_ARROW_UP ) |
|
return; |
|
|
|
float uA,uB,yA,yB; |
|
int x,y; |
|
GetPos( x,y ); |
|
if ( m_iDrawArrow == DRAW_ARROW_LEFT ) |
|
{ |
|
uA = 1.0; |
|
uB = 0.0; |
|
yA = 0.0; |
|
yB = 1.0; |
|
} |
|
else |
|
{ |
|
uA = 0.0; |
|
uB = 1.0; |
|
yA = 0.0; |
|
yB = 1.0; |
|
x += GetWide() - MEDICCALLER_ARROW_WIDE; |
|
} |
|
|
|
int iyindent = (GetTall() - MEDICCALLER_ARROW_TALL) * 0.5; |
|
y += iyindent; |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
pRenderContext->Bind( m_pArrowMaterial ); |
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); |
|
|
|
CMeshBuilder meshBuilder; |
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); |
|
|
|
meshBuilder.Position3f( x, y, 0.0f ); |
|
meshBuilder.TexCoord2f( 0, uA, yA ); |
|
meshBuilder.Color4ub( 255, 255, 255, 255 ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Position3f( x + MEDICCALLER_ARROW_WIDE, y, 0.0f ); |
|
meshBuilder.TexCoord2f( 0, uB, yA ); |
|
meshBuilder.Color4ub( 255, 255, 255, 255 ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Position3f( x + MEDICCALLER_ARROW_WIDE, y + MEDICCALLER_ARROW_TALL, 0.0f ); |
|
meshBuilder.TexCoord2f( 0, uB, yB ); |
|
meshBuilder.Color4ub( 255, 255, 255, 255 ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.Position3f( x, y + MEDICCALLER_ARROW_TALL, 0.0f ); |
|
meshBuilder.TexCoord2f( 0, uA, yB ); |
|
meshBuilder.Color4ub( 255, 255, 255, 255 ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::SetEntity( C_BaseEntity *pEntity, float flDuration, Vector &vecOffset ) |
|
{ |
|
m_hEntity = pEntity; |
|
m_flRemoveAt = gpGlobals->curtime + flDuration; |
|
m_vecOffset = vecOffset; |
|
} |
|
|
|
|
|
void CTFMedicCallerPanel::SetMedicCallerType( MedicCallerType nType ) |
|
{ |
|
m_nCallerType = nType; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::AddMedicCaller( C_BaseEntity *pEntity, float flDuration, Vector &vecOffset, MedicCallerType nType /* = CALLER_TYPE_NORMAL */ ) |
|
{ |
|
CTFMedicCallerPanel *pCaller = new CTFMedicCallerPanel( g_pClientMode->GetViewport(), "MedicCallerPanel" ); |
|
vgui::SETUP_PANEL(pCaller); |
|
pCaller->SetBounds( 0,0, MEDICCALLER_WIDE, MEDICCALLER_TALL ); |
|
pCaller->SetEntity( pEntity, flDuration, vecOffset ); |
|
pCaller->SetMedicCallerType( nType ); |
|
pCaller->SetVisible( true ); |
|
vgui::ivgui()->AddTickSignal( pCaller->GetVPanel() ); |
|
pCaller->OnTick(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFMedicCallerPanel::FireGameEvent( IGameEvent *event ) |
|
{ |
|
if ( m_nCallerType == CALLER_TYPE_AUTO ) |
|
{ |
|
if ( Q_strcmp( event->GetName(), "player_calledformedic" ) == 0 ) |
|
{ |
|
if ( m_hEntity && m_hEntity->IsPlayer() ) |
|
{ |
|
C_TFPlayer *pTFPlayer = ToTFPlayer( m_hEntity ); |
|
if ( pTFPlayer ) |
|
{ |
|
int iCaller = engine->GetPlayerForUserID( event->GetInt( "userid" ) ); |
|
if ( pTFPlayer->GetUserID() == iCaller ) |
|
{ |
|
MarkForDeletion(); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|