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.
371 lines
11 KiB
371 lines
11 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "c_tf_passtime_logic.h"
|
||
|
#include "tf_hud_passtime_ball_offscreen_arrow.h"
|
||
|
#include "iclientmode.h"
|
||
|
#include <vgui/ILocalize.h>
|
||
|
#include <vgui/ISurface.h>
|
||
|
#include <vgui/IVGui.h>
|
||
|
#include "tf_controls.h"
|
||
|
#include "view.h"
|
||
|
#include "ivieweffects.h"
|
||
|
#include "viewrender.h"
|
||
|
#include "prediction.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "c_tf_player.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#define PANEL_WIDE (XRES(48))
|
||
|
#define PANEL_TALL (XRES(48))
|
||
|
#define PANEL_ARROW_WIDE (XRES(24))
|
||
|
#define PANEL_ARROW_TALL (XRES(24))
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static const char *GetBallImageForTeam( int iTeam )
|
||
|
{
|
||
|
switch( iTeam )
|
||
|
{
|
||
|
case TF_TEAM_RED: return "../passtime/hud/passtime_ball_offscreen_red";
|
||
|
case TF_TEAM_BLUE: return "../passtime/hud/passtime_ball_offscreen_blue";
|
||
|
default: return "../passtime/hud/passtime_ball";
|
||
|
};
|
||
|
}
|
||
|
|
||
|
|
||
|
//=============================================================================
|
||
|
// CTFHudPasstimeOffscreenArrow
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFHudPasstimeOffscreenArrow::CTFHudPasstimeOffscreenArrow( Panel *parent, const char *name )
|
||
|
: EditablePanel( parent, name )
|
||
|
, m_pArrowMaterial( 0 )
|
||
|
, m_pImage( 0 )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFHudPasstimeOffscreenArrow::~CTFHudPasstimeOffscreenArrow()
|
||
|
{
|
||
|
if ( m_pArrowMaterial )
|
||
|
{
|
||
|
m_pArrowMaterial->DecrementReferenceCount();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFHudPasstimeOffscreenArrow::ApplySchemeSettings( vgui::IScheme *pScheme )
|
||
|
{
|
||
|
BaseClass::ApplySchemeSettings( pScheme );
|
||
|
LoadControlSettings( "resource/UI/HudPasstimeOffscreenArrow.res" );
|
||
|
SetBounds( 0,0, PANEL_WIDE, PANEL_TALL );
|
||
|
SetVisible( true );
|
||
|
|
||
|
if ( m_pArrowMaterial )
|
||
|
{
|
||
|
m_pArrowMaterial->DecrementReferenceCount();
|
||
|
}
|
||
|
m_pArrowMaterial = materials->FindMaterial( "HUD/medic_arrow", TEXTURE_GROUP_VGUI );
|
||
|
m_pArrowMaterial->IncrementReferenceCount();
|
||
|
|
||
|
m_pImage = FindControl<vgui::ImagePanel>( "Image" );
|
||
|
if ( m_pImage )
|
||
|
{
|
||
|
m_pImage->SetVisible( true );
|
||
|
m_pImage->SetSize( PANEL_WIDE, PANEL_TALL );
|
||
|
m_pImage->SetPos( (GetWide() - m_pImage->GetWide()) * 0.5, (GetTall() - m_pImage->GetTall()) * 0.5 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// TODO what's the difference between Paint and PaintBackground?
|
||
|
extern int HudTransform( const Vector& point, Vector& screen );
|
||
|
void CTFHudPasstimeOffscreenArrow::PaintBackground()
|
||
|
{
|
||
|
// BaseClass::PaintBackground();
|
||
|
if ( !g_pPasstimeLogic )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Let subclasses determine where to point and how to look
|
||
|
//
|
||
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||
|
C_BaseEntity *pEnt = PreparePaint( m_pImage, pLocalPlayer );
|
||
|
|
||
|
//
|
||
|
// Check if everything is valid, friendly, and visible.
|
||
|
//
|
||
|
{
|
||
|
if ( !pEnt || !pLocalPlayer )
|
||
|
{
|
||
|
SetAlpha( 0 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const bool bTargetVisible = (pEnt->GetEffects() & EF_NODRAW) != 0;
|
||
|
const bool bLocalPlayerTarget = (pEnt == pLocalPlayer);
|
||
|
if ( bTargetVisible || bLocalPlayerTarget )
|
||
|
{
|
||
|
SetAlpha( 0 );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine if the arrow should even be visible
|
||
|
// HudTransform works ok as long as the point is on-screen, otherwise it's useless.
|
||
|
// Calling HudTransform here does a lot of redundant work, but I don't care right now.
|
||
|
//
|
||
|
Vector vecEntPos = pEnt->WorldSpaceCenter();
|
||
|
{
|
||
|
Vector vecActualProjection;
|
||
|
const bool bBehind = HudTransform( vecEntPos, vecActualProjection );
|
||
|
const bool bCenterOfScreen =
|
||
|
(vecActualProjection.x < 1.0f) && (vecActualProjection.x > -1.0f)
|
||
|
&& (vecActualProjection.y < 1.0f) && (vecActualProjection.y > -1.0f);
|
||
|
if ( !bBehind && bCenterOfScreen )
|
||
|
{
|
||
|
SetAlpha( 0 );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Definitely visible
|
||
|
|
||
|
//
|
||
|
// Move the target into view space.
|
||
|
// The screen is in the y/z plane.
|
||
|
//
|
||
|
Vector vecLocalTarget;
|
||
|
{
|
||
|
VMatrix mWorldToView( SetupMatrixIdentity() );
|
||
|
const VMatrix mTemp( SetupMatrixOrgAngles( CurrentViewOrigin(), CurrentViewAngles() ) );
|
||
|
MatrixInverseTR( mTemp, mWorldToView );
|
||
|
Vector3DMultiplyPosition( mWorldToView, vecEntPos, vecLocalTarget );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calc the direction in viewspace from the view origin to the target.
|
||
|
// Since all we want is a direction on-screen that goes toward the object, we
|
||
|
// don't need to actually project it. it's transformed to view space to get a direction.
|
||
|
// vecHudDir is always a point on the exact edge of the screen.
|
||
|
//
|
||
|
float flArrowAngle;
|
||
|
{
|
||
|
const Vector vecHudDir = Vector( -vecLocalTarget.y / engine->GetScreenAspectRatio(), -vecLocalTarget.z, 0 ).Normalized();
|
||
|
flArrowAngle = atan2( vecHudDir.y, vecHudDir.x );
|
||
|
// put it in the range 0 to 2PI
|
||
|
if ( flArrowAngle > (M_PI_F * 2.0f) )
|
||
|
flArrowAngle -= (M_PI_F * 2.0f);
|
||
|
else if ( flArrowAngle < 0 )
|
||
|
flArrowAngle += (M_PI_F * 2.0f);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build the mesh for the arrow, because vgui can't rotate
|
||
|
// TODO probably don't need to build this mesh every frame
|
||
|
// The code that draws the arrow mesh has to use the current vgui
|
||
|
// position or there will be a one-frame position lag between the two.
|
||
|
//
|
||
|
{
|
||
|
int iX = PANEL_ARROW_WIDE / 2;
|
||
|
int iY = PANEL_ARROW_TALL / 2;
|
||
|
|
||
|
CMatRenderContextPtr pRenderContext( materials );
|
||
|
pRenderContext->Bind( m_pArrowMaterial );
|
||
|
|
||
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
|
||
|
CMeshBuilder meshBuilder;
|
||
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
|
||
|
|
||
|
meshBuilder.Position3f( -iX, -iY, 0 );
|
||
|
meshBuilder.TexCoord2f( 0, 0, 0 );
|
||
|
meshBuilder.Color4ub( 255, 255, 255, GetAlpha() );
|
||
|
meshBuilder.AdvanceVertex();
|
||
|
|
||
|
meshBuilder.Position3f( iX, -iY, 0 );
|
||
|
meshBuilder.TexCoord2f( 0, 1, 0 );
|
||
|
meshBuilder.Color4ub( 255, 255, 255, GetAlpha() );
|
||
|
meshBuilder.AdvanceVertex();
|
||
|
|
||
|
meshBuilder.Position3f( iX, iY, 0 );
|
||
|
meshBuilder.TexCoord2f( 0, 1, 1 );
|
||
|
meshBuilder.Color4ub( 255, 255, 255, GetAlpha() );
|
||
|
meshBuilder.AdvanceVertex();
|
||
|
|
||
|
meshBuilder.Position3f( -iX, iY, 0 );
|
||
|
meshBuilder.TexCoord2f( 0, 0, 1 );
|
||
|
meshBuilder.Color4ub( 255, 255, 255, GetAlpha() );
|
||
|
meshBuilder.AdvanceVertex();
|
||
|
meshBuilder.End();
|
||
|
|
||
|
// Determine a scale factor based on how close the player is to the ball. Scale is between 1-2.
|
||
|
Vector vecPlayerPos = pLocalPlayer->GetNetworkOrigin();
|
||
|
float flDistanceToTargetSq = (float)(vecEntPos.DistToSqr( vecPlayerPos ));
|
||
|
float flMaxDistanceSq = 1000000.0f;
|
||
|
flDistanceToTargetSq = Min( flDistanceToTargetSq, flMaxDistanceSq );
|
||
|
float flDistanceScale = (flDistanceToTargetSq / flMaxDistanceSq) + 1.0f;
|
||
|
|
||
|
GetPos( iX, iY );
|
||
|
iX += GetWide() / 2;
|
||
|
iY += GetTall() / 2;
|
||
|
pRenderContext->PushMatrix();
|
||
|
pRenderContext->Translate( iX, iY, 0 );
|
||
|
pRenderContext->Rotate( RAD2DEG( flArrowAngle ), 0, 0, 1 );
|
||
|
pRenderContext->Scale( flDistanceScale, 1.0f, 1.0f );
|
||
|
pRenderContext->Translate( GetWide() / 2, 0, 0 );
|
||
|
pMesh->Draw();
|
||
|
pRenderContext->PopMatrix();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Given the angle, calc a position on the screen for the indicator.
|
||
|
// This finds the point at which vecHudDir intersects with the edges of the screen.
|
||
|
// Roughly.
|
||
|
//
|
||
|
Vector vecHudPoint;
|
||
|
{
|
||
|
Assert( flArrowAngle >= 0 && flArrowAngle < (M_PI_F * 2.0f) );
|
||
|
vecHudPoint.y = tanf( flArrowAngle );
|
||
|
if ( fabsf( vecHudPoint.y ) <= 1 )
|
||
|
{
|
||
|
// point will be either along the left or right edge.
|
||
|
// y coord is between top and bottom edges, decide whether it lies on
|
||
|
// the left or right side of the screen.
|
||
|
const float fHalfPi = M_PI_F / 2.0f;
|
||
|
if ( (flArrowAngle < fHalfPi) || (flArrowAngle > (M_PI_F + fHalfPi)) )
|
||
|
vecHudPoint.Init( 1, vecHudPoint.y );
|
||
|
else
|
||
|
vecHudPoint.Init( -1, -vecHudPoint.y );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// point will be either along the top or bottom edge.
|
||
|
// y coord is out of bounds, clamp it and compute x coord
|
||
|
if ( flArrowAngle < M_PI_F )
|
||
|
vecHudPoint.Init( (1 / vecHudPoint.y), 1 );
|
||
|
else
|
||
|
vecHudPoint.Init( (-1 / vecHudPoint.y), -1 );
|
||
|
}
|
||
|
vecHudPoint.x *= 0.8f; // bring toward center.
|
||
|
vecHudPoint.y *= 0.5f;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert to hud coordinates and reposition the vgui control
|
||
|
//
|
||
|
{
|
||
|
const int iX = (0.5f * ( 1.0f + vecHudPoint.x ) * ScreenWidth()) - (GetWide() / 2);
|
||
|
const int iY = (0.5f * ( 1.0f + vecHudPoint.y ) * ScreenHeight()) - (GetTall() / 2);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
// This block avoids calling SetPos with invalid values while debugging,
|
||
|
// because otherwise Paint will never be called again.
|
||
|
const int min = 8;
|
||
|
const int maxx = ScreenWidth() - 8;
|
||
|
const int maxy = ScreenHeight() - 8;
|
||
|
if ( iX > min && iX < maxx && iY > min && iY < maxy )
|
||
|
#endif
|
||
|
SetPos( iX, iY );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//=============================================================================
|
||
|
// CTFHudPasstimeBallOffscreenArrow
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFHudPasstimeBallOffscreenArrow::CTFHudPasstimeBallOffscreenArrow( vgui::Panel *pParent )
|
||
|
: BaseClass( pParent, "PasstimeBallOffscreenArrow" )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_BaseEntity *CTFHudPasstimeBallOffscreenArrow::PreparePaint(
|
||
|
vgui::ImagePanel *pImage, C_TFPlayer *pLocalPlayer )
|
||
|
{
|
||
|
if ( !g_pPasstimeLogic )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
C_PasstimeBall *pBall = g_pPasstimeLogic->GetBall();
|
||
|
C_BaseEntity *pTarget = 0;
|
||
|
bool bHomingActive = false;
|
||
|
bool bHaveTarget = g_pPasstimeLogic->GetBallReticleTarget( &pTarget, &bHomingActive );
|
||
|
if ( !pImage || !pBall || !pLocalPlayer || !bHaveTarget )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( pLocalPlayer->m_Shared.IsTargetedForPasstimePass() || bHomingActive )
|
||
|
{
|
||
|
SetAlpha( (int)( (fmodf( gpGlobals->curtime * 3.0f, 1.0f )) * 255 ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetAlpha( 128 );
|
||
|
}
|
||
|
|
||
|
if ( pImage )
|
||
|
{
|
||
|
// setimage will ignore redundant calls
|
||
|
if ( pTarget )
|
||
|
{
|
||
|
pImage->SetImage( GetBallImageForTeam( pTarget->GetTeamNumber() ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pImage->SetImage( "../passtime/hud/passtime_ball" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pTarget;
|
||
|
}
|
||
|
|
||
|
|
||
|
//=============================================================================
|
||
|
// CTFHudPasstimePlayerOffscreenArrow
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFHudPasstimePlayerOffscreenArrow::CTFHudPasstimePlayerOffscreenArrow( vgui::Panel *pParent, int iPlayerIndex )
|
||
|
: BaseClass( pParent, "PasstimePlayerOffscreenArrow" )
|
||
|
, m_iPlayerIndex( iPlayerIndex )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_BaseEntity *CTFHudPasstimePlayerOffscreenArrow::PreparePaint(
|
||
|
vgui::ImagePanel *pImage, C_TFPlayer *pLocalPlayer )
|
||
|
{
|
||
|
if ( !pImage || (!pLocalPlayer->m_Shared.HasPasstimeBall() && !pLocalPlayer->IsObserver()) )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( m_iPlayerIndex ) );
|
||
|
if ( !pPlayer || (pPlayer == pLocalPlayer) || (pPlayer->m_Shared.AskForBallTime() < gpGlobals->curtime) )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SetAlpha( 128 );
|
||
|
pImage->SetImage( "../passtime/hud/passtime_pass_to_me_prompt" );
|
||
|
return pPlayer;
|
||
|
}
|