You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2324 lines
66 KiB
2324 lines
66 KiB
//========= 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; |
|
}
|
|
|