diff options
Diffstat (limited to 'game/client/cstrike/VGUI/cstrikespectatorgui.cpp')
| -rw-r--r-- | game/client/cstrike/VGUI/cstrikespectatorgui.cpp | 2324 |
1 files changed, 2324 insertions, 0 deletions
diff --git a/game/client/cstrike/VGUI/cstrikespectatorgui.cpp b/game/client/cstrike/VGUI/cstrikespectatorgui.cpp new file mode 100644 index 0000000..9c633c1 --- /dev/null +++ b/game/client/cstrike/VGUI/cstrikespectatorgui.cpp @@ -0,0 +1,2324 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "cstrikespectatorgui.h" +#include "hud.h" +#include "cs_shareddefs.h" + +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <filesystem.h> +#include "cs_gamerules.h" +#include "c_team.h" +#include "c_cs_playerresource.h" +#include "c_plantedc4.h" +#include "c_cs_hostage.h" +#include "vtf/vtf.h" +#include "clientmode.h" +#include <vgui_controls/AnimationController.h> +#include "voice_status.h" +#include "hud_radar.h" + +using namespace vgui; +DECLARE_HUDELEMENT( CCSMapOverview ) + +extern ConVar overview_health; +extern ConVar overview_names; +extern ConVar overview_tracks; +extern ConVar overview_locked; +extern ConVar overview_alpha; +extern ConVar cl_radaralpha; +ConVar cl_radar_locked( "cl_radar_locked", "0", FCVAR_ARCHIVE, "Lock the angle of the radar display?" ); + +void PreferredOverviewModeChanged( IConVar *pConVar, const char *oldString, float flOldValue ) +{ + ConVarRef var( pConVar ); + char cmd[32]; + V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", var.GetInt() ); + engine->ClientCmd( cmd ); +} +ConVar overview_preferred_mode( "overview_preferred_mode", "1", FCVAR_ARCHIVE, "Preferred overview mode", PreferredOverviewModeChanged ); + +ConVar overview_preferred_view_size( "overview_preferred_view_size", "600", FCVAR_ARCHIVE, "Preferred overview view size" ); + +#define HOSTAGE_RESCUE_DURATION (2.5f) +#define BOMB_FADE_DURATION (2.5f) +#define DEATH_ICON_FADE (7.5f) +#define DEATH_ICON_DURATION (10.0f) +#define LAST_SEEN_ICON_DURATION (4.0f) +#define DIFFERENCE_THRESHOLD (200.0f) + +// To make your own green radar file from the map overview file, turn this on, and include vtf.lib +#define no_GENERATE_RADAR_FILE + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCSSpectatorGUI::CCSSpectatorGUI(IViewPort *pViewPort) : CSpectatorGUI(pViewPort) +{ + m_pCTLabel = NULL; + m_pCTScore = NULL; + m_pTerLabel = NULL; + m_pTerScore = NULL; + m_pTimer = NULL; + m_pTimerLabel = NULL; + m_pDivider = NULL; + m_pExtraInfo = NULL; + + m_modifiedWidths = false; + + m_scoreWidth = 0; + m_extraInfoWidth = 0; + + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + // Grab some control pointers + m_pCTLabel = dynamic_cast<Label *>(FindChildByName("CTScoreLabel")); + m_pCTScore = dynamic_cast<Label *>(FindChildByName("CTScoreValue")); + m_pTerLabel = dynamic_cast<Label *>(FindChildByName("TerScoreLabel")); + m_pTerScore = dynamic_cast<Label *>(FindChildByName("TerScoreValue")); + + m_pTimer = dynamic_cast<Label *>(FindChildByName("timerclock")); + m_pTimerLabel = dynamic_cast<Label *>(FindChildByName("timerlabel")); + + m_pDivider = dynamic_cast<Panel *>(FindChildByName("DividerBar")); + + m_pExtraInfo = dynamic_cast<Label *>(FindChildByName("extrainfo")); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the list of players +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::UpdateSpectatorPlayerList() +{ + C_Team *cts = GetGlobalTeam( TEAM_CT ); + if ( cts ) + { + wchar_t frags[ 10 ]; + _snwprintf( frags, ARRAYSIZE( frags ), L"%i", cts->Get_Score() ); + + SetLabelText( "CTScoreValue", frags ); + } + + C_Team *ts = GetGlobalTeam( TEAM_TERRORIST ); + if ( ts ) + { + wchar_t frags[ 10 ]; + _snwprintf( frags, ARRAYSIZE( frags ), L"%i", ts->Get_Score() ); + + SetLabelText( "TERScoreValue", frags ); + } +} + +bool CCSSpectatorGUI::NeedsUpdate( void ) +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + if ( !player ) + return false; + + if ( m_nLastAccount != player->GetAccount() ) + return true; + + if ( m_nLastTime != (int)CSGameRules()->GetRoundRemainingTime() ) + return true; + + if ( m_nLastSpecMode != player->GetObserverMode() ) + return true; + + if ( m_nLastSpecTarget != player->GetObserverTarget() ) + return true; + + return BaseClass::NeedsUpdate(); +} + +//============================================================================= +// HPE_BEGIN: +// [smessick] +//============================================================================= +void CCSSpectatorGUI::ShowPanel( bool bShow ) +{ + BaseClass::ShowPanel( bShow ); + + if ( bShow ) + { + // Resend the overview command. + char cmd[32]; + V_snprintf( cmd, sizeof( cmd ), "overview_mode %d\n", overview_preferred_mode.GetInt() ); + engine->ClientCmd( cmd ); + } +} +//============================================================================= +// HPE_END +//============================================================================= + +//----------------------------------------------------------------------------- +// Purpose: Updates the timer label if one exists +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::UpdateTimer() +{ + // these could be NULL if players modified the UI + if ( !ControlsPresent() ) + return; + + Color timerColor = m_pTimer->GetFgColor(); + if( g_PlantedC4s.Count() > 0 ) + { + m_pTimer->SetText( "\\" ); // bomb icon + m_pTimerLabel->SetVisible( false ); + + if( g_PlantedC4s[0]->m_flNextGlow > gpGlobals->curtime + 0.1f ) + timerColor[3] = 80; + else + timerColor[3] = 255; + + m_pTimer->SetFgColor( timerColor ); + return; + } + + timerColor[3] = 255; + m_pTimer->SetFgColor( timerColor ); + m_pTimer->SetText( "e" ); // clock icon + + m_nLastTime = (int)( CSGameRules()->GetRoundRemainingTime() ); + + if ( m_nLastTime < 0 ) + m_nLastTime = 0; + + wchar_t szText[ 63 ]; + _snwprintf ( szText, ARRAYSIZE( szText ), L"%d:%02d", (m_nLastTime / 60), (m_nLastTime % 60) ); + szText[62] = 0; + + SetLabelText("timerlabel", szText ); + m_pTimerLabel->SetVisible( true ); +} + +void CCSSpectatorGUI::UpdateAccount() +{ + C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer(); + + if ( !player ) + return; + + m_nLastAccount = player->GetAccount(); + + if ( (player->GetTeamNumber() == TEAM_TERRORIST) || (player->GetTeamNumber() == TEAM_CT) ) + { + wchar_t szText[ 63 ]; + _snwprintf ( szText, ARRAYSIZE( szText ), L"$%i", m_nLastAccount ); + szText[62] = 0; + + SetLabelText( "extrainfo", szText ); + } +} + + +/*bool CCSSpectatorGUI::CanSpectateTeam( int iTeam ) +{ + bool bRetVal = true; + int iTeamOnly = 0;// TODO = gCSViewPortInterface->GetForceCamera(); + + // if we're not a spectator or HLTV and iTeamOnly is set + if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() // && !gEngfuncs.IsSpectateOnly() + && iTeamOnly ) + { + // then we want to force the same team + if ( C_BasePlayer::GetLocalPlayer()->GetTeamNumber() != iTeam ) + { + bRetVal = false; + } + } + + return bRetVal; +}*/ + +void CCSSpectatorGUI::Update() +{ + BaseClass::Update(); + + C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); + + if( pLocalPlayer ) + { + m_nLastSpecMode = pLocalPlayer->GetObserverMode(); + m_nLastSpecTarget = pLocalPlayer->GetObserverTarget(); + } + + UpdateTimer(); + + UpdateAccount(); + + UpdateSpectatorPlayerList(); + + if ( pLocalPlayer ) + { + ResizeControls(); + } + +} + + +//----------------------------------------------------------------------------- +// Purpose: Save off widths for sizing calculations +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::StoreWidths( void ) +{ + if ( !ControlsPresent() ) + return; + + if ( !m_modifiedWidths ) + { + m_scoreWidth = m_pCTScore->GetWide(); + int terScoreWidth = m_pTerScore->GetWide(); + + m_extraInfoWidth = m_pExtraInfo->GetWide(); + + if ( m_scoreWidth != terScoreWidth ) + { + m_pTerScore = NULL; // We're working with a modified res file. Don't muck things up playing with positioning. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Resize controls so scores & map names are not cut off +//----------------------------------------------------------------------------- +void CCSSpectatorGUI::ResizeControls( void ) +{ + if ( !ControlsPresent() ) + return; + + int x1, y1, w1, t1; + int x2, y2, w2, t2; + + StoreWidths(); + + // ensure scores are wide enough + int wCT, hCT, wTer, hTer; + m_pCTScore->GetBounds( x1, y1, w1, t1 ); + m_pCTScore->GetContentSize( wCT, hCT ); + m_pTerScore->GetBounds( x2, y2, w2, t2 ); + m_pTerScore->GetContentSize( wTer, hTer ); + + int desiredScoreWidth = m_scoreWidth; + desiredScoreWidth = MAX( desiredScoreWidth, wCT ); + desiredScoreWidth = MAX( desiredScoreWidth, wTer ); + + int diff = desiredScoreWidth - w1; + if ( diff != 0 ) + { + m_pCTScore->GetBounds( x1, y1, w1, t1 ); + m_pCTScore->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pTerScore->GetBounds( x1, y1, w1, t1 ); + m_pTerScore->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pCTLabel->GetPos( x1, y1 ); + m_pCTLabel->SetPos( x1 - diff, y1 ); + + m_pTerLabel->GetPos( x1, y1 ); + m_pTerLabel->SetPos( x1 - diff, y1 ); + + m_modifiedWidths = true; + } + + // ensure extra info is wide enough + int wExtra, hExtra; + m_pExtraInfo->GetBounds( x1, y1, w1, t1 ); + m_pExtraInfo->GetContentSize( wExtra, hExtra ); + + int desiredExtraWidth = m_extraInfoWidth; + desiredExtraWidth = MAX( desiredExtraWidth, wExtra ); + + diff = desiredExtraWidth - w1; + if ( diff != 0 ) + { + m_pExtraInfo->GetBounds( x1, y1, w1, t1 ); + m_pExtraInfo->SetBounds( x1 - diff, y1, w1 + diff, t1 ); + + m_pTimer->GetPos( x1, y1 ); + m_pTimer->SetPos( x1 - diff, y1 ); + + m_pTimerLabel->GetPos( x1, y1 ); + m_pTimerLabel->SetPos( x1 - diff, y1 ); + + m_pDivider->GetPos( x1, y1 ); + m_pDivider->SetPos( x1 - diff, y1 ); + + m_pCTScore->GetPos( x1, y1 ); + m_pCTScore->SetPos( x1 - diff, y1 ); + + m_pCTLabel->GetPos( x1, y1 ); + m_pCTLabel->SetPos( x1 - diff, y1 ); + + m_pTerScore->GetPos( x1, y1 ); + m_pTerScore->SetPos( x1 - diff, y1 ); + + m_pTerLabel->GetPos( x1, y1 ); + m_pTerLabel->SetPos( x1 - diff, y1 ); + + m_modifiedWidths = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Verify controls are present to resize +//----------------------------------------------------------------------------- +bool CCSSpectatorGUI::ControlsPresent( void ) const +{ + return ( m_pCTLabel != NULL && + m_pCTScore != NULL && + m_pTerLabel != NULL && + m_pTerScore != NULL && + m_pTimer != NULL && + m_pTimerLabel != NULL && + m_pDivider != NULL && + m_pExtraInfo != NULL ); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +static int AdjustValue( int curValue, int targetValue, int amount ) +{ + if ( curValue > targetValue ) + { + curValue -= amount; + + if ( curValue < targetValue ) + curValue = targetValue; + } + else if ( curValue < targetValue ) + { + curValue += amount; + + if ( curValue > targetValue ) + curValue = targetValue; + } + + return curValue; +} + +void CCSMapOverview::InitTeamColorsAndIcons() +{ + BaseClass::InitTeamColorsAndIcons(); + + Q_memset( m_TeamIconsSelf, 0, sizeof(m_TeamIconsSelf) ); + Q_memset( m_TeamIconsDead, 0, sizeof(m_TeamIconsDead) ); + Q_memset( m_TeamIconsOffscreen, 0, sizeof(m_TeamIconsOffscreen) ); + Q_memset( m_TeamIconsDeadOffscreen, 0, sizeof(m_TeamIconsDeadOffscreen) ); + + m_bombIconPlanted = -1; + m_bombIconDropped = -1; + m_bombIconCarried = -1; + m_bombRingPlanted = -1; + m_bombRingDropped = -1; + m_bombRingCarried = -1; + m_bombRingCarriedOffscreen = -1; + m_radioFlash = -1; + m_radioFlashOffscreen = -1; + m_radarTint = -1; + m_hostageFollowing = -1; + m_hostageFollowingOffscreen = -1; + m_playerFacing = -1; + m_cameraIconFirst = -1; + m_cameraIconThird = -1; + m_cameraIconFree = -1; + m_hostageRescueIcon = -1; + m_bombSiteIconA = -1; + m_bombSiteIconB = -1; + + + //setup team red + m_TeamColors[MAP_ICON_T] = COLOR_RED; + m_TeamIcons[MAP_ICON_T] = AddIconTexture( "sprites/player_red_small" ); + m_TeamIconsSelf[MAP_ICON_T] = AddIconTexture( "sprites/player_red_self" ); + m_TeamIconsDead[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead" ); + m_TeamIconsOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_T] = AddIconTexture( "sprites/player_red_dead_offscreen" ); + + // setup team blue + m_TeamColors[MAP_ICON_CT] = COLOR_BLUE; + m_TeamIcons[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_small" ); + m_TeamIconsSelf[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_self" ); + m_TeamIconsDead[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead" ); + m_TeamIconsOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_CT] = AddIconTexture( "sprites/player_blue_dead_offscreen" ); + + // setup team other + m_TeamColors[MAP_ICON_HOSTAGE] = COLOR_GREY; + m_TeamIcons[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_small" ); + m_TeamIconsSelf[MAP_ICON_HOSTAGE] = -1; + m_TeamIconsDead[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead" ); + m_TeamIconsOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_offscreen" ); + m_TeamIconsDeadOffscreen[MAP_ICON_HOSTAGE] = AddIconTexture( "sprites/player_hostage_dead_offscreen" ); + + m_bombIconPlanted = AddIconTexture( "sprites/bomb_planted" ); + m_bombIconDropped = AddIconTexture( "sprites/bomb_dropped" ); + m_bombIconCarried = AddIconTexture( "sprites/bomb_carried" ); + + m_bombRingPlanted = AddIconTexture( "sprites/bomb_planted_ring" ); + m_bombRingDropped = AddIconTexture( "sprites/bomb_dropped_ring" ); + m_bombRingCarried = AddIconTexture( "sprites/bomb_carried_ring" ); + m_bombRingCarriedOffscreen = AddIconTexture( "sprites/bomb_carried_ring_offscreen" ); + + m_hostageFollowing = AddIconTexture( "sprites/hostage_following" ); + m_hostageFollowingOffscreen = AddIconTexture( "sprites/hostage_following_offscreen" ); + m_playerFacing = AddIconTexture( "sprites/player_tick" ); + m_cameraIconFirst = AddIconTexture( "sprites/spectator_eye" ); + m_cameraIconThird = AddIconTexture( "sprites/spectator_3rdcam" ); + m_cameraIconFree = AddIconTexture( "sprites/spectator_freecam" ); + m_hostageRescueIcon = AddIconTexture( "sprites/objective_rescue" );; + m_bombSiteIconA = AddIconTexture( "sprites/objective_site_a" );; + m_bombSiteIconB = AddIconTexture( "sprites/objective_site_b" );; + + m_radioFlash = AddIconTexture("sprites/player_radio_ring"); + m_radioFlashOffscreen = AddIconTexture("sprites/player_radio_ring_offscreen"); + + m_radarTint = AddIconTexture("sprites/radar_trans"); + +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::ApplySchemeSettings(vgui::IScheme *scheme) +{ + BaseClass::ApplySchemeSettings( scheme ); + + m_hIconFont = scheme->GetFont( "DefaultSmall", true ); +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::Update( void ) +{ + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !pPlayer ) + return; + + int team = pPlayer->GetTeamNumber(); + + // if dead with fadetoblack on, we can't show anything + if ( mp_fadetoblack.GetBool() && team > TEAM_SPECTATOR && !pPlayer->IsAlive() ) + { + SetMode( MAP_MODE_OFF ); + return; + } + + bool inRadarMode = (GetMode() == MAP_MODE_RADAR); + int specmode = pPlayer->GetObserverMode(); + // if alive, we can only be in radar mode + if( !inRadarMode && pPlayer->IsAlive()) + { + SetMode( MAP_MODE_RADAR ); + inRadarMode = true; + } + + if( inRadarMode ) + { + if( specmode > OBS_MODE_DEATHCAM ) + { + // If fully dead, we don't want to be radar any more + SetMode( m_playerPreferredMode ); + m_flChangeSpeed = 0; + } + else + { + SetFollowEntity(pPlayer->entindex()); + UpdatePlayers(); + } + } + + BaseClass::Update(); + + if ( GetSpectatorMode() == OBS_MODE_CHASE ) + { + // Follow the local player in chase cam, so the map rotates using the local player's angles + SetFollowEntity( pPlayer->entindex() ); + } +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayerIndex( int index ) +{ + if ( index < 0 || index >= MAX_PLAYERS ) + return NULL; + + return &m_PlayersCSInfo[ index ]; +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForPlayer(MapPlayer_t *player) +{ + if( player == NULL ) + return NULL; + + for( int i = 0; i < MAX_PLAYERS; i++ ) + { + if( &m_Players[i] == player ) + return &m_PlayersCSInfo[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +CCSMapOverview::CSMapPlayer_t* CCSMapOverview::GetCSInfoForHostage(MapPlayer_t *hostage) +{ + if( hostage == NULL ) + return NULL; + + for( int i = 0; i < MAX_HOSTAGES; i++ ) + { + if( &m_Hostages[i] == hostage ) + return &m_HostagesCSInfo[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +#define TIME_SPOTS_STAY_SEEN (0.5f) +#define TIME_UNTIL_ENEMY_SEEN (0.5f) +// rules that define if you can see a player on the overview or not +bool CCSMapOverview::CanPlayerBeSeen( MapPlayer_t *player ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if (!localPlayer || !player ) + return false; + + CSMapPlayer_t *csPlayer = GetCSInfoForPlayer(player); + + if ( !csPlayer ) + return false; + + if( GetMode() == MAP_MODE_RADAR ) + { + // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes. + float now = gpGlobals->curtime; + + if( player->position == Vector(0,0,0) ) + return false; // Invalid guy. + + // draw special icons if within time + if ( csPlayer->overrideExpirationTime != -1 && csPlayer->overrideExpirationTime > gpGlobals->curtime ) + return true; + + // otherwise, not dead people + if( player->health <= 0 ) + return false; + + if( localPlayer->GetTeamNumber() == player->team ) + return true;// always yes for teammates. + + // and a living enemy needs to have been seen recently, and have been for a while + if( csPlayer->timeLastSeen != -1 + && ( now - csPlayer->timeLastSeen < TIME_SPOTS_STAY_SEEN ) + && ( now - csPlayer->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN ) + ) + return true; + + return false; + } + else if( player->health <= 0 ) + { + // Have to be under the overriden icon time to draw when dead. + if ( csPlayer->overrideExpirationTime == -1 || csPlayer->overrideExpirationTime <= gpGlobals->curtime ) + return false; + } + + return BaseClass::CanPlayerBeSeen(player); +} + +bool CCSMapOverview::CanHostageBeSeen( MapPlayer_t *hostage ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( !localPlayer || !hostage ) + return false; + + + CSMapPlayer_t *csHostage = GetCSInfoForHostage(hostage); + + if ( !csHostage ) + return false; + + if( GetMode() == MAP_MODE_RADAR ) + { + // This level will be for all the RadarMode thinking. Base class will be the old way for the other modes. + float now = gpGlobals->curtime; + + if( hostage->position == Vector(0,0,0) ) + return false; // Invalid guy. + + // draw special icons if within time + if ( csHostage->overrideExpirationTime != -1 && csHostage->overrideExpirationTime > gpGlobals->curtime ) + return true; + + // otherwise, not dead people + if( hostage->health <= 0 ) + return false; + + if( localPlayer->GetTeamNumber() == hostage->team ) + return true;// always yes for teammates. + + // and a living enemy needs to have been seen recently + if( csHostage->timeLastSeen != -1 && now - csHostage->timeLastSeen < TIME_SPOTS_STAY_SEEN ) + return true; + + return false; + } + else if( hostage->health <= 0 ) + { + // Have to be under the overriden icon time to draw when dead. + if ( csHostage->overrideExpirationTime == -1 || csHostage->overrideExpirationTime <= gpGlobals->curtime ) + return false; + } + + return BaseClass::CanPlayerBeSeen(hostage); +} + +CCSMapOverview::CCSMapOverview( const char *pElementName ) : BaseClass( pElementName ) +{ + m_nRadarMapTextureID = -1; + + g_pMapOverview = this; // for cvars access etc + + // restore non-radar modes + switch ( overview_preferred_mode.GetInt() ) + { + case MAP_MODE_INSET: + m_playerPreferredMode = MAP_MODE_INSET; + break; + + case MAP_MODE_FULL: + m_playerPreferredMode = MAP_MODE_FULL; + break; + + default: + m_playerPreferredMode = MAP_MODE_OFF; + break; + } +} + +void CCSMapOverview::Init( void ) +{ + BaseClass::Init(); + + // register for events as client listener + ListenForGameEvent( "hostage_killed" ); + ListenForGameEvent( "hostage_rescued" ); + ListenForGameEvent( "bomb_defused" ); + ListenForGameEvent( "bomb_exploded" ); +} + +CCSMapOverview::~CCSMapOverview() +{ + g_pMapOverview = NULL; + + //TODO release Textures ? clear lists +} + +void CCSMapOverview::UpdatePlayers() +{ + if( !m_goalIconsLoaded ) + UpdateGoalIcons(); + + UpdateHostages();// Update before players so players can spot them + + UpdateBomb();// Before players so player can properly spot where it is in this update + + UpdateFlashes(); + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + float now = gpGlobals->curtime; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + if( localMapPlayer == NULL ) + return; + + for ( int i = 1; i<= gpGlobals->maxClients; i++) + { + MapPlayer_t *player = &m_Players[i-1]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1); + + if ( !playerCS ) + continue; + + // update from global player resources + if ( pCSPR->IsConnected(i) ) + { + player->health = pCSPR->GetHealth( i ); + + if ( !pCSPR->IsAlive( i ) ) + { + // Safety actually happens after a TKPunish. + player->health = 0; + playerCS->isDead = true; + } + + if ( player->team != pCSPR->GetTeam( i ) ) + { + player->team = pCSPR->GetTeam( i ); + + if( player == localMapPlayer ) + player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ]; + else + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + } + + Vector position = player->position; + QAngle angles = player->angle; + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer && !pPlayer->IsDormant() ) + { + // update position of active players in our PVS + position = pPlayer->EyePosition(); + angles = pPlayer->EyeAngles(); + + SetPlayerPositions( i-1, position, angles ); + } + } + + if ( GetMode() == MAP_MODE_RADAR ) + { + // Check for teammates spotting the bomb + if ( m_bomb.state != CSMapBomb_t::BOMB_INVALID && pCSPR->IsBombSpotted() ) + { + SetBombSeen( true ); + } + + // Check for teammates spotting enemy players + for ( int i = 1; i<= gpGlobals->maxClients; ++i ) + { + if ( !pCSPR->IsConnected(i) ) + continue; + + if ( !pCSPR->IsAlive(i) ) + continue; + + if ( pCSPR->GetTeam(i) == localMapPlayer->team ) + continue; + + if ( pCSPR->IsPlayerSpotted(i) ) + { + SetPlayerSeen( i-1 ); + + MapPlayer_t *baseEnemy = &m_Players[i-1]; + if( baseEnemy->health > 0 ) + { + // They were just seen, so if they are alive get rid of their "last known" icon + CSMapPlayer_t *csEnemy = GetCSInfoForPlayerIndex(i-1); + + if ( csEnemy ) + { + csEnemy->overrideIcon = -1; + csEnemy->overrideIconOffscreen = -1; + csEnemy->overridePosition = Vector(0, 0, 0); + csEnemy->overrideAngle = QAngle(0, 0, 0); + csEnemy->overrideFadeTime = -1; + csEnemy->overrideExpirationTime = -1; + } + } + } + } + + for( int i = 1; i<= gpGlobals->maxClients; i++ ) + { + MapPlayer_t *player = &m_Players[i-1]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i-1); + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer || !playerCS ) + continue; + + float timeSinceLastSeen = now - playerCS->timeLastSeen; + if( timeSinceLastSeen < 0.25f ) + continue; + if( player->health <= 0 ) + continue;// We don't need to spot dead guys, since they always show + if( player->team == localMapPlayer->team ) + continue;// We don't need to spot our own guys + + // Now that everyone has had a say on people they can see for us, go through and handle baddies that can no longer be seen. + if( playerCS->timeLastSeen != now && player->health > 0 ) + { + // We are not seen now, but if we were seen recently (and for long enough), + // put up a "last known" icon and clear timelastseen + // if they are alive. Death icon is more important, which is why the health check above. + if( timeSinceLastSeen < 0.5f && ( playerCS->timeLastSeen != -1 ) ) + { + if( now - playerCS->timeFirstSeen > TIME_UNTIL_ENEMY_SEEN ) + { + playerCS->overrideIcon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ];; + playerCS->overrideIconOffscreen = m_TeamIconsOffscreen[ GetIconNumberFromTeamNumber(player->team) ]; + playerCS->overridePosition = player->position; + playerCS->overrideFadeTime = -1; + playerCS->overrideExpirationTime = now + LAST_SEEN_ICON_DURATION; + playerCS->overrideAngle = player->angle; + } + playerCS->timeLastSeen = -1; + playerCS->timeFirstSeen = -1; + } + } + } + } +} + +void CCSMapOverview::UpdateHostages() +{ + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + for( int i=0; i < MAX_HOSTAGES; i++ ) + { + if( pCSPR->IsHostageAlive( i ) ) + { + MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) ); + if( hostage == NULL ) + hostage = &m_Hostages[i];// Don't have entry yet, so need one. This'll only happen once, at start of map + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + if( !hostageCS->isDead ) + { + hostage->index = pCSPR->GetHostageEntityID(i); + hostage->position = pCSPR->GetHostagePosition( i ); + hostage->health = 100; // Hostages don't have health available from pCSPR. + hostage->angle = QAngle(0, 0, 0); // No facing, like no health + hostage->team = TEAM_CT; // CT in terms of who sees them + hostage->icon = m_TeamIcons[ MAP_ICON_HOSTAGE ]; // But hostage for icon. + hostage->color = m_TeamColors[ MAP_ICON_HOSTAGE ]; + hostageCS->isHostage = true; + +// engine->Con_NPrintf( i + 15, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z ); + } + else + { +// engine->Con_NPrintf( i + 15, "Mostly Dead" ); + } + } + else + { +// engine->Con_NPrintf( i + 15, "Dead" ); + } + } +} + +void CCSMapOverview::UpdateBomb() +{ + if( m_bomb.state == CSMapBomb_s::BOMB_GONE ) + return;// no more updates until map restart + + float now = gpGlobals->curtime; + + // First, decide if it has been too long since the bomb has been seen to clear visibility timers. + if( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN && m_bomb.timeFirstSeen != -1 ) + { + SetBombSeen( false ); + } + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + float biggestRadius = 0, smallestRadius = 0; + if ( g_PlantedC4s.Count() > 0 ) + { + // bomb is planted + C_PlantedC4 *pC4 = g_PlantedC4s[0]; + + if( pC4->IsBombActive() ) + { + m_bomb.position = pC4->GetAbsOrigin(); + m_bomb.state = CSMapBomb_t::BOMB_PLANTED; + m_bomb.ringTravelTime = 3.0f; + smallestRadius = m_flIconSize; + biggestRadius = m_flIconSize * 15.0f; + } + else + { + // Defused or exploded + m_bomb.state = CSMapBomb_t::BOMB_GONE; + } + } + else if ( pCSPR->HasC4( 0 ) ) + { + // bomb dropped + Vector pos = pCSPR->GetC4Postion(); + + if ( pos.x != 0 || pos.y != 0 || pos.z != 0 ) + { + m_bomb.position = pos; + m_bomb.state = CSMapBomb_t::BOMB_DROPPED; + m_bomb.ringTravelTime = 6.0f; + smallestRadius = m_flIconSize; + biggestRadius = m_flIconSize * 10.0f; + } + else + { + m_bomb.state = CSMapBomb_t::BOMB_INVALID; + //Not a bomb map. Man, what a weird system instead of IsBombMap. If nobody has it, and it isn't on the ground, then it isn't a bomb map. + } + } + else + { + for( int i = 1; i<= gpGlobals->maxClients; i++ ) + { + if( pCSPR->HasC4(i) ) + { + C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if( pPlayer == NULL || pPlayer->IsDormant() ) + { + // Dormant or no player means we are relying on RadarUpdate messages so we can trust the MapOverview position. + MapPlayer_t *player = &m_Players[i-1]; + m_bomb.position = player->position; + } + else + { + // Update players is about to put this Real Data in the player sturct, and we don't want the bomb pos lagged one update behind + m_bomb.position = pPlayer->GetAbsOrigin(); + } + + m_bomb.state = CSMapBomb_t::BOMB_CARRIED; + m_bomb.ringTravelTime = 0; + smallestRadius = m_flIconSize * 1.2f; + biggestRadius = m_flIconSize * 1.2f; + break; + } + } + } + + int alpha = GetMasterAlpha(); + + if( m_bomb.currentRingRadius == m_bomb.maxRingRadius || m_bomb.ringTravelTime == 0 ) + { + m_bomb.currentRingRadius = smallestRadius; + m_bomb.maxRingRadius = biggestRadius; + m_bomb.currentRingAlpha = alpha; + } + else + { + m_bomb.currentRingRadius += (m_bomb.maxRingRadius - m_flIconSize) * gpGlobals->frametime / m_bomb.ringTravelTime; + m_bomb.currentRingRadius = MIN( m_bomb.currentRingRadius, m_bomb.maxRingRadius ); + m_bomb.currentRingAlpha = (alpha - 55) * ((m_bomb.maxRingRadius - m_bomb.currentRingRadius) / (m_bomb.maxRingRadius - m_flIconSize)) + 55; + } +} + +bool CCSMapOverview::ShouldDraw( void ) +{ + int alpha = GetMasterAlpha(); + if( alpha == 0 ) + return false;// we have been set to fully transparent + + //============================================================================= + // HPE_BEGIN: + // [smessick] Turn off large map display when in freezecam. + //============================================================================= + if ( IsInFreezeCam() && GetMode() == MAP_MODE_FULL ) + { + return false; + } + //============================================================================= + // HPE_END + //============================================================================= + + float now = gpGlobals->curtime; + if( GetMode() == MAP_MODE_RADAR ) + { + if ( (GET_HUDELEMENT( CHudRadar ))->ShouldDraw() == false ) + { + return false; + } + + // We have to be alive and not blind to draw in this mode. + C_CSPlayer *pCSPlayer = C_CSPlayer::GetLocalCSPlayer(); + if( !pCSPlayer || pCSPlayer->GetObserverMode() == OBS_MODE_DEATHCAM ) + { + return false; + } + else if (pCSPlayer->m_flFlashBangTime > now) + { + return false; + } + } + + return BaseClass::ShouldDraw(); +} + +CCSMapOverview::MapPlayer_t* CCSMapOverview::GetHostageByEntityID( int entityID ) +{ + for (int i=0; i<MAX_HOSTAGES; i++) + { + if ( m_Hostages[i].index == entityID ) + return &m_Hostages[i]; + } + + return NULL; +} + +CCSMapOverview::MapPlayer_t* CCSMapOverview::GetPlayerByEntityID( int entityID ) +{ + C_BasePlayer *realPlayer = UTIL_PlayerByIndex(entityID); + + if( realPlayer == NULL ) + return NULL; + + for (int i=0; i<MAX_PLAYERS; i++) + { + MapPlayer_t *player = &m_Players[i]; + + if ( player->userid == realPlayer->GetUserID() ) + return player; + } + + return NULL; +} + +#define BORDER_WIDTH 4 +bool CCSMapOverview::AdjustPointToPanel(Vector2D *pos) +{ + if( pos == NULL ) + return false; + + int mapInset = GetBorderSize();// This gives us the amount inside the panel that the background is drawing. + if( mapInset != 0 ) + mapInset += BORDER_WIDTH; // And this gives us the border inside the map edge to give us room for offscreen icons. + + int x,y,w,t; + + //MapTpPanel has already offset the x and y. That's why we use 0 for left and top. + GetBounds( x,y,w,t ); + + bool madeChange = false; + if( pos->x < mapInset ) + { + pos->x = mapInset; + madeChange = true; + } + if( pos->x > w - mapInset ) + { + pos->x = w - mapInset; + madeChange = true; + } + if( pos->y < mapInset ) + { + pos->y = mapInset; + madeChange = true; + } + if( pos->y > t - mapInset ) + { + pos->y = t - mapInset; + madeChange = true; + } + + return madeChange; +} + +void CCSMapOverview::DrawMapTexture() +{ + int alpha = GetMasterAlpha(); + + if( GetMode() == MAP_MODE_FULL ) + SetBgColor( Color(0,0,0,0) );// no background in big mode + else + SetBgColor( Color(0,0,0,alpha * 0.5) ); + + int textureIDToUse = m_nMapTextureID; + bool foundRadarVersion = false; + if( m_nRadarMapTextureID != -1 && GetMode() == MAP_MODE_RADAR ) + { + textureIDToUse = m_nRadarMapTextureID; + foundRadarVersion = true; + } + + int mapInset = GetBorderSize(); + int pwidth, pheight; + GetSize(pwidth, pheight); + + if ( textureIDToUse > 0 ) + { + // We are drawing to the whole panel with a little border + Vector2D panelTL = Vector2D( mapInset, mapInset ); + Vector2D panelTR = Vector2D( pwidth - mapInset, mapInset ); + Vector2D panelBR = Vector2D( pwidth - mapInset, pheight - mapInset ); + Vector2D panelBL = Vector2D( mapInset, pheight - mapInset ); + + // So where are those four points on the great big map? + Vector2D textureTL = PanelToMap( panelTL );// The top left corner of the display is where on the master map? + textureTL /= OVERVIEW_MAP_SIZE;// Texture Vec2D is 0 to 1 + Vector2D textureTR = PanelToMap( panelTR ); + textureTR /= OVERVIEW_MAP_SIZE; + Vector2D textureBR = PanelToMap( panelBR ); + textureBR /= OVERVIEW_MAP_SIZE; + Vector2D textureBL = PanelToMap( panelBL ); + textureBL /= OVERVIEW_MAP_SIZE; + + Vertex_t points[4] = + { + // To draw a textured polygon, the first column is where you want to draw (to), and the second is what you want to draw (from). + // We want to draw to the panel (pulled in for a border), and we want to draw the part of the map texture that should be seen. + // First column is in panel coords, second column is in 0-1 texture coords + Vertex_t( panelTL, textureTL ), + Vertex_t( panelTR, textureTR ), + Vertex_t( panelBR, textureBR ), + Vertex_t( panelBL, textureBL ) + }; + + surface()->DrawSetColor( 255, 255, 255, alpha ); + surface()->DrawSetTexture( textureIDToUse ); + surface()->DrawTexturedPolygon( 4, points ); + } + + // If we didn't find the greenscale version of the map, then at least do a tint. + if( !foundRadarVersion && GetMode() == MAP_MODE_RADAR ) + { + surface()->DrawSetColor( 0,255,0, alpha / 4 ); + surface()->DrawFilledRect( mapInset, mapInset, m_vSize.x - 1 - mapInset, m_vSize.y - 1 - mapInset ); + } +} + +void CCSMapOverview::DrawBomb() +{ + if( m_bomb.state == CSMapBomb_t::BOMB_INVALID ) + return; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + if( localMapPlayer == NULL ) + return; + float now = gpGlobals->curtime; + + if( localMapPlayer->team == TEAM_CT ) + { + // CT's only get to see it if... + + if( localMapPlayer->health <= 0 ) + { + if ( mp_forcecamera.GetInt() != OBS_ALLOW_ALL ) + return;// They're dead and spectating isn't restricted + } + else if( (m_bomb.timeLastSeen == -1) + || ( now - m_bomb.timeLastSeen >= TIME_SPOTS_STAY_SEEN ) + || ( now - m_bomb.timeFirstSeen < TIME_UNTIL_ENEMY_SEEN ) + ) + { + return;// It's in view + } + } + // else if you aren't CT you can always see it + + int bombIcon; + int bombRing; + int bombRingOffscreen; + switch(m_bomb.state) + { + case CSMapBomb_t::BOMB_DROPPED: + { + bombIcon = m_bombIconDropped; + bombRing = m_bombRingDropped; + bombRingOffscreen = m_bombRingDropped; + break; + } + case CSMapBomb_t::BOMB_CARRIED: + { + bombIcon = m_bombIconCarried; + bombRing = m_bombRingCarried; + bombRingOffscreen = m_bombRingCarriedOffscreen; + break; + } + case CSMapBomb_t::BOMB_PLANTED: + { + bombIcon = m_bombIconPlanted; + bombRing = m_bombRingPlanted; + bombRingOffscreen = m_bombRingPlanted; + break; + } + case CSMapBomb_t::BOMB_GONE: + { + bombIcon = m_bombIconPlanted; + bombRing = m_bombRingPlanted; + bombRingOffscreen = m_bombRingPlanted; + break; + } + default: + return; + } + + int alpha = 255; + + if( m_bomb.timeGone != -1 && m_bomb.timeFade <= gpGlobals->curtime ) + alpha *= 1 - ( (float)(gpGlobals->curtime - m_bomb.timeFade) / (float)(m_bomb.timeGone - m_bomb.timeFade) ); + + if( m_bomb.state != CSMapBomb_t::BOMB_GONE ) + DrawIconCS(bombRing, bombRingOffscreen, m_bomb.position, m_bomb.currentRingRadius, 0, m_bomb.currentRingAlpha); + DrawIconCS(bombIcon, bombIcon, m_bomb.position, m_flIconSize, 0, alpha); +} + +bool CCSMapOverview::DrawIconCS( int textureID, int offscreenTextureID, Vector pos, float scale, float angle, int alpha, bool allowRotation, const char *text, Color *textColor, float status, Color *statusColor ) +{ + if( GetMode() == MAP_MODE_RADAR && cl_radaralpha.GetInt() == 0 ) + return false; + + if( alpha <= 0 ) + return false; + + Vector2D pospanel = WorldToMap( pos ); + pospanel = MapToPanel( pospanel ); + + int idToUse = textureID; + float angleToUse = angle; + + Vector2D oldPos = pospanel; + Vector2D adjustment(0,0); + if( AdjustPointToPanel( &pospanel ) ) + { + if( offscreenTextureID == -1 ) + return false; //Doesn't want to draw if off screen. + + // Move it in to on panel, and change the icon. + idToUse = offscreenTextureID; + // And point towards the original spot + adjustment = Vector2D(pospanel.x - oldPos.x, pospanel.y - oldPos.y); + QAngle adjustmentAngles; + Vector adjustment3D(adjustment.x, -adjustment.y, 0); // Y gets flipped in WorldToMap + VectorAngles(adjustment3D, adjustmentAngles) ; + if( allowRotation ) + { + // Some icons don't want to rotate even when off radar + angleToUse = adjustmentAngles[YAW]; + + // And the angle needs to be in world space, not panel space. + if( m_bFollowAngle ) + { + angleToUse += m_fViewAngle; + } + else + { + if ( m_bRotateMap ) + angleToUse += 180.0f; + else + angleToUse += 90.0f; + } + } + + // Don't draw names for icons that are offscreen (bunches up and looks bad) + text = NULL; + } + + int d = GetPixelOffset( scale ); + + Vector offset; + + offset.x = -scale; offset.y = scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos1 = WorldToMap( pos + offset ); + Vector2D pos1Panel = MapToPanel(pos1); + pos1Panel.x += adjustment.x; + pos1Panel.y += adjustment.y; + + offset.x = scale; offset.y = scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos2 = WorldToMap( pos + offset ); + Vector2D pos2Panel = MapToPanel(pos2); + pos2Panel.x += adjustment.x; + pos2Panel.y += adjustment.y; + + offset.x = scale; offset.y = -scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos3 = WorldToMap( pos + offset ); + Vector2D pos3Panel = MapToPanel(pos3); + pos3Panel.x += adjustment.x; + pos3Panel.y += adjustment.y; + + offset.x = -scale; offset.y = -scale; + VectorYawRotate( offset, angleToUse, offset ); + Vector2D pos4 = WorldToMap( pos + offset ); + Vector2D pos4Panel = MapToPanel(pos4); + pos4Panel.x += adjustment.x; + pos4Panel.y += adjustment.y; + + Vertex_t points[4] = + { + Vertex_t( pos1Panel, Vector2D(0,0) ), + Vertex_t( pos2Panel, Vector2D(1,0) ), + Vertex_t( pos3Panel, Vector2D(1,1) ), + Vertex_t( pos4Panel, Vector2D(0,1) ) + }; + + surface()->DrawSetColor( 255, 255, 255, alpha ); + surface()->DrawSetTexture( idToUse ); + surface()->DrawTexturedPolygon( 4, points ); + + pospanel.y += d + 4; + + if ( status >=0.0f && status <= 1.0f && statusColor ) + { + // health bar is 50x3 pixels + surface()->DrawSetColor( 0,0,0,255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x+d, pospanel.y+1 ); + + int length = (float)(d*2)*status; + surface()->DrawSetColor( statusColor->r(), statusColor->g(), statusColor->b(), 255 ); + surface()->DrawFilledRect( pospanel.x-d, pospanel.y-1, pospanel.x-d+length, pospanel.y+1 ); + + pospanel.y += 3; + } + + if ( text && textColor ) + { + wchar_t iconText[ MAX_PLAYER_NAME_LENGTH*2 ]; + + g_pVGuiLocalize->ConvertANSIToUnicode( text, iconText, sizeof( iconText ) ); + + int wide, tall; + surface()->GetTextSize( m_hIconFont, iconText, wide, tall ); + + int x = pospanel.x-(wide/2); + int y = pospanel.y; + + // draw black shadow text + surface()->DrawSetTextColor( 0, 0, 0, 255 ); + surface()->DrawSetTextPos( x+1, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + + // draw name in color + surface()->DrawSetTextColor( textColor->r(), textColor->g(), textColor->b(), 255 ); + surface()->DrawSetTextPos( x, y ); + surface()->DrawPrintText( iconText, wcslen(iconText) ); + } + + return true; +} + +void CCSMapOverview::DrawMapPlayers() +{ + DrawGoalIcons(); + DrawHostages(); + + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + surface()->DrawSetTextFont( m_hIconFont ); + + Color colorGreen( 0, 255, 0, 255 ); // health bar color + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + for (int i=0; i < MAX_PLAYERS; i++) + { + int alpha = 255; + MapPlayer_t *player = &m_Players[i]; + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i); + + if ( !playerCS ) + continue; + + if ( !CanPlayerBeSeen( player ) ) + continue; + + float status = -1; + const char *name = NULL; + + if ( m_bShowNames && CanPlayerNameBeSeen( player ) ) + name = player->name; + + if ( m_bShowHealth && CanPlayerHealthBeSeen( player ) ) + status = player->health/100.0f; + + // Now draw them + if( playerCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon + { + int alphaToUse = alpha; + if( playerCS->overrideFadeTime != -1 && playerCS->overrideFadeTime <= gpGlobals->curtime ) + { + // Fade linearly from fade start to disappear + alphaToUse *= 1 - (float)(gpGlobals->curtime - playerCS->overrideFadeTime) / (float)(playerCS->overrideExpirationTime - playerCS->overrideFadeTime); + } + + DrawIconCS( playerCS->overrideIcon, playerCS->overrideIconOffscreen, playerCS->overridePosition, m_flIconSize * 1.1f, GetViewAngle(), player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, -1, &colorGreen ); + if( player->health > 0 ) + DrawIconCS( m_playerFacing, -1, playerCS->overridePosition, m_flIconSize * 1.1f, playerCS->overrideAngle[YAW], player->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &player->color, status, &colorGreen ); + } + else + { + float zDifference = 0; + if( localPlayer ) + { + if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() ) + zDifference = player->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z; + else + zDifference = player->position.z - localPlayer->GetAbsOrigin().z; + } + + float sizeForRing = m_flIconSize * 1.4f; + float sizeForPlayer = m_flIconSize * 1.1f; // The 1.1 is because the player dots are shrunken a little, so their facing pip can have some space to live + if ( zDifference > DIFFERENCE_THRESHOLD ) + { + // A dot above is bigger and a little fuzzy now. + sizeForRing *= 1.4f; + sizeForPlayer *= 1.4f; + alpha *= 0.5f; + } + else if ( zDifference < -DIFFERENCE_THRESHOLD ) + { + // A dot below is smaller. + sizeForRing *= 0.7f; + sizeForPlayer *= 0.7f; + } + + bool showTalkRing = localPlayer && (localPlayer->GetTeamNumber() == player->team || localPlayer->GetTeamNumber() == TEAM_SPECTATOR); + + if( showTalkRing && playerCS->currentFlashAlpha > 0 )// Flash type + { + // Make them flash a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], playerCS->currentFlashAlpha); + } + else if( showTalkRing && pCSPR->IsAlive( i + 1 ) && GetClientVoiceMgr()->IsPlayerSpeaking( i + 1) ) // Or solid on type + { + // Make them show a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, player->position, sizeForRing, player->angle[YAW], 255); + } + + bool doingLocalPlayer = GetPlayerByUserID(localPlayer->GetUserID()) == player; + float angleForPlayer = GetViewAngle(); + + if( doingLocalPlayer ) + { + sizeForPlayer *= 4.0f; // The self icon is really big since it has a camera view cone attached. + angleForPlayer = player->angle[YAW];// And, the self icon now rotates, natch. + } + + int offscreenIcon = m_TeamIconsOffscreen[GetIconNumberFromTeamNumber(player->team)]; + DrawIconCS( player->icon, offscreenIcon, player->position, sizeForPlayer, angleForPlayer, alpha, true, name, &player->color, status, &colorGreen ); + if( !doingLocalPlayer ) + { + // Draw the facing for everyone but the local player. + if( player->health > 0 ) + DrawIconCS( m_playerFacing, -1, player->position, sizeForPlayer, player->angle[YAW], alpha, true, name, &player->color, status, &colorGreen ); + } + } + } + + DrawBomb();// After players so it can draw on top +} + +void CCSMapOverview::DrawHostages() +{ + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + surface()->DrawSetTextFont( m_hIconFont ); + + Color colorGreen( 0, 255, 0, 255 ); // health bar color + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + for (int i=0; i < MAX_HOSTAGES; i++) + { + int alpha = 255; + MapPlayer_t *hostage = GetHostageByEntityID( pCSPR->GetHostageEntityID(i) ); + if( hostage == NULL ) + continue; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + continue; + + if ( !CanHostageBeSeen( hostage ) ) + { +// engine->Con_NPrintf( i + 30, "Can't be seen." ); + continue; + } + + float status = -1; + const char *name = NULL; + + if( hostageCS->overrideExpirationTime > gpGlobals->curtime )// If dead, an X, if alive, an alpha'd normal icon + { +// engine->Con_NPrintf( i + 30, "ID:%d Override Pos:(%.0f,%.0f,%.0f)", hostage->index, hostageCS->overridePosition.x, hostageCS->overridePosition.y, hostageCS->overridePosition.z ); + int alphaToUse = alpha; + if( hostageCS->overrideFadeTime != -1 && hostageCS->overrideFadeTime <= gpGlobals->curtime ) + { + // Fade linearly from fade start to disappear + alphaToUse *= 1 - (float)(gpGlobals->curtime - hostageCS->overrideFadeTime) / (float)(hostageCS->overrideExpirationTime - hostageCS->overrideFadeTime); + } + + DrawIconCS( hostageCS->overrideIcon, hostageCS->overrideIconOffscreen, hostageCS->overridePosition, m_flIconSize, hostageCS->overrideAngle[YAW], hostage->health > 0 ? alphaToUse / 2 : alphaToUse, true, name, &hostage->color, status, &colorGreen ); + } + else + { + if( localPlayer && localPlayer->GetTeamNumber() == hostage->team && hostageCS->currentFlashAlpha > 0 ) + { + // Make them flash a halo + DrawIconCS(m_radioFlash, m_radioFlashOffscreen, hostage->position, m_flIconSize * 1.4f, hostage->angle[YAW], hostageCS->currentFlashAlpha); + } + +// engine->Con_NPrintf( i + 30, "ID:%d Pos:(%.0f,%.0f,%.0f)", hostage->index, hostage->position.x, hostage->position.y, hostage->position.z ); + int normalIcon, offscreenIcon; + float zDifference = 0; + if( localPlayer ) + { + if( (localPlayer->GetObserverMode() != OBS_MODE_NONE) && localPlayer->GetObserverTarget() ) + zDifference = hostage->position.z - localPlayer->GetObserverTarget()->GetAbsOrigin().z; + else + zDifference = hostage->position.z - localPlayer->GetAbsOrigin().z; + } + + float sizeForHostage = m_flIconSize; + if( zDifference > DIFFERENCE_THRESHOLD ) + { + // A dot above is bigger and a little fuzzy now. + sizeForHostage = m_flIconSize * 1.5f; + alpha *= 0.5f; + } + else if( zDifference < -DIFFERENCE_THRESHOLD ) + { + // A dot below is smaller. + sizeForHostage = m_flIconSize * 0.6f; + } + + normalIcon = hostage->icon; + offscreenIcon = m_TeamIconsOffscreen[ MAP_ICON_HOSTAGE ]; + DrawIconCS( normalIcon, offscreenIcon, hostage->position, sizeForHostage, GetViewAngle(), alpha, true, name, &hostage->color, status, &colorGreen ); + + if( pCSPR->IsHostageFollowingSomeone( i ) ) + { + // If they are following a CT, then give them a little extra symbol to show it. + DrawIconCS( m_hostageFollowing, m_hostageFollowingOffscreen, hostage->position, sizeForHostage, hostage->angle[YAW], alpha ); + } + } + } +} +void CCSMapOverview::SetMap(const char * levelname) +{ + BaseClass::SetMap(levelname); + + int wide, tall; + surface()->DrawGetTextureSize( m_nMapTextureID, wide, tall ); + if( wide == 0 && tall == 0 ) + { + m_nMapTextureID = -1; + m_nRadarMapTextureID = -1; + return;// No map image, so no radar image + } + + char radarFileName[MAX_PATH]; + Q_snprintf(radarFileName, MAX_PATH, "%s_radar", m_MapKeyValues->GetString("material")); + m_nRadarMapTextureID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile(m_nRadarMapTextureID, radarFileName, true, false); + int radarWide = -1; + int radarTall = -1; + surface()->DrawGetTextureSize(m_nRadarMapTextureID, radarWide, radarTall); + bool radarTextureFound = false; + if( radarWide == wide && radarTall == tall ) + { + // Unbelievable that these is no failure return from SetTextureFile, and not + // even a ValidTexture check on the ID. So I can check if it is different from + // the original. It'll be a 32x32 default if not present. + radarTextureFound = true; + } + + if( !radarTextureFound ) + { + if( !CreateRadarImage(m_MapKeyValues->GetString("material"), radarFileName) ) + m_nRadarMapTextureID = -1; + } + + ClearGoalIcons(); +} + +bool CCSMapOverview::CreateRadarImage(const char *mapName, const char * radarFileName) +{ +#ifdef GENERATE_RADAR_FILE + char fullFileName[MAX_PATH]; + Q_snprintf(fullFileName, MAX_PATH, "materials/%s.vtf", mapName); + char fullRadarFileName[MAX_PATH]; + Q_snprintf(fullRadarFileName, MAX_PATH, "materials/%s.vtf", radarFileName); + + // Not found, so try to make one + FileHandle_t fp; + fp = ::filesystem->Open( fullFileName, "rb" ); + if( !fp ) + { + return false; + } + ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_TAIL ); + int srcVTFLength = ::filesystem->Tell( fp ); + ::filesystem->Seek( fp, 0, FILESYSTEM_SEEK_HEAD ); + + CUtlBuffer buf; + buf.EnsureCapacity( srcVTFLength ); + int overviewMapBytesRead = ::filesystem->Read( buf.Base(), srcVTFLength, fp ); + ::filesystem->Close( fp ); + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );// Need to set these explicitly since ->Read goes straight to memory and skips them. + buf.SeekPut( CUtlBuffer::SEEK_HEAD, overviewMapBytesRead ); + + IVTFTexture *radarTexture = CreateVTFTexture(); + if (radarTexture->Unserialize(buf)) + { + ImageFormat oldImageFormat = radarTexture->Format(); + radarTexture->ConvertImageFormat(IMAGE_FORMAT_RGBA8888, false); + unsigned char *imageData = radarTexture->ImageData(0,0,0); + int size = radarTexture->ComputeTotalSize(); // in bytes! + unsigned char *pEnd = imageData + size; + + for( ; imageData < pEnd; imageData += 4 ) + { + imageData[0] = 0; // R + imageData[2] = 0; // B + } + + radarTexture->ConvertImageFormat(oldImageFormat, false); + + buf.Clear(); + radarTexture->Serialize(buf); + + fp = ::filesystem->Open(fullRadarFileName, "wb"); + ::filesystem->Write(buf.Base(), buf.TellPut(), fp); + ::filesystem->Close(fp); + DestroyVTFTexture(radarTexture); + buf.Purge(); + + // And need a vmt file to go with it. + char vmtFilename[MAX_PATH]; + Q_snprintf(vmtFilename, MAX_PATH, "%s", fullRadarFileName); + char *extension = &vmtFilename[Q_strlen(vmtFilename) - 3]; + *extension++ = 'v'; + *extension++ = 'm'; + *extension++ = 't'; + *extension++ = '\0'; + fp = ::filesystem->Open(vmtFilename, "wt"); + ::filesystem->Write("\"UnlitGeneric\"\n", 15, fp); + ::filesystem->Write("{\n", 2, fp); + ::filesystem->Write("\t\"$translucent\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$basetexture\" \"", 17, fp); + ::filesystem->Write(radarFileName, Q_strlen(radarFileName), fp); + ::filesystem->Write("\"\n", 2, fp); + ::filesystem->Write("\t\"$vertexalpha\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$vertexcolor\" \"1\"\n", 20, fp); + ::filesystem->Write("\t\"$no_fullbright\" \"1\"\n", 22, fp); + ::filesystem->Write("\t\"$ignorez\" \"1\"\n", 16, fp); + ::filesystem->Write("}\n", 2, fp); + ::filesystem->Close(fp); + + m_nRadarMapTextureID = surface()->CreateNewTextureID(); + surface()->DrawSetTextureFile( m_nRadarMapTextureID, radarFileName, true, true); + return true; + } +#endif + return false; +} + +void CCSMapOverview::ResetRound() +{ + BaseClass::ResetRound(); + + for (int i=0; i<MAX_PLAYERS; i++) + { + CSMapPlayer_t *p = &m_PlayersCSInfo[i]; + + p->isDead = false; + + p->overrideFadeTime = -1; + p->overrideExpirationTime = -1; + p->overrideIcon = -1; + p->overrideIconOffscreen = -1; + p->overridePosition = Vector( 0, 0, 0); + p->overrideAngle = QAngle(0, 0, 0); + + p->timeLastSeen = -1; + p->timeFirstSeen = -1; + p->isHostage = false; + + p->flashUntilTime = -1; + p->nextFlashPeakTime = -1; + p->currentFlashAlpha = 0; + } + + for (int i=0; i<MAX_HOSTAGES; i++) + { + MapPlayer_t *basep = &m_Hostages[i]; + CSMapPlayer_t *p = &m_HostagesCSInfo[i]; + + basep->health = 100; + Q_memset( basep->trail, 0, sizeof(basep->trail) ); + basep->position = Vector( 0, 0, 0 ); + basep->index = 0; + + p->isDead = false; + + p->overrideFadeTime = -1; + p->overrideExpirationTime = -1; + p->overrideIcon = -1; + p->overrideIconOffscreen = -1; + p->overridePosition = Vector( 0, 0, 0); + p->overrideAngle = QAngle(0, 0, 0); + + p->timeLastSeen = -1; + p->timeFirstSeen = -1; + p->isHostage = false; + + p->flashUntilTime = -1; + p->nextFlashPeakTime = -1; + p->currentFlashAlpha = 0; + } + + m_bomb.position = Vector(0,0,0); + m_bomb.state = CSMapBomb_t::BOMB_INVALID; + m_bomb.timeLastSeen = -1; + m_bomb.timeFirstSeen = -1; + m_bomb.timeFade = -1; + m_bomb.timeGone = -1; + + m_bomb.currentRingRadius = -1; + m_bomb.currentRingAlpha = -1; + m_bomb.maxRingRadius = -1; + m_bomb.ringTravelTime = -1; + + m_goalIconsLoaded = false; +} + +void CCSMapOverview::DrawCamera() +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if (!localPlayer) + return; + + if( localPlayer->GetObserverMode() == OBS_MODE_ROAMING ) + { + // Instead of the programmer-art red dot, we'll draw an icon for when our camera is roaming. + int alpha = 255; + DrawIconCS(m_cameraIconFree, m_cameraIconFree, localPlayer->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha); + } + else if( localPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) + { + if( localPlayer->GetObserverTarget() ) + { + // Fade it if it is on top of a player dot. And don't rotate it. + int alpha = 255 * 0.5f; + DrawIconCS(m_cameraIconFirst, m_cameraIconFirst, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 1.5f, GetViewAngle(), alpha); + } + } + else if( localPlayer->GetObserverMode() == OBS_MODE_CHASE ) + { + if( localPlayer->GetObserverTarget() ) + { + // Or Draw the third-camera a little bigger. (Needs room to be off the dot being followed) + int alpha = 255; + DrawIconCS(m_cameraIconThird, m_cameraIconThird, localPlayer->GetObserverTarget()->GetAbsOrigin(), m_flIconSize * 3.0f, localPlayer->EyeAngles()[YAW], alpha); + } + } +} + +void CCSMapOverview::FireGameEvent( IGameEvent *event ) +{ + const char * type = event->GetName(); + + if ( Q_strcmp(type,"hostage_killed") == 0 ) + { + MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") ); + +// DevMsg("Hostage id %d just died.\n", event->GetInt("hostage")); + + if ( !hostage ) + return; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + hostage->health = 0; + hostageCS->isDead = true; + Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails + + hostageCS->overrideIcon = m_TeamIconsDead[MAP_ICON_HOSTAGE]; + hostageCS->overrideIconOffscreen = hostageCS->overrideIcon; + hostageCS->overridePosition = hostage->position; + hostageCS->overrideAngle = hostage->angle; + hostageCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE; + hostageCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION; + } + else if ( Q_strcmp(type,"hostage_rescued") == 0 ) + { + MapPlayer_t *hostage = GetHostageByEntityID( event->GetInt("hostage") ); + +// DevMsg("Hostage id %d just got rescued.\n", event->GetInt("hostage")); + + if ( !hostage ) + return; + + CSMapPlayer_t *hostageCS = GetCSInfoForHostage(hostage); + + if ( !hostageCS ) + return; + + hostage->health = 0; + hostageCS->isDead = true; + Q_memset( hostage->trail, 0, sizeof(hostage->trail) ); // clear trails + + hostageCS->overrideIcon = hostage->icon; + hostageCS->overrideIconOffscreen = -1; + hostageCS->overridePosition = hostage->position; + hostageCS->overrideAngle = hostage->angle; + hostageCS->overrideFadeTime = gpGlobals->curtime; + hostageCS->overrideExpirationTime = gpGlobals->curtime + HOSTAGE_RESCUE_DURATION; + } + else if ( Q_strcmp(type,"bomb_defused") == 0 ) + { + m_bomb.state = CSMapBomb_t::BOMB_GONE; + m_bomb.timeFade = gpGlobals->curtime; + m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION; + } + else if ( Q_strcmp(type,"bomb_exploded") == 0 ) + { + m_bomb.state = CSMapBomb_t::BOMB_GONE; + m_bomb.timeFade = gpGlobals->curtime; + m_bomb.timeGone = gpGlobals->curtime + BOMB_FADE_DURATION; + } + else if ( Q_strcmp(type,"player_death") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + player->health = 0; + Q_memset( player->trail, 0, sizeof(player->trail) ); // clear trails + + CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player); + + if ( !playerCS ) + return; + + playerCS->isDead = true; + playerCS->overrideIcon = m_TeamIconsDead[GetIconNumberFromTeamNumber(player->team)]; + playerCS->overrideIconOffscreen = playerCS->overrideIcon; + playerCS->overridePosition = player->position; + playerCS->overrideAngle = player->angle; + playerCS->overrideFadeTime = gpGlobals->curtime + DEATH_ICON_FADE; + playerCS->overrideExpirationTime = gpGlobals->curtime + DEATH_ICON_DURATION; + } + else if ( Q_strcmp(type,"player_team") == 0 ) + { + MapPlayer_t *player = GetPlayerByUserID( event->GetInt("userid") ); + + if ( !player ) + return; + + CBasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + if( localPlayer == NULL ) + return; + MapPlayer_t *localMapPlayer = GetPlayerByUserID(localPlayer->GetUserID()); + + player->team = event->GetInt("team"); + + if( player == localMapPlayer ) + player->icon = m_TeamIconsSelf[ GetIconNumberFromTeamNumber(player->team) ]; + else + player->icon = m_TeamIcons[ GetIconNumberFromTeamNumber(player->team) ]; + + player->color = m_TeamColors[ GetIconNumberFromTeamNumber(player->team) ]; + } + else + { + BaseClass::FireGameEvent(event); + } +} + +void CCSMapOverview::SetMode(int mode) +{ + if ( mode == MAP_MODE_RADAR ) + { + m_flChangeSpeed = 0; // change size instantly + // We want the _output_ of the radar to be consistant, so we need to take the map scale in to account. + float desiredZoom = (DESIRED_RADAR_RESOLUTION * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0, 0, vgui::AnimationController::INTERPOLATOR_LINEAR ); + + if( CBasePlayer::GetLocalPlayer() ) + SetFollowEntity( CBasePlayer::GetLocalPlayer()->entindex() ); + + SetPaintBackgroundType( 2 );// rounded corners + ShowPanel( true ); + } + else if ( mode == MAP_MODE_INSET ) + { + SetPaintBackgroundType( 2 );// rounded corners + + float desiredZoom = (overview_preferred_view_size.GetFloat() * m_fMapScale) / (OVERVIEW_MAP_SIZE * m_fFullZoom); + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + } + else + { + SetPaintBackgroundType( 0 );// square corners + + float desiredZoom = 1.0f; + + g_pClientMode->GetViewportAnimationController()->RunAnimationCommand( this, "zoom", desiredZoom, 0.0f, 0.2f, vgui::AnimationController::INTERPOLATOR_LINEAR ); + } + + BaseClass::SetMode(mode); +} + +void CCSMapOverview::UpdateSizeAndPosition() +{ + int x,y,w,h; + + vgui::surface()->GetScreenSize( w, h ); + + switch( m_nMode ) + { + case MAP_MODE_RADAR: + { + // To allow custom hud scripts to work, get our size from the HudRadar element that people already tweak. + int x, y, w, t; + (GET_HUDELEMENT( CHudRadar ))->GetBounds(x, y, w, t); + m_vPosition.x = x; + m_vPosition.y = y; + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + } + + m_vSize.x = w; + m_vSize.y = w;// Intentionally not 't'. We need to enforce square-ness to prevent people from seeing more of the map by fiddling their HudLayout + break; + } + + case MAP_MODE_INSET: + { + m_vPosition.x = XRES(16); + m_vPosition.y = YRES(16); + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + } + + m_vSize.x = w/4; + m_vSize.y = m_vSize.x/1.333; + break; + } + + case MAP_MODE_FULL: + default: + { + m_vSize.x = w; + m_vSize.y = h; + + m_vPosition.x = 0; + m_vPosition.y = 0; + + if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() ) + { + m_vPosition.y += g_pSpectatorGUI->GetTopBarHeight(); + m_vSize.y -= g_pSpectatorGUI->GetTopBarHeight(); + m_vSize.y -= g_pSpectatorGUI->GetBottomBarHeight(); + } + break; + } + } + + GetBounds( x,y,w,h ); + + if ( m_flChangeSpeed > 0 ) + { + // adjust slowly + int pixels = m_flChangeSpeed * gpGlobals->frametime; + x = AdjustValue( x, m_vPosition.x, pixels ); + y = AdjustValue( y, m_vPosition.y, pixels ); + w = AdjustValue( w, m_vSize.x, pixels ); + h = AdjustValue( h, m_vSize.y, pixels ); + } + else + { + // set instantly + x = m_vPosition.x; + y = m_vPosition.y; + w = m_vSize.x; + h = m_vSize.y; + } + + SetBounds( x,y,w,h ); +} + +void CCSMapOverview::SetPlayerSeen( int index ) +{ + CSMapPlayer_t *pCS = GetCSInfoForPlayerIndex(index); + + float now = gpGlobals->curtime; + + if( pCS ) + { + if( pCS->timeLastSeen == -1 ) + pCS->timeFirstSeen = now; + + pCS->timeLastSeen = now; + } +} + +void CCSMapOverview::SetBombSeen( bool seen ) +{ + if( seen ) + { + float now = gpGlobals->curtime; + + if( m_bomb.timeLastSeen == -1 ) + m_bomb.timeFirstSeen = now; + + m_bomb.timeLastSeen = now; + } + else + { + m_bomb.timeFirstSeen = -1; + m_bomb.timeLastSeen = -1; + } +} + +void CCSMapOverview::FlashEntity( int entityID ) +{ + MapPlayer_t *player = GetPlayerByEntityID(entityID); + if( player == NULL ) + return; + + CSMapPlayer_t *playerCS = GetCSInfoForPlayer(player); + + if ( !playerCS ) + return; + + playerCS->flashUntilTime = gpGlobals->curtime + 2.0f; + playerCS->currentFlashAlpha = 255; + playerCS->nextFlashPeakTime = gpGlobals->curtime + 0.5f; +} + +void CCSMapOverview::UpdateFlashes() +{ + float now = gpGlobals->curtime; + for (int i=0; i<MAX_PLAYERS; i++) + { + CSMapPlayer_t *playerCS = GetCSInfoForPlayerIndex(i); + if( playerCS->flashUntilTime <= now ) + { + // Flashing over. + playerCS->currentFlashAlpha = 0; + } + else + { + if( playerCS->nextFlashPeakTime <= now ) + { + // Time for a peak + playerCS->currentFlashAlpha = 255; + playerCS->nextFlashPeakTime = now + 0.5f; + playerCS->nextFlashPeakTime = MIN( playerCS->nextFlashPeakTime, playerCS->flashUntilTime ); + } + else + { + // Just fade away + playerCS->currentFlashAlpha -= ((playerCS->currentFlashAlpha * gpGlobals->frametime) / (playerCS->nextFlashPeakTime - now)); + playerCS->currentFlashAlpha = MAX( 0, playerCS->currentFlashAlpha ); + } + } + } +} + + +//----------------------------------------------------------------------------- +void CCSMapOverview::SetPlayerPreferredMode( int mode ) +{ + // A player has given an explicit overview_mode command, so we need to honor that when we are done being the radar. + m_playerPreferredMode = mode; + + // save off non-radar preferred modes + switch ( mode ) + { + case MAP_MODE_OFF: + overview_preferred_mode.SetValue( MAP_MODE_OFF ); + break; + + case MAP_MODE_INSET: + overview_preferred_mode.SetValue( MAP_MODE_INSET ); + break; + + case MAP_MODE_FULL: + overview_preferred_mode.SetValue( MAP_MODE_FULL ); + break; + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::SetPlayerPreferredViewSize( float viewSize ) +{ + overview_preferred_view_size.SetValue( viewSize ); +} + + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetIconNumberFromTeamNumber( int teamNumber ) +{ + switch(teamNumber) + { + case TEAM_TERRORIST: + return MAP_ICON_T; + + case TEAM_CT: + return MAP_ICON_CT; + + default: + return MAP_ICON_HOSTAGE; + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::ClearGoalIcons() +{ + m_goalIcons.RemoveAll(); +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::UpdateGoalIcons() +{ + // The goal entities don't exist on the client, so we have to get them from the CS Resource. + C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources(); + if ( !pCSPR ) + return; + + Vector bombA = pCSPR->GetBombsiteAPosition(); + if( bombA != vec3_origin ) + { + CSMapGoal_t bombGoalA; + bombGoalA.position = bombA; + bombGoalA.iconToUse = m_bombSiteIconA; + m_goalIcons.AddToTail( bombGoalA ); + m_goalIconsLoaded = true; + } + + Vector bombB = pCSPR->GetBombsiteBPosition(); + if( bombB != vec3_origin ) + { + CSMapGoal_t bombGoalB; + bombGoalB.position = bombB; + bombGoalB.iconToUse = m_bombSiteIconB; + m_goalIcons.AddToTail( bombGoalB ); + m_goalIconsLoaded = true; + } + + for( int rescueIndex = 0; rescueIndex < MAX_HOSTAGE_RESCUES; rescueIndex++ ) + { + Vector hostageI = pCSPR->GetHostageRescuePosition( rescueIndex ); + if( hostageI != vec3_origin ) + { + CSMapGoal_t hostageGoalI; + hostageGoalI.position = hostageI; + hostageGoalI.iconToUse = m_hostageRescueIcon; + m_goalIcons.AddToTail( hostageGoalI ); + m_goalIconsLoaded = true; + } + } +} + +//----------------------------------------------------------------------------- +void CCSMapOverview::DrawGoalIcons() +{ + for( int iconIndex = 0; iconIndex < m_goalIcons.Count(); iconIndex++ ) + { + // Goal icons are drawn without turning, but with edge adjustment. + CSMapGoal_t *currentIcon = &(m_goalIcons[iconIndex]); + int alpha = 255; + DrawIconCS(currentIcon->iconToUse, currentIcon->iconToUse, currentIcon->position, m_flIconSize, GetViewAngle(), alpha, false); + } +} + +//----------------------------------------------------------------------------- +bool CCSMapOverview::IsRadarLocked() +{ + return cl_radar_locked.GetBool(); +} + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetMasterAlpha( void ) +{ + // The master alpha is the alpha that the map wants to draw at. The background will be at half that, and the icons + // will always be full. (The icons fade themselves for functional reasons like seen-recently.) + int alpha = 255; + if( GetMode() == MAP_MODE_RADAR ) + alpha = cl_radaralpha.GetInt(); + else + alpha = overview_alpha.GetFloat() * 255; + alpha = clamp( alpha, 0, 255 ); + + return alpha; +} + +//----------------------------------------------------------------------------- +int CCSMapOverview::GetBorderSize( void ) +{ + switch( GetMode() ) + { + case MAP_MODE_RADAR: + return 4; + case MAP_MODE_INSET: + return 4; + case MAP_MODE_FULL: + default: + return 0; + } +} + +//----------------------------------------------------------------------------- +Vector2D CCSMapOverview::PanelToMap( const Vector2D &panelPos ) +{ + // This is the reversing of baseclass's MapToPanel + int pwidth, pheight; + GetSize(pwidth, pheight); + float viewAngle = GetViewAngle(); + float fScale = (m_fZoom * m_fFullZoom) / OVERVIEW_MAP_SIZE; + + Vector offset; + offset.x = (panelPos.x - (pwidth * 0.5f)) / pheight; + offset.y = (panelPos.y - (pheight * 0.5f)) / pheight; + + offset.x /= fScale; + offset.y /= fScale; + + VectorYawRotate( offset, -viewAngle, offset ); + + Vector2D mapPos; + mapPos.x = offset.x + m_MapCenter.x; + mapPos.y = offset.y + m_MapCenter.y; + + return mapPos; +} |