diff options
Diffstat (limited to 'game/client/tf/tf_hud_passtime_reticle.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_passtime_reticle.cpp | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_passtime_reticle.cpp b/game/client/tf/tf_hud_passtime_reticle.cpp new file mode 100644 index 0000000..a6c26f0 --- /dev/null +++ b/game/client/tf/tf_hud_passtime_reticle.cpp @@ -0,0 +1,629 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "c_func_passtime_goal.h" +#include "c_tf_passtime_logic.h" +#include "tf_hud_passtime_reticle.h" +#include "passtime_convars.h" +#include "tf_weapon_passtime_gun.h" +#include "c_tf_player.h" +#include "view.h" +#include "c_tf_playerresource.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// The team colors from g_PR are wrong and I couldn't fix that fast enough. +// These colors were sampled from HUD art. +static Color GetTeamColor( int iTeam ) +{ + switch( iTeam ) + { + case TF_TEAM_RED: return Color(0xFF, 0x51, 0x51); + case TF_TEAM_BLUE: return Color(0xA5, 0xDE, 0xFF); + default: return Color(0xF5, 0xE7, 0xDE); + }; +} + +//----------------------------------------------------------------------------- +// C_PasstimeReticle +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +C_PasstimeReticle::~C_PasstimeReticle() +{ + for( int i = 0; i < m_pSprites.Count(); ++i ) + { + clienteffects->RemoveEffect( m_pSprites[i] ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::OnClientThink() +{ + if ( !Update() ) + { + SetAllAlphas( 0 ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::AddSprite( CFXQuad *pQuad ) +{ + Assert( pQuad ); + m_pSprites.AddToTail( pQuad ); +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetAllOrigins( const Vector &vec ) +{ + for ( int i = 0; i < m_pSprites.Count(); ++i ) + { + m_pSprites[i]->m_FXData.SetOrigin( vec ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetAllNormals( const Vector &vec ) +{ + for ( int i = 0; i < m_pSprites.Count(); ++i ) + { + m_pSprites[i]->m_FXData.SetNormal( vec ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetAllAlphas( byte iA ) +{ + auto flA = iA / 255.0f; + for ( int i = 0; i < m_pSprites.Count(); ++i ) + { + m_pSprites[i]->m_FXData.SetAlpha( flA, flA ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetAllScales( float flScale ) +{ + for ( int i = 0; i < m_pSprites.Count(); ++i ) + { + m_pSprites[i]->m_FXData.SetScale( flScale, flScale ); + } +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetOrigin( int i, const Vector &vec ) +{ + m_pSprites[i]->m_FXData.SetOrigin( vec ); +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetNormal( int i, const Vector &normal ) +{ + m_pSprites[i]->m_FXData.SetNormal( normal ); +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetAlpha( int i, byte iA ) +{ + auto flA = iA / 255.0f; + m_pSprites[i]->m_FXData.SetAlpha( flA, flA ); +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetRgba( int i, byte iR, byte iG, byte iB, byte iA ) +{ + m_pSprites[i]->m_FXData.SetColor( iR / 255.0f, iG / 255.0f, iB / 255.0f ); + auto flA = iA / 255.0; + m_pSprites[i]->m_FXData.SetAlpha( flA, flA ); +} + +//----------------------------------------------------------------------------- +void C_PasstimeReticle::SetScale( int i, float flScale ) +{ + m_pSprites[i]->m_FXData.SetScale( flScale, flScale ); +} + + +//----------------------------------------------------------------------------- +// C_PasstimeBallReticle +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +static const float k_flBallReticleSize = 64; +C_PasstimeBallReticle::C_PasstimeBallReticle() +{ + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_1", k_flBallReticleSize, 360 ) ); // the O + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_2", k_flBallReticleSize, 360 ) ); // the >< +} + +//----------------------------------------------------------------------------- +bool C_PasstimeBallReticle::Update() +{ + if ( !g_pPasstimeLogic || !g_pPasstimeLogic->GetBall() ) + { + return false; + } + + auto *pBall = g_pPasstimeLogic->GetBall(); + auto *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + C_BaseEntity *pTarget = 0; + auto bHomingActive = false; + auto bHaveTarget = g_pPasstimeLogic->GetBallReticleTarget( &pTarget, &bHomingActive ); + if ( !pBall || !pLocalPlayer || !bHaveTarget ) + { + return false; + } + + auto vecTargetPos = pTarget->WorldSpaceCenter(); + SetAllOrigins( vecTargetPos ); + SetAllNormals( -MainViewForward() ); + + auto teamColor = GetTeamColor( pTarget->GetTeamNumber() ); + auto iAlpha = ( bHomingActive || pLocalPlayer->m_Shared.IsTargetedForPasstimePass() ) + ? (int)( (fmodf( gpGlobals->curtime * 3.0f, 1.0f )) * 255 ) + : 180; + + SetRgba( 0, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + SetRgba( 1, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + + auto flDist = (vecTargetPos - MainViewOrigin()).Length(); + auto flScale = RemapValClamped( flDist, 768.0f, 4096.0f, 1.0f, 3.0f ); + flScale *= k_flBallReticleSize; + if ( bHomingActive || pLocalPlayer->m_Shared.IsTargetedForPasstimePass() ) + { + flScale *= 2; + } + SetScale( 0, flScale ); + SetScale( 1, flScale ); + + return true; +} + +//----------------------------------------------------------------------------- +// C_PasstimeGoalReticle +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +C_PasstimeGoalReticle::C_PasstimeGoalReticle( C_FuncPasstimeGoal *pGoal ) +{ + Assert( pGoal ); + m_hGoal.Set( pGoal ); + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_1", 256, 50 ) ); + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_2", 128, 0 ) ); +} + +//----------------------------------------------------------------------------- +bool C_PasstimeGoalReticle::Update() +{ + if ( !g_pPasstimeLogic || !g_pPasstimeLogic->GetBall() ) + { + return false; + } + + // don't show if ball isn't being carried by local player + auto *pEnt = g_pPasstimeLogic->GetBall()->GetCarrier(); + if ( !pEnt || (pEnt != C_BasePlayer::GetLocalPlayer()) ) + { + return false; + } + + auto *pGoal = m_hGoal.Get(); + if ( !g_pPasstimeLogic || !g_pPasstimeLogic->GetBall() || IsLocalPlayerSpectator() + || !pGoal || pGoal->BGoalTriggerDisabled() || (pGoal->GetTeamNumber() != pEnt->GetTeamNumber()) ) + { + return false; + } + + auto teamColor = GetTeamColor( pEnt->GetTeamNumber() ); + + auto vec = pGoal->WorldSpaceCenter(); + auto facingFrac = MainViewForward().Dot( (vec - MainViewOrigin()).Normalized() ); + if ( facingFrac < 0.6 ) + { + return false; + } + facingFrac = RemapValClamped( facingFrac, 0.8f, 1.0f, 1.0f, 0.3f ); + + // ring + SetOrigin( 0, vec ); + auto flPulseSpeed = 10.0f; + auto flPulseFrac = Clamp( FastCos( gpGlobals->curtime * flPulseSpeed ), 0.0f, 1.0f ); + SetRgba( 0, teamColor.r(), teamColor.g(), teamColor.b(), flPulseFrac * (120 * facingFrac) ); + + // arrow + float tmp; + flPulseFrac = 1.0f - modff( gpGlobals->curtime, &tmp ); + SetOrigin( 1, vec + Vector(0, 0, flPulseFrac * 64) ); + SetAllNormals( -MainViewForward() ); + SetRgba( 1, teamColor.r(), teamColor.g(), teamColor.b(), flPulseFrac * (255 * facingFrac) ); + return true; +} + + +//----------------------------------------------------------------------------- +// C_PasstimePassReticle +//----------------------------------------------------------------------------- + +const float kPassReticleScale = 64; +//----------------------------------------------------------------------------- +C_PasstimePassReticle::C_PasstimePassReticle() +{ + m_flTargetScore = FLT_MAX; + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_passlock", kPassReticleScale, 100 ) ); // the * + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_1", kPassReticleScale, 0 ) ); // the O + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_2", kPassReticleScale, 0 ) ); // the >< +} + +//----------------------------------------------------------------------------- +bool C_PasstimePassReticle::Update() +{ + if ( !g_pPasstimeLogic || !g_pPasstimeLogic->GetBall() || IsLocalPlayerSpectator() ) + { + return false; + } + + auto *pBallCarrier = g_pPasstimeLogic->GetBall()->GetCarrier(); + if ( !pBallCarrier ) + { + return false; + } + if ( (pBallCarrier != C_BasePlayer::GetLocalPlayer()) ) + { + return false; + } + + SetAllNormals( -MainViewForward() ); + + // the player's actual pass target always takes precedence, but if it's + // not set, try to find a candidate and display a hint for that + auto *pPassTarget = pBallCarrier->m_Shared.GetPasstimePassTarget(); + if ( pPassTarget ) + { + m_hTarget = pPassTarget; + auto vecTargetPos = pPassTarget->WorldSpaceCenter(); + SetAllOrigins( vecTargetPos ); + + auto teamColor = GetTeamColor( pBallCarrier->GetTeamNumber() ); + auto neutralColor = GetTeamColor( TEAM_UNASSIGNED ); + auto iAlpha = (int)( (fmodf( gpGlobals->curtime * 3.0f, 1.0f )) * 255 ); + SetRgba( 0, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + SetRgba( 1, neutralColor.r(), neutralColor.g(), neutralColor.b(), 255 ); + SetRgba( 2, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + + auto flDist = (vecTargetPos - MainViewOrigin()).Length(); + auto flScale = RemapValClamped( flDist, 768.0f, 8192.0f, 1.0f, 8.0f ); + SetAllScales( flScale * kPassReticleScale * 2 ); + } + else + { + FindPassHintTarget( pBallCarrier ); + if ( !m_hTarget ) + { + return false; + } + + auto flPulseSpeed = 20; + auto flPulseFrac = Clamp( FastCos( gpGlobals->curtime * flPulseSpeed ), 0.3f, 1.0f ); + auto iAlpha = 200 * flPulseFrac * Clamp( m_flTargetScore + 0.5f, 0.0f, 1.0f ); // higher flScore is better + + auto teamColor = GetTeamColor( TEAM_UNASSIGNED ); + auto neutralColor = GetTeamColor( TEAM_UNASSIGNED ); + SetRgba( 0, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + SetRgba( 1, neutralColor.r(), neutralColor.g(), neutralColor.b(), 80 ); + SetRgba( 2, teamColor.r(), teamColor.g(), teamColor.b(), iAlpha ); + + auto vecTargetPos = m_hTarget->WorldSpaceCenter(); + SetAllOrigins( vecTargetPos ); + + auto flDist = (vecTargetPos - MainViewOrigin()).Length(); + auto flScale = RemapValClamped( flDist, 768.0f, 8192.0f, 1.0f, 8.0f ); + SetAllScales( flScale * kPassReticleScale ); + } + + return true; +} + +//----------------------------------------------------------------------------- +extern int HudTransform( const Vector &point, Vector &screen ); +void C_PasstimePassReticle::FindPassHintTarget( C_TFPlayer *pLocalPlayer ) +{ + m_hTarget = 0; + m_flTargetScore = -FLT_MAX; + + auto flFovDeg = 70; + auto flDotFov = cosf( DEG2RAD( flFovDeg / 2.0f ) ); + auto vecViewPos = MainViewOrigin(); + auto vecViewFwd = MainViewForward(); + + auto flMaxPassDist = g_pPasstimeLogic->GetMaxPassRange() - 400; // arbitrary, based on TF_MAX_SPEED + + // for each player in front of the local player, + for( int i = 1; i <= MAX_PLAYERS; i++ ) + { + auto *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); + if ( !C_PasstimeGun::BValidPassTarget( pLocalPlayer, pPlayer ) ) + { + continue; + } + + auto vecTargetPos = pPlayer->EyePosition(); + auto vecToTarget = vecTargetPos - vecViewPos; + if ( vecToTarget.NormalizeInPlace() > flMaxPassDist ) + { + // the server may disagree on this, so the client is less permissive + // when it guesses in order to prevent false positives + continue; // too far away, probably + } + + auto fDotTarget = vecToTarget.Dot( vecViewFwd ); + if ( fDotTarget <= flDotFov ) + { + continue; // not in front + } + + // determine player's distance from center of screen + Vector vecScreenPos; + auto bBehindViewplane = HudTransform( vecTargetPos, vecScreenPos ); + if ( bBehindViewplane ) + { + continue; // paranoia + } + auto flScore = 0.5f - vecScreenPos.Length2D(); + if ( flScore <= m_flTargetScore ) + { + continue; // someone else already found that's closer + } + + // trace to see if they are visible + // this is the same trace the gun does + trace_t tr; + CTraceFilterSimple tracefilter( pLocalPlayer, COLLISION_GROUP_PROJECTILE ); + UTIL_TraceLine( vecViewPos, vecTargetPos, MASK_PLAYERSOLID, &tracefilter, &tr ); + if ( tr.m_pEnt != pPlayer ) + { + continue; // not visible + } + + m_flTargetScore = flScore; + m_hTarget = pPlayer; + } +} + + +//----------------------------------------------------------------------------- +// C_PasstimeBounceReticle +//----------------------------------------------------------------------------- +C_PasstimeBounceReticle::C_PasstimeBounceReticle() +{ + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_passlock", 160, 0 ) ); // the * + AddSprite( CreateReticleSprite( "passtime/hud/passtime_ball_reticle_piece_1", 80, 200 ) ); // the O +} + +void C_PasstimeBounceReticle::Show( const Vector &vec, const Vector &normal ) +{ + SetOrigin( 0, vec ); + SetOrigin( 1, vec );//+ (normal * 16) ); + SetNormal( 0, normal ); + SetNormal( 1, -MainViewForward() ); + SetRgba( 0, 255, 255, 0, 200 ); + SetRgba( 1, 255, 255, 0, 200 ); +} + +void C_PasstimeBounceReticle::Hide() +{ + SetAllAlphas( 0 ); +} + +bool C_PasstimeBounceReticle::Update() +{ + return !IsLocalPlayerSpectator(); +} + +//----------------------------------------------------------------------------- +// C_PasstimePlayerReticle +//----------------------------------------------------------------------------- +C_PasstimePlayerReticle::C_PasstimePlayerReticle( C_TFPlayer *pPlayer ) +{ + m_hPlayer.Set( pPlayer ); + AddSprite( CreateReticleSprite( "passtime/hud/passtime_teamicon_red", 128, 0 ) ); + AddSprite( CreateReticleSprite( "passtime/hud/passtime_teamicon_blue", 128, 0 ) ); +} + +//----------------------------------------------------------------------------- +bool C_PasstimePlayerReticle::Update() +{ + if ( !g_pPasstimeLogic ) + { + return false; + } + + auto *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + auto *pPlayer = m_hPlayer.Get(); + if ( !pLocalPlayer || pLocalPlayer->IsPlayerDead() + || !pPlayer || pPlayer->IsPlayerDead() ) + { + return false; + } + + if ( pPlayer->m_Shared.GetPercentInvisible() > 0 ) + { + // dont' show because player is invisible, friend or foe + return false; + } + + auto iFriendsDetail = tf_passtime_player_reticles_friends.GetInt(); + auto iEnemiesDetail = tf_passtime_player_reticles_enemies.GetInt(); + + auto bIsDisguisedEnemy = pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) + && ( pPlayer->m_Shared.GetDisguiseTeam() == pLocalPlayer->GetTeamNumber() ) + && !pPlayer->m_Shared.IsFullyInvisible(); + + auto bIsFriend = pLocalPlayer->InSameTeam( pPlayer ) || bIsDisguisedEnemy; + + if ( (bIsFriend && (iFriendsDetail <= 0)) + || (!bIsFriend && (iEnemiesDetail <= 0)) ) + { + // don't show because disabled + return false; + } + + if ( !pLocalPlayer->m_Shared.HasPasstimeBall() + && ((bIsFriend && (iFriendsDetail == 1)) + || (!bIsFriend && (iEnemiesDetail == 1)))) + { + // don't show because not visible unless have ball + return false; + } + + if ( pPlayer->IsDormant() ) + { + // don't show because not getting updated from server for some reason + // probably not in PVS + return false; + } + + auto nTeamNumber = pPlayer->GetTeamNumber(); + if ( !pLocalPlayer->InSameTeam( pPlayer ) && bIsFriend ) // they're not on my team but they're showing as a friend, they must be a spy so use my team color + { + nTeamNumber = pLocalPlayer->GetTeamNumber(); + } + + int iShowSprite, iHideSprite; + if ( nTeamNumber == TF_TEAM_RED ) + { + iShowSprite = 0; + iHideSprite = 1; + } + else if ( nTeamNumber == TF_TEAM_BLUE ) + { + iShowSprite = 1; + iHideSprite = 0; + } + else + { + return false; + } + + auto vecTarget = pPlayer->EyePosition(); + int iX, iY; + auto bOnScreen = GetVectorInHudSpace( vecTarget, iX, iY ); + if ( !bOnScreen ) + { + return false; + } + + trace_t tr; + CTraceFilterIgnorePlayers tracefilter( pLocalPlayer, COLLISION_GROUP_PROJECTILE ); + UTIL_TraceLine( MainViewOrigin(), vecTarget, MASK_PLAYERSOLID, &tracefilter, &tr ); + if ( tr.fraction == 1 ) + { + // made it all the way, the guy is visible so hide the icon + return false; + } + + auto flDist = (vecTarget - MainViewOrigin()).Length(); + auto flScale = RemapValClamped( flDist, 1024.0f, 4096.0f, 80, 128 ); + + SetAlpha( iHideSprite, 0 ); + SetAlpha( iShowSprite, 100 ); + SetAllScales( flScale ); + SetAllOrigins( vecTarget ); + SetAllNormals( -MainViewForward() ); + return true; +} + +//----------------------------------------------------------------------------- +// C_PasstimeAskForBallReticle +//----------------------------------------------------------------------------- +C_PasstimeAskForBallReticle::C_PasstimeAskForBallReticle( C_TFPlayer *pPlayer ) +{ + m_hPlayer.Set( pPlayer ); + AddSprite( CreateReticleSprite( "passtime/hud/passtime_pass_to_me_prompt", 128, 0 ) ); + SetRgba( 0, 255, 255, 255, 200 ); +} + +//----------------------------------------------------------------------------- +bool C_PasstimeAskForBallReticle::Update() +{ + if ( !g_pPasstimeLogic ) + { + return false; + } + + auto *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + auto *pPlayer = m_hPlayer.Get(); + + if ( !pLocalPlayer || !pPlayer ) + { + return false; + } + + auto bLocalPlayerObserver = pLocalPlayer->IsObserver(); + if ( !bLocalPlayerObserver && pLocalPlayer->IsPlayerDead() ) + { + return false; + } + + if ( (pPlayer->m_Shared.AskForBallTime() < gpGlobals->curtime) || pPlayer->IsPlayerDead() ) + { + return false; + } + + if ( !bLocalPlayerObserver && !pLocalPlayer->m_Shared.HasPasstimeBall() && !pPlayer->InSameTeam( pLocalPlayer ) ) + { + return false; + } + + auto vecTarget = pPlayer->EyePosition(); + vecTarget.z += 16; + int iX, iY; + auto bOnScreen = GetVectorInHudSpace( vecTarget, iX, iY ); + if ( !bOnScreen ) + { + return false; + } + + auto flDist = (vecTarget - MainViewOrigin()).Length(); + auto flScale = RemapValClamped( flDist, 1024.0f, 4096.0f, 40, 200 ); + SetAllScales( flScale ); + SetAllOrigins( vecTarget ); + SetAllNormals( -MainViewForward() ); + SetRgba( 0, 255, 255, 255, (((int)(gpGlobals->curtime * 10)) & 1 ? 200 : 0) ); + return true; +} + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CFXQuad *CreateReticleSprite( const char *pModelName, float flScale, float flSpinSpeed ) +{ + FXQuadData_t q; + memset(&q, 0, sizeof(q)); + q.m_Color.Init(1,1,1); + q.m_flDeltaYaw = flSpinSpeed; + q.m_flDieTime = FLT_MAX; + q.m_flEndAlpha = 1; + q.m_flEndScale = flScale; + q.m_flLifeTime = 0; + q.m_flScaleBias = 0; + q.m_flStartAlpha = 1; + q.m_flStartScale = flScale; + q.m_flYaw = 180; + q.SetMaterial( pModelName ); + q.m_uiFlags = 0; + q.m_vecNormal.Init(1,0,0); + q.m_vecOrigin.Init(0,0,0); + CFXQuad *pQuad = new CFXQuad(q); + clienteffects->AddEffect( pQuad ); + return pQuad; +} |