cs16-client-legacy/cl_dll/hud/radar.cpp
2016-10-22 19:31:43 +03:00

562 lines
14 KiB
C++

/*
radar.cpp - Radar
Copyright (C) 2016 a1batross
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
In addition, as a special exception, the author gives permission to
link the code of this program with the Half-Life Game Engine ("HL
Engine") and Modified Game Libraries ("MODs") developed by Valve,
L.L.C ("Valve"). You must obey the GNU General Public License in all
respects for all of the code used other than the HL Engine and MODs
from Valve. If you modify this file, you may extend this exception
to your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from your
version.
*/
#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "draw_util.h"
#include "triangleapi.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
#endif
DECLARE_COMMAND( m_Radar, ShowRadar )
DECLARE_COMMAND( m_Radar, HideRadar )
DECLARE_MESSAGE( m_Radar, Radar )
DECLARE_MESSAGE( m_Radar, HostageK )
DECLARE_MESSAGE( m_Radar, HostagePos )
DECLARE_MESSAGE( m_Radar, BombDrop )
DECLARE_MESSAGE( m_Radar, BombPickup )
static byte r_RadarCross[8][8] =
{
{1,1,0,0,0,0,1,1},
{1,1,1,0,0,1,1,1},
{0,1,1,1,1,1,1,0},
{0,0,1,1,1,1,0,0},
{0,0,1,1,1,1,0,0},
{0,1,1,1,1,1,1,0},
{1,1,1,0,0,1,1,1},
{1,1,0,0,0,0,1,1}
};
static byte r_RadarT[8][8] =
{
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0}
};
static byte r_RadarFlippedT[8][8] =
{
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{0,0,0,1,1,0,0,0},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1}
};
#define BLOCK_SIZE_MAX 1024
static byte data2D[BLOCK_SIZE_MAX*4]; // intermediate texbuffer
int CHudRadar::Init()
{
HOOK_MESSAGE( Radar );
HOOK_COMMAND( "drawradar", ShowRadar );
HOOK_COMMAND( "hideradar", HideRadar );
HOOK_MESSAGE( HostageK );
HOOK_MESSAGE( HostagePos );
HOOK_MESSAGE( BombDrop );
HOOK_MESSAGE( BombPickup );
m_iFlags = HUD_DRAW;
cl_radartype = CVAR_CREATE( "cl_radartype", "0", FCVAR_ARCHIVE );
bTexturesInitialized = bUseRenderAPI = false;
gHUD.AddHudElem( this );
return 1;
}
void CHudRadar::Reset()
{
// make radar don't draw old players after new map
for( int i = 0; i < 34; i++ )
{
g_PlayerExtraInfo[i].radarflashes = 0;
if( i <= MAX_HOSTAGES ) g_HostageInfo[i].radarflashes = 0;
}
}
static void Radar_InitBitmap( int w, int h, byte *buf )
{
for( int x = 0; x < w; x++ )
{
for( int y = 0; y < h; y++ )
{
data2D[(y * 8 + x) * 4 + 0] = 255;
data2D[(y * 8 + x) * 4 + 1] = 255;
data2D[(y * 8 + x) * 4 + 2] = 255;
data2D[(y * 8 + x) * 4 + 3] = buf[y*h + x] * 255;
}
}
}
int CHudRadar::InitBuiltinTextures( void )
{
texFlags_t defFlags = (texFlags_t)(TF_NOMIPMAP | TF_NOPICMIP | TF_NEAREST | TF_CLAMP | TF_HAS_ALPHA);
if( bTexturesInitialized )
return 1;
const struct
{
const char *name;
byte *buf;
int *texnum;
int w, h;
void (*init)( int w, int h, byte *buf );
int texType;
}
textures[] =
{
{ "radarT", (byte*)r_RadarT, &hT, 8, 8, Radar_InitBitmap, TEX_CUSTOM },
{ "radarcross", (byte*)r_RadarCross, &hCross, 8, 8, Radar_InitBitmap, TEX_CUSTOM },
{ "radarflippedT", (byte*)r_RadarFlippedT, &hFlippedT, 8, 8, Radar_InitBitmap, TEX_CUSTOM }
};
size_t i, num_builtin_textures = sizeof( textures ) / sizeof( textures[0] );
for( i = 0; i < num_builtin_textures; i++ )
{
textures[i].init( textures[i].w, textures[i].h, textures[i].buf );
*textures[i].texnum = gRenderAPI.GL_CreateTexture( textures[i].name, textures[i].w, textures[i].h, data2D, defFlags );
if( *textures[i].texnum == 0 )
{
for( size_t j = 0; j < i; i++ )
{
gRenderAPI.GL_FreeTexture( *textures[i].texnum );
}
return 0;
}
gRenderAPI.GL_SetTextureType( *textures[i].texnum, textures[i].texType );
}
hDot = gRenderAPI.GL_LoadTexture( "*white", NULL, 0, 0 );
bTexturesInitialized = true;
return 1;
}
void CHudRadar::Shutdown( void )
{
// GL_FreeTexture( hDot ); engine inner texture
if( bTexturesInitialized )
{
gRenderAPI.GL_FreeTexture( hT );
gRenderAPI.GL_FreeTexture( hFlippedT );
gRenderAPI.GL_FreeTexture( hCross );
}
}
int CHudRadar::VidInit(void)
{
bUseRenderAPI = g_iXash && InitBuiltinTextures();
m_hRadar.SetSpriteByName( "radar" );
m_hRadarOpaque.SetSpriteByName( "radaropaque" );
iMaxRadius = (m_hRadar.rect.right - m_hRadar.rect.left) / 2.0f;
return 1;
}
void CHudRadar::UserCmd_HideRadar()
{
m_iFlags &= ~HUD_DRAW;
}
void CHudRadar::UserCmd_ShowRadar()
{
m_iFlags |= HUD_DRAW;
}
int CHudRadar::MsgFunc_Radar(const char *pszName, int iSize, void *pbuf )
{
BufferReader reader( pszName, pbuf, iSize );
int index = reader.ReadByte();
g_PlayerExtraInfo[index].origin.x = reader.ReadCoord();
g_PlayerExtraInfo[index].origin.y = reader.ReadCoord();
g_PlayerExtraInfo[index].origin.z = reader.ReadCoord();
return 1;
}
bool CHudRadar::FlashTime( float flTime, extra_player_info_t *pplayer )
{
// radar flashing
if( pplayer->radarflashes )
{
if( flTime > pplayer->radarflashtime )
{
pplayer->nextflash = !pplayer->nextflash;
pplayer->radarflashtime += pplayer->radarflashtimedelta;
pplayer->radarflashes--;
}
}
else
{
return true;
}
return pplayer->nextflash;
}
bool CHudRadar::HostageFlashTime( float flTime, hostage_info_t *pplayer )
{
// radar flashing
if( pplayer->radarflashes )
{
if( flTime > pplayer->radarflashtime )
{
pplayer->nextflash = !pplayer->nextflash;
pplayer->radarflashtime += pplayer->radarflashtimedelta;
pplayer->radarflashes--;
}
}
else
{
return false; // non-flashing hostage must be never drawn on radar!
}
return pplayer->nextflash;
}
void CHudRadar::DrawZAxis( Vector pos, int r, int g, int b, int a )
{
const float diff = 128;
if( pos.z > -diff && pos.z < diff )
{
DrawRadarDot( pos.x, pos.y, r, g, b, a );
}
else if( pos.z <= -diff )
{
// higher than player
DrawT( pos.x, pos.y, r, g, b, a );
}
else
{
// lower than player
DrawFlippedT( pos.x, pos.y, r, g, b, a );
}
}
int CHudRadar::Draw(float flTime)
{
if ( (gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) ||
gEngfuncs.IsSpectateOnly() ||
!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT))) ||
gHUD.m_fPlayerDead )
return 1;
int iTeamNumber = g_PlayerExtraInfo[ gHUD.m_Scoreboard.m_iPlayerNum ].teamnumber;
int r, g, b;
if( cl_radartype->value )
{
SPR_Set(m_hRadarOpaque.spr, 200, 200, 200);
SPR_DrawHoles(0, 0, 0, &m_hRadarOpaque.rect);
}
else
{
SPR_Set( m_hRadar.spr, 25, 75, 25 );
SPR_DrawAdditive( 0, 0, 0, &m_hRadarOpaque.rect );
}
if( bUseRenderAPI )
{
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
gEngfuncs.pTriAPI->CullFace( TRI_NONE );
gEngfuncs.pTriAPI->Brightness( 1 );
gRenderAPI.GL_SelectTexture( 0 );
}
for(int i = 0; i < 33; i++)
{
// skip local player and dead players
if( i == gHUD.m_Scoreboard.m_iPlayerNum || g_PlayerExtraInfo[i].dead )
continue;
// skip non-teammates
if( g_PlayerExtraInfo[i].teamnumber != iTeamNumber )
continue;
// decide should player draw at this time. For flashing.
// Always true for non-flashing players
if( !FlashTime( flTime, &g_PlayerExtraInfo[i]) )
continue;
// player with C4 must be red
if( g_PlayerExtraInfo[i].has_c4 )
{
DrawUtils::UnpackRGB( r, g, b, RGB_REDISH );
}
else
{
// white
DrawUtils::UnpackRGB( r, g, b, RGB_WHITE );
}
// calc radar position
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_PlayerExtraInfo[i].origin, gHUD.m_vecAngles);
DrawZAxis( pos, r, g, b, 255 );
}
// Terrorist specific code( C4 Bomb )
if( g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].teamnumber == TEAM_TERRORIST )
{
if ( !g_PlayerExtraInfo[33].dead &&
g_PlayerExtraInfo[33].radarflashes &&
FlashTime( flTime, &g_PlayerExtraInfo[33] ))
{
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_PlayerExtraInfo[33].origin, gHUD.m_vecAngles);
if( g_PlayerExtraInfo[33].playerclass ) // bomb planted
{
DrawCross( pos.x, pos.y, 255, 0, 0, 255 );
}
else
{
DrawZAxis( pos, 255, 0, 0, 255 );
}
}
}
// Counter-Terrorist specific code( hostages )
else if( g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].teamnumber == TEAM_CT )
{
// draw hostages for CT
for( int i = 0; i < MAX_HOSTAGES; i++ )
{
if( !HostageFlashTime( flTime, g_HostageInfo + i ) )
{
continue;
}
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_HostageInfo[i].origin, gHUD.m_vecAngles);
if( g_HostageInfo[i].dead )
{
DrawZAxis( pos, 255, 0, 0, 255 );
}
else
{
DrawZAxis( pos, 4, 25, 110, 255 );
}
}
}
return 0;
}
void CHudRadar::DrawPlayerLocation()
{
DrawUtils::DrawConsoleString( 30, 30, g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].location );
}
inline void CHudRadar::DrawColoredTexture( int x, int y, int size, byte r, byte g, byte b, byte a, int texHandle )
{
gRenderAPI.GL_Bind( 0, texHandle );
gEngfuncs.pTriAPI->Color4ub( r, g, b, a );
DrawUtils::Draw2DQuad( (iMaxRadius + x - size * 2) * gHUD.m_flScale,
(iMaxRadius + y - size * 2) * gHUD.m_flScale,
(iMaxRadius + x + size * 2) * gHUD.m_flScale,
(iMaxRadius + y + size * 2) * gHUD.m_flScale);
}
void CHudRadar::DrawRadarDot( int x, int y, int r, int g, int b, int a )
{
const int size = 1;
if( bUseRenderAPI )
{
DrawColoredTexture( x, y, size, r, g, b, a, hDot );
}
else
{
FillRGBA(iMaxRadius + x - size*2, iMaxRadius + y - size*2, size*4, size*4, r, g, b, a);
}
}
void CHudRadar::DrawCross( int x, int y, int r, int g, int b, int a )
{
const int size = 2;
if( bUseRenderAPI )
{
DrawColoredTexture( x, y, size, r, g, b, a, hCross );
}
else
{
FillRGBA(iMaxRadius + x, iMaxRadius + y, size, size, r, g, b, a);
FillRGBA(iMaxRadius + x - size, iMaxRadius + y - size, size, size, r, g, b, a);
FillRGBA(iMaxRadius + x - size, iMaxRadius + y + size, size, size, r, g, b, a);
FillRGBA(iMaxRadius + x + size, iMaxRadius + y - size, size, size, r, g, b, a);
FillRGBA(iMaxRadius + x + size, iMaxRadius + y + size, size, size, r, g, b, a);
}
}
void CHudRadar::DrawT( int x, int y, int r, int g, int b, int a )
{
const int size = 2;
if( bUseRenderAPI )
{
DrawColoredTexture( x, y, size, r, g, b, a, hT );
}
else
{
FillRGBA( iMaxRadius + x - size, iMaxRadius + y - size, size * 3, size, r, g, b, a);
FillRGBA( iMaxRadius + x, iMaxRadius + y, size, size * 2, r, g, b, a);
}
}
void CHudRadar::DrawFlippedT( int x, int y, int r, int g, int b, int a )
{
const int size = 2;
if( bUseRenderAPI )
{
DrawColoredTexture( x, y, size, r, g, b, a, hFlippedT );
}
else
{
FillRGBA( iMaxRadius + x, iMaxRadius + y - size, size, size*2, r, g, b, a);
FillRGBA( iMaxRadius + x - size, iMaxRadius + y + size, size*3, size, r, g, b, a);
}
}
Vector CHudRadar::WorldToRadar(const Vector vPlayerOrigin, const Vector vObjectOrigin, const Vector vAngles )
{
Vector2D diff = vObjectOrigin.Make2D() - vPlayerOrigin.Make2D();
const float RADAR_SCALE = 32.0f;
// Supply epsilon values to avoid divide-by-zero
if( diff.x == 0 )
diff.x = 0.00001f;
if( diff.y == 0 )
diff.y = 0.00001f;
float flOffset = DEG2RAD( vAngles.y - RAD2DEG( atan2( diff.y, diff.x ) ) );
// this magic 32.0f just scales position on radar
float iRadius = min( diff.Length() / RADAR_SCALE, iMaxRadius );
// transform origin difference to radar source
Vector ret( (float)(iRadius * sin(flOffset)),
(float)(iRadius * -cos(flOffset)),
(float)(vPlayerOrigin.z - vObjectOrigin.z) );
return ret;
}
int CHudRadar::MsgFunc_BombDrop(const char *pszName, int iSize, void *pbuf)
{
BufferReader reader( pszName, pbuf, iSize );
g_PlayerExtraInfo[33].origin.x = reader.ReadCoord();
g_PlayerExtraInfo[33].origin.y = reader.ReadCoord();
g_PlayerExtraInfo[33].origin.z = reader.ReadCoord();
g_PlayerExtraInfo[33].radarflashes = 99999;
g_PlayerExtraInfo[33].radarflashtime = gHUD.m_flTime;
g_PlayerExtraInfo[33].radarflashtimedelta = 0.5f;
strncpy(g_PlayerExtraInfo[33].teamname, "TERRORIST", MAX_TEAM_NAME);
g_PlayerExtraInfo[33].dead = false;
g_PlayerExtraInfo[33].nextflash = true;
int Flag = reader.ReadByte();
g_PlayerExtraInfo[33].playerclass = Flag;
if( Flag ) // bomb planted
{
gHUD.m_SpectatorGui.m_bBombPlanted = 0;
gHUD.m_Timer.m_iFlags = 0;
}
return 1;
}
int CHudRadar::MsgFunc_BombPickup(const char *pszName, int iSize, void *pbuf)
{
g_PlayerExtraInfo[33].radarflashes = false;
g_PlayerExtraInfo[33].dead = true;
return 1;
}
int CHudRadar::MsgFunc_HostagePos(const char *pszName, int iSize, void *pbuf)
{
BufferReader reader( pszName, pbuf, iSize );
int Flag = reader.ReadByte();
int idx = reader.ReadByte();
if( idx <= MAX_HOSTAGES )
{
g_HostageInfo[idx].origin.x = reader.ReadCoord();
g_HostageInfo[idx].origin.y = reader.ReadCoord();
g_HostageInfo[idx].origin.z = reader.ReadCoord();
g_HostageInfo[idx].dead = false;
if( Flag == 1 ) // first message about this hostage, start flashing
{
g_HostageInfo[idx].radarflashes = 99999;
g_HostageInfo[idx].radarflashtime = gHUD.m_flTime;
g_HostageInfo[idx].radarflashtimedelta = 0.5f;
}
}
return 1;
}
int CHudRadar::MsgFunc_HostageK(const char *pszName, int iSize, void *pbuf)
{
BufferReader reader( pszName, pbuf, iSize );
int idx = reader.ReadByte();
if ( idx <= MAX_HOSTAGES )
{
g_HostageInfo[idx].dead = true;
g_HostageInfo[idx].radarflashtime = gHUD.m_flTime;
g_HostageInfo[idx].radarflashes = 15;
g_HostageInfo[idx].radarflashtimedelta = 0.1f;
}
return 1;
}