549 lines
14 KiB
C++
Raw Normal View History

2016-01-29 00:29:48 +06:00
/*
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.
2016-01-29 00:29:48 +06:00
*/
#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
2016-01-29 00:29:48 +06:00
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 )
2016-01-29 00:29:48 +06:00
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
2016-01-29 00:29:48 +06:00
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 );
2016-01-29 00:29:48 +06:00
2016-07-04 00:43:32 +06:00
m_iFlags = HUD_DRAW;
2016-01-29 00:29:48 +06:00
cl_radartype = CVAR_CREATE( "cl_radartype", "0", FCVAR_ARCHIVE );
bTexturesInitialized = bUseRenderAPI = false;
2016-01-29 00:29:48 +06:00
gHUD.AddHudElem( this );
return 1;
}
void CHudRadar::Reset()
{
2016-01-29 01:08:01 +06:00
// make radar don't draw old players after new map
for( int i = 0; i < 34; i++ )
{
2016-02-14 22:19:27 +03:00
g_PlayerExtraInfo[i].radarflashon = false;
2016-01-29 00:29:48 +06:00
2016-02-14 22:19:27 +03:00
if( i <= MAX_HOSTAGES ) g_HostageInfo[i].radarflashon = false;
2016-01-29 01:08:01 +06:00
}
2016-01-29 00:29:48 +06:00
}
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 );
}
}
2016-01-29 00:29:48 +06:00
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;
2016-01-29 00:29:48 +06:00
return 1;
}
void CHudRadar::UserCmd_HideRadar()
{
2016-07-04 00:43:32 +06:00
m_iFlags &= ~HUD_DRAW;
2016-01-29 00:29:48 +06:00
}
void CHudRadar::UserCmd_ShowRadar()
{
2016-07-04 00:43:32 +06:00
m_iFlags |= HUD_DRAW;
2016-01-29 00:29:48 +06:00
}
int CHudRadar::MsgFunc_Radar(const char *pszName, int iSize, void *pbuf )
{
BufferReader reader( pszName, pbuf, iSize );
2016-01-29 00:29:48 +06:00
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();
2016-01-29 00:29:48 +06:00
return 1;
}
bool Radar_FlashTime( float flTime, extra_player_info_t *pplayer )
{
if( !pplayer->radarflashon )
return true;
// radar flashing
if( pplayer->radarflashes )
{
float timer = (flTime - pplayer->radarflash);
if( timer > 0.5f )
{
pplayer->nextflash = !pplayer->nextflash;
pplayer->radarflash = flTime;
pplayer->radarflashes--;
}
}
else pplayer->radarflashon = 0;
return pplayer->nextflash;
}
bool Radar_FlashTime( float flTime, hostage_info_t *pplayer )
{
if( !pplayer->radarflashon )
return true;
// radar flashing
if( pplayer->radarflashes )
{
float timer = (flTime - pplayer->radarflash);
if( timer > 0.5f )
{
pplayer->nextflash = !pplayer->nextflash;
pplayer->radarflash = flTime;
pplayer->radarflashes--;
}
}
else pplayer->radarflashon = 0;
return pplayer->nextflash;
}
2016-01-29 00:29:48 +06:00
int CHudRadar::Draw(float flTime)
{
2016-01-29 01:08:01 +06:00
if ( (gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) ||
gEngfuncs.IsSpectateOnly() ||
!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT))) ||
gHUD.m_fPlayerDead )
2016-01-29 00:29:48 +06:00
return 1;
int iTeamNumber = g_PlayerExtraInfo[ gHUD.m_Scoreboard.m_iPlayerNum ].teamnumber;
2016-01-29 00:56:05 +06:00
int r, g, b;
2016-01-29 00:29:48 +06:00
if( cl_radartype->value )
{
SPR_Set(m_hRadarOpaque.spr, 200, 200, 200);
SPR_DrawHoles(0, 0, 0, &m_hRadarOpaque.rect);
2016-01-29 00:29:48 +06:00
}
else
{
SPR_Set( m_hRadar.spr, 25, 75, 25 );
SPR_DrawAdditive( 0, 0, 0, &m_hRadarOpaque.rect );
2016-01-29 00:29:48 +06:00
}
if( bUseRenderAPI )
{
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
gEngfuncs.pTriAPI->CullFace( TRI_NONE );
gEngfuncs.pTriAPI->Brightness( 1 );
gRenderAPI.GL_SelectTexture( 0 );
}
2016-01-29 00:29:48 +06:00
for(int i = 0; i < 33; i++)
{
// skip local player and dead players
if( i == gHUD.m_Scoreboard.m_iPlayerNum || g_PlayerExtraInfo[i].dead )
2016-01-29 00:29:48 +06:00
continue;
// skip non-teammates
2016-01-29 00:29:48 +06:00
if( g_PlayerExtraInfo[i].teamnumber != iTeamNumber )
continue;
// decide should player draw at this time. For flashing.
// Always true for non-flashing players
if( !Radar_FlashTime( flTime, &g_PlayerExtraInfo[i]) )
continue;
// player with C4 must be red
2016-01-29 00:56:05 +06:00
if( g_PlayerExtraInfo[i].has_c4 )
{
DrawUtils::UnpackRGB( r, g, b, RGB_REDISH );
2016-01-29 00:56:05 +06:00
}
else
{
// white
DrawUtils::UnpackRGB( r, g, b, RGB_WHITE );
2016-01-29 00:56:05 +06:00
}
// calc radar position
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_PlayerExtraInfo[i].origin, gHUD.m_vecAngles);
if( pos.z < 20 && pos.z > -20 )
2016-01-29 00:29:48 +06:00
{
DrawRadarDot( pos.x, pos.y, r, g, b, 255 );
}
else if( gHUD.m_vecOrigin.z > g_PlayerExtraInfo[i].origin.z )
{
DrawFlippedT( pos.x, pos.y, r, g, b, 255 );
2016-01-29 00:29:48 +06:00
}
else
{
DrawT( pos.x, pos.y, r, g, b, 255 );
2016-01-29 00:29:48 +06:00
}
}
// Terrorist specific code( C4 Bomb )
if( g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].teamnumber == TEAM_TERRORIST )
2016-01-29 00:29:48 +06:00
{
if( Radar_FlashTime( flTime, &g_PlayerExtraInfo[33] ) )
2016-01-29 00:29:48 +06:00
{
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_PlayerExtraInfo[33].origin, gHUD.m_vecAngles);
if( g_PlayerExtraInfo[33].playerclass ) // bomb planted
2016-01-29 00:29:48 +06:00
{
DrawCross( pos.x, pos.y, 255, 0, 0, 255);
}
else
{
DrawRadarDot( pos.x, pos.y, 255, 0, 0, 255 );
}
2016-01-29 00:29:48 +06:00
}
}
// Counter-Terrorist specific code( hostages )
else if( g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].teamnumber == TEAM_CT )
2016-01-29 00:29:48 +06:00
{
// draw hostages for CT
for( int i = 0; i < MAX_HOSTAGES; i++ )
{
if( g_HostageInfo[i].dead )
continue;
if( !Radar_FlashTime( flTime, &g_HostageInfo[i] ) )
2016-01-29 00:29:48 +06:00
continue;
Vector pos = WorldToRadar(gHUD.m_vecOrigin, g_HostageInfo[i].origin, gHUD.m_vecAngles);
DrawRadarDot( pos.x, pos.y, 255, 0, 0, 255 );
2016-01-29 00:29:48 +06:00
}
}
return 0;
2016-01-29 00:29:48 +06:00
}
void CHudRadar::DrawPlayerLocation()
{
DrawUtils::DrawConsoleString( 30, 30, g_PlayerExtraInfo[gHUD.m_Scoreboard.m_iPlayerNum].location );
2016-01-29 00:29:48 +06:00
}
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 )
2016-01-29 00:29:48 +06:00
{
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);
}
2016-01-29 00:29:48 +06:00
}
void CHudRadar::DrawCross( int x, int y, int r, int g, int b, int a )
2016-01-29 00:29:48 +06:00
{
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);
}
2016-01-29 00:29:48 +06:00
}
void CHudRadar::DrawT( int x, int y, int r, int g, int b, int a )
2016-01-29 00:56:05 +06:00
{
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);
}
2016-01-29 00:56:05 +06:00
}
void CHudRadar::DrawFlippedT( int x, int y, int r, int g, int b, int a )
2016-01-29 00:56:05 +06:00
{
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);
}
2016-01-29 00:56:05 +06:00
}
Vector CHudRadar::WorldToRadar(const Vector vPlayerOrigin, const Vector vObjectOrigin, const Vector vAngles )
2016-01-29 00:29:48 +06:00
{
Vector2D diff = vObjectOrigin.Make2D() - vPlayerOrigin.Make2D();
const float RADAR_SCALE = 32.0f;
2016-01-29 00:29:48 +06:00
// Supply epsilon values to avoid divide-by-zero
if( diff.x == 0 )
diff.x = 0.00001f;
if( diff.y == 0 )
diff.y = 0.00001f;
2016-01-29 00:29:48 +06:00
float flOffset = DEG2RAD( vAngles.y - RAD2DEG( atan2( diff.y, diff.x ) ) );
2016-01-29 00:29:48 +06:00
// this magic 32.0f just scales position on radar
float iRadius = min( diff.Length() / RADAR_SCALE, iMaxRadius );
2016-01-29 00:29:48 +06:00
// transform origin difference to radar source
Vector ret( (float)(iRadius * sin(flOffset)),
(float)(iRadius * -cos(flOffset)),
(float)(vPlayerOrigin.z - vObjectOrigin.z) );
return ret;
2016-01-29 00:29:48 +06:00
}
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].radarflashon = 1;
g_PlayerExtraInfo[33].radarflashes = 99999;
g_PlayerExtraInfo[33].radarflash = gHUD.m_flTime;
strncpy(g_PlayerExtraInfo[33].teamname, "TERRORIST", MAX_TEAM_NAME);
g_PlayerExtraInfo[33].dead = 0;
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].radarflashon = 0;
g_PlayerExtraInfo[33].radarflash = 0.0f;
g_PlayerExtraInfo[33].radarflashes = 0;
g_PlayerExtraInfo[33].dead = 1;
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();
if ( Flag == 1 )
{
g_HostageInfo[idx].radarflash = gHUD.m_flTime;
g_HostageInfo[idx].radarflashon = 1;
g_HostageInfo[idx].radarflashes = 99999;
}
strncpy(g_HostageInfo[idx].teamname, "CT", MAX_TEAM_NAME);
g_HostageInfo[idx].dead = 0;
}
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 = 1;
g_HostageInfo[idx].radarflash = gHUD.m_flTime;
g_HostageInfo[idx].radarflashes = 15;
}
return 1;
}