//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include #include "clientmode_csnormal.h" #include "cs_gamerules.h" #include "hud_numericdisplay.h" #include "voice_status.h" #include "c_plantedc4.h" #include "weapon_c4.h" #include "c_cs_hostage.h" #include "c_cs_playerresource.h" #include #include "hud_macros.h" #include "vgui/IVGui.h" #include "vgui/ILocalize.h" #include "mapoverview.h" #include "cstrikespectatorgui.h" #include "hud_radar.h" #define RADAR_DOT_NORMAL 0 #define RADAR_DOT_BOMB (1<<0) #define RADAR_DOT_HOSTAGE (1<<1) #define RADAR_DOT_BOMBCARRIER (1<<2) #define RADAR_DOT_VIP (1<<3) #define RADAR_DOT_LARGE_FLASH (1<<4) #define RADAR_DOT_BOMB_PLANTED (1<<5) #define RADAR_IGNORE_Z (1<<6) //always draw this item as if it was at the same Z as the player extern CUtlVector< CC4* > g_C4s; ConVar cl_radartype( "cl_radartype", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); ConVar cl_radaralpha( "cl_radaralpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 ); ConVar cl_locationalpha( "cl_locationalpha", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, NULL, true, 0, true, 255 ); DECLARE_HUDELEMENT( CHudRadar ); DECLARE_HUD_MESSAGE( CHudRadar, UpdateRadar ); static CHudRadar *s_Radar = NULL; CUtlVector g_RadarFlashes; CHudRadar::CHudRadar( const char *pName ) : vgui::Panel( NULL, "HudRadar" ), CHudElement( pName ) { SetParent( g_pClientMode->GetViewport() ); m_pBackground = NULL; m_pBackgroundTrans = NULL; m_flNextBombFlashTime = 0.0; m_bBombFlash = true; m_flNextHostageFlashTime = 0.0; m_bHostageFlash = true; m_bHideRadar = false; s_Radar = this; SetHiddenBits( HIDEHUD_PLAYERDEAD ); } CHudRadar::~CHudRadar() { s_Radar = NULL; } void CHudRadar::Init() { HOOK_HUD_MESSAGE( CHudRadar, UpdateRadar ); } void CHudRadar::LevelInit() { m_flNextBombFlashTime = 0.0; m_bBombFlash = true; m_flNextHostageFlashTime = 0.0; m_bHostageFlash = true; g_RadarFlashes.RemoveAll(); // Map Overview handles radar duties now. if( g_pMapOverview ) g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR); } void CHudRadar::Reset() { CHudElement::Reset(); if( g_pMapOverview ) g_pMapOverview->SetMode(CCSMapOverview::MAP_MODE_RADAR); } void CHudRadar::MsgFunc_UpdateRadar(bf_read &msg ) { int iPlayerEntity = msg.ReadByte(); //Draw objects on the radar //============================= C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( !pLocalPlayer ) return; C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); if ( !pCSPR ) return; //Players for(int i=1;i<=MAX_PLAYERS;i++) { if( i == pLocalPlayer->entindex() ) continue; C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(i) ); if ( pPlayer ) { pPlayer->m_bDetected = false; } } while ( iPlayerEntity > 0 ) { int x = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; int y = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; int z = msg.ReadSBitLong( COORD_INTEGER_BITS-1 ) * 4; int a = msg.ReadSBitLong( 9 ); C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(iPlayerEntity) ); Vector origin( x, y, z ); QAngle angles( 0, a, 0 ); if ( g_pMapOverview ) { g_pMapOverview->SetPlayerPositions( iPlayerEntity-1, origin, angles ); } iPlayerEntity = msg.ReadByte(); // read index for next player if ( !pPlayer ) continue; bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( pPlayer->entindex() ) != pLocalPlayer->GetTeamNumber()); //============================================================================= // HPE_BEGIN: // [tj] This used to do slightly different logic that caused other players // to twitch while you were observing. //============================================================================= // Don't update players if they are in PVS. if (!pPlayer->IsDormant()) { continue; } //Don't update players if you are sill alive and they are an enemy. if (bOppositeTeams && !pLocalPlayer->IsObserver()) { continue; } //============================================================================= // HPE_END //============================================================================= // update origin and angle for players out of my PVS origin = pPlayer->GetAbsOrigin(); angles = pPlayer->GetAbsAngles(); origin.x = x; origin.y = y; angles.y = a; pPlayer->SetAbsOrigin( origin ); pPlayer->SetAbsAngles( angles ); pPlayer->m_bDetected = true; } } bool CHudRadar::ShouldDraw() { C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); //============================================================================= // HPE_BEGIN: // [tj] Added base class call //============================================================================= return pPlayer && pPlayer->IsAlive() && !m_bHideRadar && CHudElement::ShouldDraw(); //============================================================================= // HPE_END //============================================================================= } void CHudRadar::SetVisible(bool state) { BaseClass::SetVisible(state); if( g_pMapOverview && g_pMapOverview->GetMode() == CCSMapOverview::MAP_MODE_RADAR ) { // We are the hud element still, but he is in charge of the new style now. g_pMapOverview->SetVisible( state ); } } void CHudRadar::Paint() { // We are the hud element still, but Overview is in charge of the new style now. return; } void CHudRadar::WorldToRadar( const Vector location, const Vector origin, const QAngle angles, float &x, float &y, float &z_delta ) { float x_diff = location.x - origin.x; float y_diff = location.y - origin.y; int iRadarRadius = GetWide(); //width of the panel, it resizes now! float fRange = 16 * iRadarRadius; // radar's range float flOffset = atan(y_diff/x_diff); flOffset *= 180; flOffset /= M_PI; if ((x_diff < 0) && (y_diff >= 0)) flOffset = 180 + flOffset; else if ((x_diff < 0) && (y_diff < 0)) flOffset = 180 + flOffset; else if ((x_diff >= 0) && (y_diff < 0)) flOffset = 360 + flOffset; y_diff = -1*(sqrt((x_diff)*(x_diff) + (y_diff)*(y_diff))); x_diff = 0; flOffset = angles.y - flOffset; flOffset *= M_PI; flOffset /= 180; // now theta is in radians float xnew_diff = x_diff * cos(flOffset) - y_diff * sin(flOffset); float ynew_diff = x_diff * sin(flOffset) + y_diff * cos(flOffset); // The dot is out of the radar's range.. Scale it back so that it appears on the border if ( (-1 * y_diff) > fRange ) { float flScale; flScale = ( -1 * y_diff) / fRange; xnew_diff /= flScale; ynew_diff /= flScale; } xnew_diff /= 32; ynew_diff /= 32; //output x = (iRadarRadius/2) + (int)xnew_diff; y = (iRadarRadius/2) + (int)ynew_diff; z_delta = location.z - origin.z; } void CHudRadar::DrawPlayerOnRadar( int iPlayer, C_CSPlayer *pLocalPlayer ) { float x, y, z_delta; int iBaseDotSize = ScreenWidth() / 256; int r, g, b, a = 235; C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); if ( !pCSPR ) return; C_CSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayer ) ); if ( !pPlayer ) return; bool bOppositeTeams = (pLocalPlayer->GetTeamNumber() != TEAM_UNASSIGNED && pCSPR->GetTeam( iPlayer ) != pLocalPlayer->GetTeamNumber()); if ( bOppositeTeams && pPlayer->m_bDetected == false ) return; WorldToRadar( pPlayer->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta ); if( pCSPR->HasC4( iPlayer ) || pCSPR->IsVIP( iPlayer ) || bOppositeTeams ) { r = 250; g = 0; b = 0; } else if ( 0 /*m_bTrackArray[i-1] == true */ ) // Tracked players (friends we want to keep track of on the radar) { iBaseDotSize *= 2; r = 185; g = 20; b = 20; } else { r = 75; g = 75; b = 250; } // Handle the radio flashes bool bRadarFlash = false; if ( g_RadarFlashes.Count() > iPlayer ) bRadarFlash = g_RadarFlashes[iPlayer].m_bRadarFlash && g_RadarFlashes[iPlayer].m_iNumRadarFlashes > 0; if ( bRadarFlash || GetClientVoiceMgr()->IsPlayerSpeaking( iPlayer ) ) { r = 230; g = 110; b = 25; a = 245; DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_LARGE_FLASH, r, g, b, a ); } else { DrawRadarDot( x, y, z_delta, iBaseDotSize, RADAR_DOT_NORMAL, r, g, b, a ); } } void CHudRadar::DrawEntityOnRadar( CBaseEntity *pEnt, C_CSPlayer *pLocalPlayer, int flags, int r, int g, int b, int a ) { float x, y, z_delta; int iBaseDotSize = 4; WorldToRadar( pEnt->GetAbsOrigin(), pLocalPlayer->GetAbsOrigin(), pLocalPlayer->LocalEyeAngles(), x, y, z_delta ); if( flags & RADAR_IGNORE_Z ) z_delta = 0; DrawRadarDot( x, y, z_delta, iBaseDotSize, flags, r, g, b, a ); } void CHudRadar::FillRect( int x, int y, int w, int h ) { int panel_x, panel_y, panel_w, panel_h; GetBounds( panel_x, panel_y, panel_w, panel_h ); vgui::surface()->DrawFilledRect( x, y, x+w, y+h ); } void CHudRadar::DrawRadarDot( int x, int y, float z_diff, int iBaseDotSize, int flags, int r, int g, int b, int a ) { vgui::surface()->DrawSetColor( r, g, b, a ); if ( flags & RADAR_DOT_LARGE_FLASH ) { FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); } else if ( z_diff < -128 ) // below the player { z_diff *= -1; if ( z_diff > 3096 ) { z_diff = 3096; } int iBar = (int)( z_diff / 400 ) + 2; // Draw an upside-down T shape to symbolize the dot is below the player. iBaseDotSize /= 2; //horiz FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize ); //vert FillRect( x, y - iBar*iBaseDotSize, iBaseDotSize, iBar*iBaseDotSize ); } else if ( z_diff > 128 ) // above the player { if ( z_diff > 3096 ) { z_diff = 3096; } int iBar = (int)( z_diff / 400 ) + 2; iBaseDotSize /= 2; // Draw a T shape to symbolize the dot is above the player. //horiz FillRect( x-(2*iBaseDotSize), y, 5*iBaseDotSize, iBaseDotSize ); //vert FillRect( x, y, iBaseDotSize, iBar*iBaseDotSize ); } else { if ( flags & RADAR_DOT_HOSTAGE ) { FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); } else if ( flags & RADAR_DOT_BOMB ) { if ( flags & RADAR_DOT_BOMB_PLANTED ) { iBaseDotSize = 2; // draw an X for the planted bomb FillRect( x, y, iBaseDotSize, iBaseDotSize ); FillRect( x-2, y-2, iBaseDotSize, iBaseDotSize ); FillRect( x-2, y+2, iBaseDotSize, iBaseDotSize ); FillRect( x+2, y-2, iBaseDotSize, iBaseDotSize ); FillRect( x+2, y+2, iBaseDotSize, iBaseDotSize ); } else { FillRect( x-1, y-1, iBaseDotSize+1, iBaseDotSize+1 ); } } else { FillRect( x, y, iBaseDotSize, iBaseDotSize ); } } } void Radar_FlashPlayer( int iPlayer ) { if ( g_RadarFlashes.Count() <= iPlayer ) { g_RadarFlashes.AddMultipleToTail( iPlayer - g_RadarFlashes.Count() + 1 ); } CPlayerRadarFlash *pFlash = &g_RadarFlashes[iPlayer]; pFlash->m_flNextRadarFlashTime = gpGlobals->curtime; pFlash->m_iNumRadarFlashes = 16; pFlash->m_bRadarFlash = false; g_pMapOverview->FlashEntity(iPlayer); } CON_COMMAND( drawradar, "Draws HUD radar" ) { (GET_HUDELEMENT( CHudRadar ))->DrawRadar(); } CON_COMMAND( hideradar, "Hides HUD radar" ) { (GET_HUDELEMENT( CHudRadar ))->HideRadar(); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Location text under radar DECLARE_HUDELEMENT( CHudLocation ); CHudLocation::CHudLocation( const char *pName ) : vgui::Label( NULL, "HudLocation", "" ), CHudElement( pName ) { SetParent( g_pClientMode->GetViewport() ); } void CHudLocation::Init() { // Make sure we get ticked... vgui::ivgui()->AddTickSignal( GetVPanel() ); } void CHudLocation::LevelInit() { } bool CHudLocation::ShouldDraw() { CCSMapOverview *pCSMapOverview = (CCSMapOverview *)GET_HUDELEMENT( CCSMapOverview ); if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_RADAR && pCSMapOverview && pCSMapOverview->ShouldDraw() == true ) return true; else if( g_pMapOverview && g_pMapOverview->GetMode() == CMapOverview::MAP_MODE_INSET ) return true; return false; } void CHudLocation::ApplySchemeSettings(vgui::IScheme *pScheme) { BaseClass::ApplySchemeSettings( pScheme ); m_fgColor = Color( 64, 255, 64, 255 ); SetFont( pScheme->GetFont( "ChatFont" ) ); SetBorder( NULL ); SetBgColor( Color( 0, 0, 0, 0 ) ); SetFgColor( m_fgColor ); } void CHudLocation::OnTick() { m_fgColor[3] = cl_locationalpha.GetInt(); SetFgColor( m_fgColor ); const char *pszLocation = ""; C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( pPlayer ) { pszLocation = pPlayer->GetLastKnownPlaceName(); } SetText( g_pVGuiLocalize->Find( pszLocation ) ); // We have two different locations based on the Overview mode. // So we just position ourselves below, and center our text in their width. if( g_pMapOverview ) { int x = 0, y = 0; int width = 0, height = 0; g_pMapOverview->GetAsPanel()->GetPos( x, y ); g_pMapOverview->GetAsPanel()->GetSize( width, height ); y += g_pMapOverview->GetAsPanel()->GetTall(); SetPos( x, y ); SetWide( width ); } }