/*
* [U]NITED [G]AMING [C]HEATS Client Hook
*
* Copyright (c) LIQUID     2001-2002
* Copyright (c) ZOOLABUS   2001-2002
* 
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
* 
* GNU license doesn't cover Engine directory.
* Content of Engine directory is copyrighted (c) 1999, 2000, by Valve LLC
* and it is licensed under Valve's proprietary license (see original HL SDK).
*/

#define  WIN32_LEAN_AND_MEAN 
#include <windows.h>
#include <mmsystem.h>
#pragma warning (disable:4786) // vc++ stl "truncated browser info"
#pragma warning (disable:4244) // float to int 
#pragma warning (disable:4731) // ebp modified
#include <map>
#include <vector>
#include <list>
#undef NDEBUG
#include <assert.h>
#include "engine/wrect.h"
#include "engine/cl_dll.h"
#include "engine/cdll_int.h"
#include "engine/const.h"
#include "engine/progdefs.h"
#include "engine/eiface.h"
#include "engine/edict.h"
#include "engine/studio_event.h"
#include "engine/entity_types.h"
#include "engine/pmtrace.h" 
#include "common/ref_params.h"
#include "common/screenfade.h"
#include "common/event_api.h"
#include "common/com_model.h"
#include "misc/parsemsg.h"
#include "misc/sprites.h" 
#include "engine/r_studioint.h"
#include "engine/triangleapi.h"
#include "engine/pm_defs.h"
#include "engine/r_efx.h"
#include "sprites.h" 
#include "common/net_api.h"
#include <time.h>
#include <gl/gl.h>
#include "stdafx.h"

using namespace std;
#include <tlhelp32.h> 
#include <fstream>
#include "cvar.h"
#include "attack.h"
#include "calcscreen.h"
#include "timehandling.h"
#include "stringfinder.h"
#include "color.h"
#include "aimbot.h"
#include "playeritems.h"
#include "bind.h"
#include "interpreter.h"
#include "client.h"
#include "menu.h"
#include "console.h"
#include "Winamp.h"
#include "engine/studio.h"

#include "GenericPatch.h"
extern GenericPatch soundPatch;
extern bool detour_S_DynamicSound();

#pragma warning (disable:4005)
#pragma warning (disable:4800)
#pragma optimize("gt",on)

#ifndef M_PI
#define M_PI		3.14159265358979323846	// matches value in gcc v2 math.h
#endif

#define UGC_VERSION "1.0a [-BETA-]"

using namespace std;

typedef void( *fCAM_Think )( void );
typedef void( *fCL_CameraOffset )( float * );
typedef void( *fCL_CreateMove )( float, struct usercmd_s *, int );
typedef int( *fCL_IsThirdPerson )( void );
typedef void( *fDemo_ReadBuffer )( int, unsigned char * );
typedef int( *fHUD_AddEntity )( int, struct cl_entity_s *, const char * );
typedef int( *fHUD_ConnectionlessPacket )( struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size );
typedef void( *fHUD_CreateEntities )( void );
typedef int( *fHUD_DirectorEvent )( unsigned char command, unsigned int firstObject, unsigned int secondObject, unsigned int flags );
typedef void( *fHUD_DrawNormalTriangles )( void );
typedef void( *fHUD_DrawTransparentTriangles )( void );
typedef void( *fHUD_Frame )( double time );
typedef int( *fHUD_GetHullBounds )( int hullnumber, float *mins, float *maxs );
typedef struct cl_entity_s *( *fHUD_GetUserEntity )( int index );
typedef int( *fHUD_Init )( void );
typedef int( *fHUD_Key_Event )( int, int, const char * );
typedef void( *fHUD_PlayerMove )( struct playermove_s *, int );
typedef void( *fHUD_PlayerMoveInit )( struct playermove_s * );
typedef char( *fHUD_PlayerMoveTexture )( char * );
typedef void( *fHUD_PostRunCmd )( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed );
typedef void( *fHUD_ProcessPlayerState )( struct entity_state_s *, const struct entity_state_s * );
typedef int( *fHUD_Redraw )( float, int );
typedef int( *fHUD_Reset )( void );
typedef void( *fHUD_Shutdown )( void );
typedef void( *fHUD_StudioEvent )( const struct mstudioevent_s *, const struct cl_entity_s * );
typedef void( *fHUD_TempEntUpdate )( double, double, double, struct tempent_s **, struct tempent_s **, int( *Callback_AddVisibleEntity )( struct cl_entity_s *fEntity ), void( *Callback_TempEntPlaySound )( struct tempent_s *fTemp, float damp ) );
typedef void( *fHUD_TxferLocalOverrides )( struct entity_state_s *, const struct clientdata_s * );
typedef void( *fHUD_TxferPredictionData )( struct entity_state_s *, const struct entity_state_s *, struct clientdata_s *, const struct clientdata_s *, struct weapon_data_s *, const struct weapon_data_s * );
typedef int( *fHUD_UpdateClientData )( client_data_t *, float );
typedef int( *fHUD_VidInit )( void );
typedef int( *fHUD_VoiceStatus )( int entindex, qboolean bTalking );
typedef void( *fIN_Accumulate )( void );
typedef void( *fIN_ActivateMouse )( void );
typedef void( *fIN_ClearStates )( void );
typedef void( *fIN_DeactivateMouse )( void );
typedef void( *fIN_MouseEvent )( int mstate );
typedef int( *fInitialize )( cl_enginefunc_t *, int );
typedef struct kbutton_s *( *fKB_Find )( const char *name );
typedef void( *fV_CalcRefdef )( struct ref_params_s *fparams ); 
typedef void (*fHUD_DirectorMessage)(unsigned char, unsigned int, unsigned int, unsigned int);
typedef int  (*fHUD_GetStudioModelInterface)( int, struct r_studio_interface_s **, struct engine_studio_api_s *);

void( *pCAM_Think )( void );
void( *pCL_CameraOffset )( float * );
void( *pCL_CreateMove )( float, struct usercmd_s *, int );
int( *pCL_IsThirdPerson )( void );
void( *pDemo_ReadBuffer )( int, unsigned char * );
int( *pHUD_AddEntity )( int, struct cl_entity_s *, const char * );
int( *pHUD_ConnectionlessPacket )( struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size );
void( *pHUD_CreateEntities )( void );
int( *pHUD_DirectorEvent )( unsigned char command, unsigned int firstObject, unsigned int secondObject, unsigned int flags );
void( *pHUD_DrawNormalTriangles )( void );
void( *pHUD_DrawTransparentTriangles )( void );
void( *pHUD_Frame )( double time );
int( *pHUD_GetHullBounds )( int hullnumber, float *mins, float *maxs );
struct cl_entity_s *( *pHUD_GetUserEntity )( int index );
int( *pHUD_Init )( void );
int( *pHUD_Key_Event )( int, int, const char * );
void( *pHUD_PlayerMove )( struct playermove_s *, int );
void( *pHUD_PlayerMoveInit )( struct playermove_s * );
char( *pHUD_PlayerMoveTexture )( char * );
void( *pHUD_PostRunCmd )( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed );
void( *pHUD_ProcessPlayerState )( struct entity_state_s *, const struct entity_state_s * );
int( *pHUD_Redraw )( float, int );
int( *pHUD_Reset )( void );
void( *pHUD_Shutdown )( void );
void( *pHUD_StudioEvent )( const struct mstudioevent_s *, const struct cl_entity_s * );
void( *pHUD_TempEntUpdate )( double, double, double, struct tempent_s **, struct tempent_s **, int( *Callback_AddVisibleEntity )( struct cl_entity_s *pEntity ), void( *Callback_TempEntPlaySound )( struct tempent_s *pTemp, float damp ) );
void( *pHUD_TxferLocalOverrides )( struct entity_state_s *, const struct clientdata_s * );
void( *pHUD_TxferPredictionData )( struct entity_state_s *, const struct entity_state_s *, struct clientdata_s *, const struct clientdata_s *, struct weapon_data_s *, const struct weapon_data_s * );
int( *pHUD_UpdateClientData )( client_data_t *, float );
int( *pHUD_VidInit )( void );
int( *pHUD_VoiceStatus )( int entindex, qboolean bTalking );
void( *pIN_Accumulate )( void );
void( *pIN_ActivateMouse )( void );
void( *pIN_ClearStates )( void );
void( *pIN_DeactivateMouse )( void );
void( *pIN_MouseEvent )( int mstate );
int( *pInitialize )( cl_enginefunc_t *, int );
struct kbutton_s *( *pKB_Find )( const char *name );
void( *pV_CalcRefdef )( struct ref_params_s *pparams ); 
void (*pHUD_DirectorMessage)(unsigned char, unsigned int, unsigned int, unsigned int);
int  (*pHUD_GetStudioModelInterface)( int, struct r_studio_interface_s **, struct engine_studio_api_s *);

int WallActive(void)
{
	return cvar.wall;
}

bool LambertActive(void)
{
	return cvar.lambert;
}

int GlExtraActive(void)
{
	return cvar.glextra;
}

SCREENINFO screeninfo;

// Hostage Info Structure
struct hostage_info
{
	cl_entity_s *ent;
};

// A Global to store the engine functions
struct cl_enginefuncs_s  gEngfuncs;
struct cl_enginefuncs_s  gHookedEngfuncs;

// A Global to store the Effects API
struct efx_api_s gHookEfxApi;

// A Global to store the Studio API
engine_studio_api_t  IEngineStudio;
engine_studio_api_t* pIEngineStudio=0;

// A Global to store the local player info
local_player_info me;

// A Global for all the Players
VecPlayers vPlayers;

// A Global for all the Entities
vector<hostage_info> vEntity;

// Global for the Aiming Randomizer Class
AimingRandomizer gAimingRandomizer;

// A list of commands being blocked
StringFinder blockCommand;

// The Method of blocking used
enum { CMDBLOCK_ALL, CMDBLOCK_RELPATH };

// Rear View Mirror
bool m_bReversed;
bool bNewFrame=false;

// should movement be stopped e.g. for shooting?
bool bStopMovement=false;

// dont draw sniper blackout if true
bool sniper=false;

// Bomb Timer Stuff
bool bBombIsPlanted=false;
float fBombPlantedTime=0;
int iC4Timer=0;

// Rounds counter
int iRounds=0;

// Blinking for Radar/Esp
StopTimer blinkTimer;
bool blinkPhase = false;

// Local Players Items
// TODO: Move into local_player_info
PlayerItems playerItems;

// Logging
ofstream commandLogFile;
bool commandLogFileOpened = false;

// HUD Message
char  gHudMessage[256]="";
StopTimer gHudTimer;

// Script Message (text ..., txt ...)
char  gHudMessage2[256]="";
StopTimer gHudTimer2;

// Scheduled Commands (later x blabla)
struct ScheduledCommand
{
	string           cmd;
	RunningStopTimer timer;
};
typedef list<ScheduledCommand> ScheduleList;
ScheduleList scheduleList;

// Nice Firing stuff
enum { FIRESEQ_INACTIVE     = 0, 
       FIRESEQ_STARTDELAY   = 1, 
	   FIRESEQ_ACTIVE       = 2,
	   FIRESEQ_RELEASEDELAY = 3 };
static bool bAttacking = false;
static bool bAttack2 = false;
static bool bPressedUse = false;
static int  nFireSequence = FIRESEQ_INACTIVE;
static bool bFireCycle  = true;

// Death Info
struct DeathInfos{
	DeathInfos():active(false) {}
	bool   active;
	float  from[3];
	float  to[3];
	string killerName;
	string weaponName;
	string victimName;
};
DeathInfos deathInfos;
static bool bGotHeadshot=false;

// Current WeaponID  see attack.h
int   currentWeaponID=0;

// Current FOV as float and int
int    iCurrentFOV=90;
float  fCurrentFOV=90.0;

// Burst Info
BurstInfo burstInfo;

// HL.exe or CSTRIKE.exe
HANDLE hProcess=0;

// HW.dll offset
DWORD hw_dll_offset=0;

// CLIENT.dll offset
HMODULE hClientDll=0;
DWORD client_dll_offset=0;
DWORD client_dll_end=0;

// sequence info data
int Cstrike_SequenceInfo[] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0..9   
	0, 1, 2, 0, 1, 2, 0, 1, 2, 0, // 10..19 
	1, 2, 0, 1, 1, 2, 0, 1, 1, 2, // 20..29 
	0, 1, 2, 0, 1, 2, 0, 1, 2, 0, // 30..39 
	1, 2, 0, 1, 2, 0, 1, 2, 0, 1, // 40..49 
	2, 0, 1, 2, 0, 0, 0, 4, 0, 4, // 50..59 
	0, 5, 0, 5, 0, 0, 1, 1, 2, 0, // 60..69 
	1, 1, 2, 0, 1, 0, 1, 0, 1, 2, // 70..79 
	0, 1, 2, 3, 3, 3, 3, 3, 3, 3, // 80..89 
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 90..99 
	3
};
enum { SEQUENCE_SHOOT=1, SEQUENCE_RELOAD=2, SEQUENCE_DIE=3, SEQUENCE_THROW=4, SEQUENCE_ARM_C4=5 };
char* Cstrike_infotext[] = {"", "- PENG -", "- RELOADING -", "", "*** THROWING ***", "- ARMING C4 -" };

// points system
struct PointSystemEntry { char description[16]; float points; };
PointSystemEntry pointSystem[] = {
	"shoot_at_me", +600.0  , // 0
	"aim_at_me"  , +500.0  , // 1
	"visible"    , +250.0  , // 2
	"bestplayer" , +75.0   , // 3
	"arm_c4"     , +50.0   , // 4
	"behind_me"  , -250.0  , // 5
	"dist_factor", +180000.0  , // 6  // factor/distance = addvalue
	"fov_factor" , -15.0      , // 7
	"awp_bonus"  , +100.0     , // 8
	"",0.0
};

// should current Aimbot calculated view angles be used?
bool   Aimbot_active=false;

// Lock var with in min and max
#define BOUND_VALUE(var,min,max) if((var)>(max)){(var)=(max);};if((var)<(min)){(var)=(min);}

// Find the Highest Value out of a,b and c
#define MAX_VALUE(a,b,c) ( a>b? (a>c?a:(b>c?b:c)) : (b>c?b:c) )

// ====== sound data
static ColorEntry dummycolor;
class SoundMarker
{
public:
	SoundMarker() : color(&dummycolor),priority(0) {} // "snd_default"

	float origin[3];
	char  description[16];
	int team;
	int index;
	StopTimer timer;
	ColorEntry* color;
	int priority; // 0/1/2
};
typedef vector<SoundMarker> SoundMarkerVector;
SoundMarkerVector vecSoundMarker(65);
int soundMarkerCurrentIndex = 0;

// ====== directories
char ogcdir [256]=""; // set in dllmain
char hldir  [256]=""; // set in dllmain

// beamindex (sprites/beam.spr)
int beamindex = 0;

// mod info
enum{ MOD_UNKNOWN=0,MOD_CSTRIKE=1,MOD_DOD=2 };
int mod=MOD_UNKNOWN; 

// global timers/variables
net_status_s ns;
StopTimer pingTimer;
StopTimer doubleKill;
StopTimer tripleKill;
vector<DeathInfos> myKills;

// get points
int getPoints(const char* hittype)
{
	double damage = 0;
	if(strstr(hittype,"headshot")) //No armour, lots of damage
		damage = 4;
	if(strstr(hittype,"helmet")) //Still a headshot, lots of damage
		damage = 3;
	if(strstr(hittype,"kevlar")) //Armour, not much damage
		damage = 1;
	if(strstr(hittype,"flesh")) //No armour, some damage
		damage = 2;

	return damage;
}

//========================================================================================
string getOgcDirFile(const char* basename)
{
	if(strstr(basename,"..")){ return ":*?\\/<>\""; }
	string ret = ogcdir;
	return (ret+basename);
}

//========================================================================================
void deleteMultipleFiles(const char* basedir, const char* wildcard)
{
	WIN32_FIND_DATA findData;
	HANDLE hFindFile;
	char* fileName = findData.cFileName;

	string searchPattern = basedir;
	searchPattern += wildcard;
	
	hFindFile = FindFirstFile( searchPattern.c_str(), &findData );
	if( hFindFile == INVALID_HANDLE_VALUE){ return; }
	do{
		string fullname = basedir;
		fullname += fileName;
		DeleteFile(fullname.c_str());
	} while( FindNextFile(hFindFile,&findData) );
}

//========================================================================================
void cleanupWindowsTempFiles()
{
	
}

//========================================================================================
inline float GetPseudoDistance(const float* const pos1, const float* const pos2)
{
	return
		fabs(pos1[0]-pos2[0])+
		fabs(pos1[1]-pos2[1])+
		fabs(pos1[2]-pos2[2]);
}

//========================================================================================
inline float GetDistanceFrom(float* pos)
{	
	register double a = pos[0] - me.pmEyePos[0];
	register double b = pos[1] - me.pmEyePos[1];
	register double c = pos[2] - me.pmEyePos[2];
	return sqrt(a*a + b*b + c*c);
}

//========================================================================================
bool __fastcall CalcScreen(float *origin,float *vecScreen)
{
	return NewCalcScreen(origin, vecScreen);
}

//========================================================================================
void gDrawFilledBoxAtLocation( float* origin, DWORD color, int radius)
{
	float vecScreen[2]; 
	if( !CalcScreen(origin, vecScreen) ) { return; }

	int red   = (color>>24);
	int green = (color>>16)&0xFF;
	int blue  = (color>>8)&0xFF;
	int alpha = (color)&0xFF;
	int radius2 = radius<<1;

	gEngfuncs.pfnFillRGBA(vecScreen[0]-radius,vecScreen[1]-radius,radius2,radius2,red,green,blue,alpha);
}

//========================================================================================
void SPR_Draw (int frame, int x, int y, const wrect_t *prc)
{
	if (!sniper) gEngfuncs.pfnSPR_Draw (frame, x, y, prc);
}

//========================================================================================
void SPR_DrawHoles (int frame, int x, int y, const wrect_t *prc)
{
	if (!sniper) gEngfuncs.pfnSPR_DrawHoles (frame, x, y, prc);
}

//========================================================================================
HSPRITE sprset_hsprite = 0;
int     sprset_r, sprset_g, sprset_b;
extern bool oglSubtractive;
void SPR_DrawAdditive( int frame, int x, int y, const wrect_t *prc )
{
	if(cvar.hud) gEngfuncs.pfnSPR_Set (sprset_hsprite,sprset_r,sprset_g,sprset_b);
	gEngfuncs.pfnSPR_DrawAdditive(frame,x,y,prc);

}

//========================================================================================
void FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a )
{
	if(cvar.hud)
	{
		register ColorEntry* color = colorList.get(5);
		gEngfuncs.pfnFillRGBA(x,y,width,height,color->r,color->g,color->b,a);
	} else {
		gEngfuncs.pfnFillRGBA(x,y,width,height,r,g,b,a);
	}
}
	
//========================================================================================
int	DrawCharacter( int x, int y, int number, int r, int g, int b )
{
	if( b==0 && g>0 && cvar.hud)
	{
		register double ratio = (double(r)/double(g));
		if( ratio>=1.5 && ratio<=1.7 )
		{
			register ColorEntry* color = colorList.get(5);
			return gEngfuncs.pfnDrawCharacter(x,y,number,color->r*r/255,color->g*r/255,color->b*r/255);
		}
	}
	return gEngfuncs.pfnDrawCharacter(x,y,number,r,g,b);
}

//========================================================================================
static void updateLocalPlayer()
{
	me.ent    = gEngfuncs.GetLocalPlayer();

	static cl_entity_s dummy;
	memset((char*)&dummy,0,sizeof(dummy));
	
	if(!me.ent){ me.ent = &dummy; }
}

//========================================================================================
void SPR_Set (HSPRITE hPic, int r, int g, int b)
{
	if(cvar.sniper)
	{
		const char * SPR_TOP        = "sprites/top";
		const char * SPR_BOTTOM     = "sprites/bottom";
		const char * SPR_LEFT       = "sprites/left";
		const char * SPR_RIGHT      = "sprites/right";
		const char * SPR_VERTICAL   = "sprites/vertical";
		const char * SPR_HORIZONTAL = "sprites/horizontal";
		const char * sprite_name    =  sprite_lookup(hPic);	

		if( !strncmp(sprite_name, SPR_TOP,      strlen(SPR_TOP)       )
		||  !strncmp(sprite_name, SPR_BOTTOM,   strlen(SPR_BOTTOM)    )
		||  !strncmp(sprite_name, SPR_LEFT,     strlen(SPR_LEFT)      )
		||  !strncmp(sprite_name, SPR_RIGHT,    strlen(SPR_RIGHT)     )
		||  !strncmp(sprite_name, SPR_VERTICAL, strlen(SPR_VERTICAL)  )
		||  !strncmp(sprite_name, SPR_RIGHT,    strlen(SPR_HORIZONTAL)))
		{
			sniper=true;
		} 
		else sniper = false;
	}
	else sniper = false;

	if(cvar.hud)
	{
		register ColorEntry* color = colorList.get(5);
		sprset_hsprite=hPic;
		int intensity = MAX_VALUE(r,g,b);
		sprset_r = color->r*intensity/255;
		sprset_g = color->g*intensity/255;
		sprset_b = color->b*intensity/255;
	}
	
	gEngfuncs.pfnSPR_Set (hPic,r,g,b);
}

//========================================================================================
void tintArea(int x,int y,int w,int h, ColorEntry* clr)
{
	oglSubtractive = true;
	gEngfuncs.pfnFillRGBA(x,y,w,h,clr->r,clr->g,clr->b,clr->a);
	oglSubtractive = false;
}

//========================================================================================
int DrawLen(char *fmt)
{
	int len=0;
	for ( char * p = fmt; *p; p++ ) len+=screeninfo.charWidths[*p]; 
	return len;
}

//========================================================================================
void DrawHudStringCenter (int x, int y, int r, int g, int b, const char *fmt, ... )
{
	va_list va_alist;
	char buf[256];

	va_start (va_alist, fmt);
	_vsnprintf (buf, sizeof(buf), fmt, va_alist);
	va_end (va_alist);
	
	if( cvar.confont ) {
		int length, height;
		gEngfuncs.pfnDrawConsoleStringLen( buf, &length, &height );
		x = x - length/2;
		gEngfuncs.pfnDrawSetTextColor((float)r/255.0,(float)g/255.0,(float)b/255.0);
		gEngfuncs.pfnDrawConsoleString(x,y,buf);

	} else {
		// y-check
		int borderY = displayCenterY*2-18;
		if( y<0 || y>borderY ) { return; }

		int drawLen = DrawLen(buf);
		x = x-drawLen/2;

		if(cvar.text_background)
		{
			tintArea(x-2,y,drawLen+4,16,colorList.get(24));
		}

		int borderX = displayCenterX*2-11;
		int minX = x;
		int maxX = x+drawLen;
		bool needSingleCheck = (minX<1 || maxX>borderX);

		if( needSingleCheck )
		{
			for ( char * p = buf; *p; p++ )
			{
				int next = x + screeninfo.charWidths[*p];
				// IMPORTANT NOTE: when drawing admin-mod style charactters
				//    you MAY NOT provide x/y coordinates that cause drawing
				//    off screen. This causes HL to crash or just quit
				if( x>0 && x<borderX )
					gEngfuncs.pfnDrawCharacter (x, y, *p, r, g, b);
				x = next;
			}
		} else {
			for ( char * p = buf; *p; p++ )
			{
				int next = x + screeninfo.charWidths[*p];
				
				// IMPORTANT NOTE: when drawing admin-mod style charactters
				//    you MAY NOT provide x/y coordinates that cause drawing
				//    off screen. This causes HL to crash or just quit
				gEngfuncs.pfnDrawCharacter (x, y, *p, r, g, b);
				x = next;
			}
		}
	}
}

//========================================================================================
void DrawHudString (int x, int y, int r, int g, int b, const char *fmt, ... )
{
	va_list va_alist;
	char buf[256];

	va_start (va_alist, fmt);
	_vsnprintf (buf, sizeof(buf), fmt, va_alist);
	va_end (va_alist);
	
	if(cvar.confont)
	{
		gEngfuncs.pfnDrawSetTextColor((float)r/255.0,(float)g/255.0,(float)b/255.0);
		gEngfuncs.pfnDrawConsoleString(x,y,buf);
	} else {
		// y-check
		int borderY = displayCenterY*2-18;
		if( y<0 || y>borderY ) { return; }
		
		bool needSingleCheck=false;
		int borderX = displayCenterX*2-11;
		
		int drawLen = DrawLen(buf);
		if( x<1 ){ needSingleCheck=true; }
		else
		{
			int maxX = x+drawLen;
			needSingleCheck = (maxX>borderX);
		}

		if(cvar.text_background)
		{
			tintArea(x-2,y,drawLen+4,16,colorList.get(24));
		}

		if( needSingleCheck )
		{
			for ( char * p = buf; *p; p++ )
			{
				int next = x + screeninfo.charWidths[*p];
				// IMPORTANT NOTE: when drawing admin-mod style charactters
				//    you MAY NOT provide x/y coordinates that cause drawing
				//    off screen. This causes HL to crash or just quit
				if( x>0 && x<borderX )
					gEngfuncs.pfnDrawCharacter (x, y, *p, r, g, b);
				x = next;
			}
		}else {
			for ( char * p = buf; *p; p++ )
			{
				int next = x + screeninfo.charWidths[*p];
				// IMPORTANT NOTE: when drawing admin-mod style charactters
				//    you MAY NOT provide x/y coordinates that cause drawing
				//    off screen. This causes HL to crash or just quit
				gEngfuncs.pfnDrawCharacter (x, y, *p, r, g, b);
				x = next;
			}
		}
	}
}

//========================================================================================
void Con_Echo(const char *fmt, ... )
{
	va_list va_alist;
	char buf[256];

	va_start (va_alist, fmt);
	_vsnprintf (buf, sizeof(buf), fmt, va_alist);
	va_end (va_alist);

	// echo + scroll up:
	gConsole.echo("%s",buf);
}

//======================== USER MESSAGE HOOKS ===============================================
#define USER_MSG_INTERCEPT(name) \
	static pfnUserMsgHook name ## Org = NULL; \
	static int UserMsg ## name ( const char *pszName, int iSize, void *pbuf)
#define USER_MSG_CALL(name) (*name ## Org)(pszName,iSize,pbuf)

//===========================================================================================
USER_MSG_INTERCEPT(TeamInfo)
{
	static bool         done=false;
	static StringFinder teamNumber;
	if(!done)
	{
		// cs
		teamNumber.add("terrorist"  ,1);teamNumber.add("ct"         ,2);
		teamNumber.add("unassigned" ,0);teamNumber.add("spectator"  ,0);
		teamNumber.add("axis"   ,1);teamNumber.add("allies" ,2);
		teamNumber.add("red force", 1);teamNumber.add("blue force",2);
		teamNumber.add("usa",    2);teamNumber.add("up-ko",  4);
		teamNumber.add("rebels", 2);teamNumber.add("agents", 4);
		teamNumber.add("united nations", 2);teamNumber.add("arab liberation force", 4);
		teamNumber.add("#dustbowl_team1",2 );teamNumber.add("#dustbowl_team2",3 );
		teamNumber.add("#dunted_team1"  ,2 );teamNumber.add("#dunted_team2"  ,2 );
		teamNumber.add("#dunted_team3"  ,3 );
		teamNumber.add("red"    ,1);teamNumber.add("blue"   ,2);
		teamNumber.add("yellow" ,3);teamNumber.add("green"  ,4);

		done = true;
	}

	BEGIN_READ(pbuf,iSize);
	int px = READ_BYTE();
	char * teamtext = READ_STRING();

	updateLocalPlayer();

	if(!*teamtext) { vPlayers[px].team = 0; }
	else
	{
		_strlwr(teamtext);
		if(teamNumber.find(teamtext))
		{
			vPlayers[px].team = teamNumber.num;
			if(px==me.ent->index){ me.team = teamNumber.num; }
		}
		else
		{
            Con_Echo("TeamInfo: unknown px=%d, team=%s",px,teamtext);
			vPlayers[px].team = -1;
			if(px==me.ent->index){ me.team = -1; }
		}
	}

	return USER_MSG_CALL(TeamInfo);
}

//===========================================================================================
USER_MSG_INTERCEPT(CurWeapon)
{
	BEGIN_READ( pbuf, iSize );
	int iState = READ_BYTE();
	int iID    = READ_CHAR();
	int iClip  = READ_CHAR();
	if (iState) me.iClip = iClip;
	playerItems.msgCurWeapon (iState,iID,iClip);

	// update our own weaponID:
	if(iState)
	{
		char * name = playerItems.getNamebyId(iID);

		if(!strcmp(name,"mp5navy")) currentWeaponID = WEAPON_MP5;
		else currentWeaponID = burstInfo.getWeaponID( name );
	}

	return USER_MSG_CALL(CurWeapon);
}

//========================================================================================
USER_MSG_INTERCEPT(Concuss)
{
	return 1;
}

//========================================================================================
USER_MSG_INTERCEPT(SayText)
{
	updateLocalPlayer();

	if(gConsole.mode == OgcConsole::MODE_CHAT)
	{
		BEGIN_READ( pbuf, iSize);
		int    client = READ_BYTE(); // the client who spoke the message
		char*  text   = READ_STRING();

		// cut name
		char* realtext = strstr(text," : ");
		if(realtext)
		{
			char* name = text; 
			*realtext=0;
			realtext+=3;
			while(*realtext==' ')realtext++;

			if( vPlayers[client].entinfo.name[0]!='\\' || client==me.ent->index ) 
			{
				cmd.exec("sigmessage");
				gConsole.say(realtext,name,vPlayers[client].team );
				return 1;
			} 
		}
	}
	return USER_MSG_CALL(SayText);
}

//========================================================================================
USER_MSG_INTERCEPT(DeathMsg)
{
	updateLocalPlayer();

	//DeathMsg: [B]Killer [B]Victim [B]Headshot [S]Weapon Name
	BEGIN_READ( pbuf, iSize );
	int killer = READ_BYTE();
	int victim = READ_BYTE();
	int headshot = READ_BYTE();
    char* weaponName = READ_STRING();
	
	vPlayers[victim].health = 0;
	vPlayers[victim].hitpoints = 0;
	
	if(victim==me.ent->index)
	{	
		cvar.spec_fix = false;
	}

	if(cvar.alive_method==1)
	{
		vPlayers[victim].setAlive(false);
		if(victim==me.ent->index){ me.alive = false; }
	}

	if( killer==me.ent->index && headshot)
	{
		bGotHeadshot=true;
	}
	
	if(victim==me.ent->index && killer!=victim && cvar.death)
	{
		int ax = killer;
		if(ax!=-1)
		{
			PlayerInfo& theKiller = vPlayers[ax];
			float* from = theKiller.getEnt()->curstate.origin;
			float* to   = me.pmEyePos;

			VectorCopy(from,deathInfos.from);
			VectorCopy(to,deathInfos.to);

			if( theKiller.getEnt()->curstate.usehull ) deathInfos.from[2]+=12.1f;
			else                                       deathInfos.from[2]+=17.1f;

			deathInfos.killerName = vPlayers[ax].entinfo.name;
			deathInfos.weaponName = weaponName;
			deathInfos.active     = true;
		}
	}

	else if(victim!=me.ent->index && killer==me.ent->index && cvar.frag)
	{
		int ax = victim;
		if(ax!=-1)
		{
			PlayerInfo& theVictim = vPlayers[ax];
			float* from = me.pmEyePos;
			float* to   = theVictim.getEnt()->curstate.origin;

			DeathInfos newKill;

			VectorCopy(from,newKill.from);
			VectorCopy(to,newKill.to);

			if( theVictim.getEnt()->curstate.usehull ) newKill.from[2]+=12.1f;
			else                                       newKill.from[2]+=17.1f;

			newKill.victimName = vPlayers[ax].entinfo.name;
			newKill.weaponName = weaponName;
			newKill.active     = false;
			myKills.push_back(newKill);
		}
	}
	
	if(victim!=me.ent->index && killer==me.ent->index && cvar.kill_sound)
	{
		if (tripleKill.running()){
			cmd.exec("play sounds\\triplekill.wav");
		}
		if (doubleKill.running()){
			
			if(!tripleKill.running())
				cmd.exec("play sounds\\doublekill.wav");
			tripleKill.countdown(1);
		}
		if (headshot) { 
			if(!doubleKill.running())
				cmd.exec("play sounds\\headshot.wav");
		}
		doubleKill.countdown(1);
	}
	return USER_MSG_CALL(DeathMsg);
}

//========================================================================================
USER_MSG_INTERCEPT(Money)
{
	BEGIN_READ( pbuf, iSize );
	playerItems.money = READ_SHORT();
	return USER_MSG_CALL(Money);
}

//========================================================================================
USER_MSG_INTERCEPT(Battery)
{
	BEGIN_READ( pbuf, iSize );
	playerItems.armor = READ_BYTE();
	return USER_MSG_CALL(Battery);
}

//========================================================================================
USER_MSG_INTERCEPT(SetFOV)
{
	BEGIN_READ( pbuf, iSize );
	me.iFOV = READ_BYTE();
	if(!me.iFOV)   { me.iFOV=90; }

	if(me.iFOV==90){ me.inZoomMode=false; }
	else           { me.inZoomMode=true;  } 

	if(cvar.antizoom && me.iFOV!=90 ) 
	{ 
		me.iFOV     = 89; 
		((unsigned char*)pbuf)[0]=89;	
	}

	iCurrentFOV = me.iFOV;
	fCurrentFOV = me.iFOV;
	return USER_MSG_CALL(SetFOV);
}

//========================================================================================
USER_MSG_INTERCEPT(ScoreAttrib)
{
	updateLocalPlayer();

	BEGIN_READ(pbuf, iSize);
    int idx  = READ_BYTE();
    int info = READ_BYTE();
    vPlayers[idx].iInfo = info;

	if( cvar.alive_method==0)
	{
		if(info&1) { vPlayers[idx].setAlive(false); vPlayers[idx].clearPVS(); }
		else       { vPlayers[idx].setAlive(); }
	}

	if(idx==me.ent->index) me.alive = ((info&1)==0);

	return USER_MSG_CALL(ScoreAttrib);
}

//========================================================================================
USER_MSG_INTERCEPT(ScoreInfo)
{
	updateLocalPlayer();

	BEGIN_READ(pbuf, iSize);
	int idx = READ_BYTE();
	PlayerInfo& r = vPlayers[idx];

	r.frags  = READ_SHORT();
	r.deaths = READ_SHORT();

	if(r.frags<=0 )   { r.ratio=0; }
	else              { r.ratio = (double)r.frags / ((r.deaths<=0)?1:r.deaths); }

	// update bestplayer
	r.bestplayer = false;
	bool foundbetter = false;
	for(int i=0;i<vPlayers.size();i++)
	{
		if     ( r.ratio < vPlayers[i].ratio ) foundbetter = true;
		else if( r.ratio > vPlayers[i].ratio ) vPlayers[i].bestplayer=false;
	}
	if(!foundbetter) r.bestplayer = true;

	
	// score handling:
	if(idx==me.ent->index)
	{
		static int lastfrags=0;
		static int lastdeaths=0;

		if(me.headshots>r.frags || r.frags==0) 
		{ 
			// reset statistics
			me.headshots = 0; 
			me.hspercent = 0;
			bGotHeadshot = false; 
			lastfrags    = r.frags;
			lastdeaths   = r.deaths;
		}
		else
		{
			if(bGotHeadshot)         { ++me.headshots; }

			double den = r.frags ? (double)r.frags : 1.0;
			me.hspercent = (double)me.headshots/den*100.0;

			if(cvar.saystats && (lastfrags!=r.frags || lastdeaths!=r.deaths ))
			{
				char tmp[128];
				switch(cvar.saystats)
				{
				case 1:	
					sprintf(tmp,"say -] -[UGC]- [-BETA-] | KILLS: %d | DEATHS: %d | HEADSHOTS: %d (%4.1f) [-",
						r.frags,r.deaths,me.headshots,me.hspercent);
					gEngfuncs.pfnClientCmd(tmp);
					break;
				case 2:
					char* tag = "&y";
					if( r.frags>lastfrags  ) tag = "&g";
					if( r.frags<lastfrags  ) tag = "&r";
					if( r.deaths>lastdeaths) tag = "&r";

					Con_Echo("%s-] -[UGC]- [-BETA-] | KILLS: %d | DEATHS: %d | HEADSHOTS: %d (%4.1f) [-",
						tag,r.frags,r.deaths,me.headshots,me.hspercent);
					break;
				}
			}
		}
		bGotHeadshot = false;
		lastfrags    = r.frags;
		lastdeaths   = r.deaths;
	}

	return USER_MSG_CALL(ScoreInfo);
}

//========================================================================================
USER_MSG_INTERCEPT(TextMsg)
{
	updateLocalPlayer();

	BEGIN_READ(pbuf, iSize);
	int id = READ_BYTE();
	char* msg = READ_STRING();

	if( *msg )
	{
		if (!strcmp(msg, "#Bomb_Planted"))
		{
			fBombPlantedTime = ClientTime::current;
			bBombIsPlanted=true;
		}
		else if (!strcmp(msg, "#Target_Bombed"))
		{
			if (bBombIsPlanted)
			{
				iC4Timer = (int) ((ClientTime::current-fBombPlantedTime)+0.4999999);
				if(iC4Timer<0 || iC4Timer>120)  {  iC4Timer=0;  } 
				bBombIsPlanted=false;
			}
		}
		else if (!strcmp(msg, "#Bomb_Defused"))
		{
			bBombIsPlanted=false;
		}

	}

	return USER_MSG_CALL(TextMsg);
}

//========================================================================================
void AtRoundStart(void);
USER_MSG_INTERCEPT(ResetHUD)
{
	updateLocalPlayer();

	bBombIsPlanted=false;
	if (iRounds) AtRoundStart();
	iRounds++;

	return USER_MSG_CALL(ResetHUD);
}

//========================================================================================
USER_MSG_INTERCEPT(WeaponList)
{
	updateLocalPlayer();
	playerItems.msgWeaponList(iSize,pbuf);
	return USER_MSG_CALL(WeaponList);
}

//========================================================================================
int	HookUserMsg (char *szMsgName, pfnUserMsgHook pfn)
{
	int ret = gEngfuncs.pfnHookUserMsg (szMsgName, pfn);

	#define REDIRECT_MESSAGE(name) \
		else if (!strcmp(szMsgName,#name)) \
		{ \
			name##Org = pfn; \
			return gEngfuncs.pfnHookUserMsg (szMsgName, UserMsg##name ); \
		}

	if(0){}
	REDIRECT_MESSAGE( TeamInfo    ) REDIRECT_MESSAGE( CurWeapon   )
	REDIRECT_MESSAGE( ScoreAttrib ) REDIRECT_MESSAGE( ScoreInfo   )
	REDIRECT_MESSAGE( SetFOV      ) REDIRECT_MESSAGE( TextMsg     )
	REDIRECT_MESSAGE( ResetHUD    ) REDIRECT_MESSAGE( WeaponList  )
	REDIRECT_MESSAGE( Money       ) REDIRECT_MESSAGE( Battery     )
	REDIRECT_MESSAGE( DeathMsg    ) REDIRECT_MESSAGE( Concuss     )
	REDIRECT_MESSAGE( SayText     ) 
	return ret;
}

//========================================================================================
int	( *OrigStudioDrawPlayer	)( int flags, struct entity_state_s *pplayer );
int	 NewStudioDrawPlayer ( int flags, struct entity_state_s *pplayer )
{
	int ret = OrigStudioDrawPlayer ( flags, pplayer );
	gAimbot.PostStudioDrawPlayer( flags, pplayer);
	return ret;
}

//========================================================================================
int HUD_GetStudioModelInterface( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio )
{
	int nResult = pHUD_GetStudioModelInterface(version,ppinterface,pstudio);

	memcpy( &IEngineStudio, pstudio, sizeof( IEngineStudio ) );
	pIEngineStudio = &IEngineStudio;
    
	if(ppinterface && *ppinterface)
	{
		OrigStudioDrawPlayer = (*ppinterface)->StudioDrawPlayer;
		(*ppinterface)->StudioDrawPlayer = NewStudioDrawPlayer;
	}
	return nResult;
}

//========================================================================================
inline bool isEnemy(int ax)
{
	return (me.team != vPlayers[ax].team);
}

//========================================================================================
ColorEntry* PlayerColor(int ax)
{
	static ColorEntry colorDefault;
	int i = 0;
	
	if((!vPlayers[ax].getAlive() && vPlayers[ax].health == 0) && vPlayers[ax].team)
		return colorList.get(48+vPlayers[ax].team);

	if (cvar.tglow   && (ax == gAimbot.target))
		return colorList.get(4);

	if(cvar.sglow && vPlayers[ax].getEnt()->curstate.body && vPlayers[ax].team == 1)
		return colorList.get(3);
	if(cvar.sglow && strstr(vPlayers[ax].entinfo.model,"vip"))
		return colorList.get(3);

	if(cvar.rglow)
	{
		int seqinfo = vPlayers[ax].getEnt()->curstate.sequence;
		if( seqinfo>1)
		if( Cstrike_SequenceInfo[seqinfo] == 2 )
			i = 37;
	}

	if (vPlayers[ax].team == 1) { return colorList.get(i + 1); }
	else                        { return colorList.get(i + 2); }
}

//========================================================================================
HSPRITE ch_sniper=-1, sniper_scope=-1;
void drawCrosshair()
{
	if( me.inZoomMode ) { return; }

	int centerX = displayCenterX;
	int centerY = displayCenterY;
	ColorEntry* clr = colorList.get(0);
	int r=clr->r, g=clr->g, b=clr->b, a=clr->a;
	
	switch(cvar.cross)
	{
	case 1:
		gEngfuncs.pfnFillRGBA(centerX - 14, centerY, 9, 1,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX +5,   centerY, 9, 1,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX, centerY - 14, 1, 9,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX, centerY +  5, 1, 9,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX, centerY     , 1, 1,r,g,b,a); // center
		break;
	case 2:
		gEngfuncs.pfnFillRGBA(centerX - 14, centerY, 9,2,r,g,b,a); // left
		gEngfuncs.pfnFillRGBA(centerX +6,   centerY, 9,2,r,g,b,a); // right
		gEngfuncs.pfnFillRGBA(centerX, centerY - 14, 2,9,r,g,b,a); // top
		gEngfuncs.pfnFillRGBA(centerX, centerY +  7, 2,9,r,g,b,a); // bottom
		gEngfuncs.pfnFillRGBA(centerX, centerY     , 2,2,r,g,b,a); // center
		break;
	case 3:
		gEngfuncs.pfnFillRGBA(centerX-25,centerY,50,1,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX,centerY-25,1,50,r,g,b,a);
		break;
	case 4:
		gEngfuncs.pfnFillRGBA(centerX-25,centerY,50,2,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX,centerY-25,2,50,r,g,b,a);
		break;
	case 5:
		if(ch_sniper!=-1){
			gEngfuncs.pfnSPR_Set(ch_sniper,255,255,255);
			gEngfuncs.pfnSPR_DrawHoles(0,displayCenterX-128,displayCenterY-128,NULL);
		}	
		break;
	case 6:
		if(sniper_scope!=-1){
			gEngfuncs.pfnSPR_Set(sniper_scope,255,255,255);
			gEngfuncs.pfnSPR_DrawHoles(0,displayCenterX-128,displayCenterY-128,NULL);
		}	
		break;
	case 7:
		gEngfuncs.pfnFillRGBA(0,centerY,2*centerX,1,r,g,b,a);
		gEngfuncs.pfnFillRGBA(centerX,0,1,2*centerY,r,g,b,a);
		break;
	}
}

//========================================================================================
void handleContinuousZoom()
{
	if( cvar.czoom == 1000 ) // leave zoom mode
	{
		// reset zoom
		iCurrentFOV = 90;
		fCurrentFOV = 90.0;
		(*SetFOVOrg)("SetFOV", 1, &iCurrentFOV);
		
		// prevent further zooming
		cvar.czoom = 0; 
	} else 	{
		// advance/decrease zoom level
		iCurrentFOV -= cvar.czoom;
		BOUND_VALUE(iCurrentFOV,10,90);
		fCurrentFOV = iCurrentFOV;

		(*SetFOVOrg)("SetFOV", 1, &iCurrentFOV);
	}
}

//========================================================================================
char* gGetWeaponName( int weaponmodel )
{
	static char weapon[50];
	weapon[0]=0;

	model_s* mdl = IEngineStudio.GetModelByIndex( weaponmodel );
	if( !mdl ){ return weapon; }

	char* name = mdl->name;  if( !name )          { return weapon; }
	int len = strlen(name);  if( len>48 || len<10){ return weapon; }

	strcpy(weapon,name+9); len -=9;
	if(len>4)weapon[len-4]=(char)0;

	return weapon;
}

//========================================================================================
inline void gDrawBoxAtScreenXY( int x, int y, int r, int g, int b, int alpha, int radius=1)
{
	int radius2 = radius<<1;
	gEngfuncs.pfnFillRGBA(x-radius+2, y-radius, radius2-2,2,r,g,b,alpha);
	gEngfuncs.pfnFillRGBA(x-radius, y-radius, 2,radius2,r,g,b,alpha);
	gEngfuncs.pfnFillRGBA(x-radius, y+radius, radius2,2,r,g,b,alpha);
	gEngfuncs.pfnFillRGBA(x+radius, y-radius, 2,radius2+2,r,g,b,alpha);
}

inline void gFillBoxAtScreenXY( int x, int y, int r, int g, int b, int alpha, int radius=1)
{
	gEngfuncs.pfnFillRGBA(x, y, radius, radius,r,g,b,alpha);
}

//========================================================================================
void calcRadarPoint(float* origin, int& screenx, int& screeny)
{
	float dx  = origin[0] - me.pmEyePos[0];
	float dy  = origin[1] - me.pmEyePos[1];

    // rotate
	float x =  dy*me.minus_cos_yaw + dx*me.sin_yaw;
	float y =  dx*me.minus_cos_yaw - dy*me.sin_yaw;

	float& range = cvar.radar_range;
	if(fabs(x)>range || fabs(y)>range)
	{ 
		// clipping
		if(y>x)
		{
			if(y>-x) {
				x = range*x/y;
				y = range;
			}  else  {
				y = -range*y/x; 
				x = -range; 
			}
		} else {
			if(y>-x) {
				y = range*y/x; 
				x = range; 
			}  else  {
				x = -range*x/y;
				y = -range;
			}
		}
	}

	screenx = cvar.radar_x+int(x/cvar.radar_range*float(cvar.radar_size));
	screeny = cvar.radar_y+int(y/cvar.radar_range*float(cvar.radar_size));
}

//========================================================================================
void drawRadarPoint(float* origin,int r,int g,int b,bool blink=false)
{
	int screenx,screeny;
	calcRadarPoint(origin,screenx,screeny);
	if(!blink || blinkPhase){ gEngfuncs.pfnFillRGBA(screenx,screeny,3,3,r,g,b,255);}
}

//========================================================================================
void drawRadarFrame()
{
	int& centerx = cvar.radar_x;
	int& centery = cvar.radar_y;
	int& size    = cvar.radar_size;
	
	ColorEntry* clr;
	
	clr = colorList.get(12);
	tintArea(centerx-size, centery-size, 2*size+2, 2*size+2, clr);

	clr = colorList.get(11);
	gEngfuncs.pfnFillRGBA(centerx,centery-size,1,2*size,clr->r,clr->g,clr->b,clr->a);
	gEngfuncs.pfnFillRGBA(centerx-size,centery,2*size,1,clr->r,clr->g,clr->b,clr->a);
}

//========================================================================================
static OgcCommandMenu::Menu* curMenu = ogcMenu.baseMenu;
bool  menu_active = false;

//========================================================================================
bool gMenuActive(){ return menu_active; }

//========================================================================================
void drawMenu(  )
{
	if(!menu_active) return;
	if(!curMenu) { curMenu = ogcMenu.baseMenu; }

	vector<OgcCommandMenu::MenuEntry>& items = curMenu->items;

	int x=cvar.menu_x, y=cvar.menu_y, w=cvar.menu_w;
	enum{ TEXT_H = 20 };
	int title_h = TEXT_H+5;
	int frame_y = cvar.menu_y - title_h;
	int frame_h = items.size()*TEXT_H + title_h;

	// title background
	tintArea(x,frame_y,w,title_h,colorList.get(21));

	// upper background
	int h1 = curMenu->selection*TEXT_H;
	if(h1)
	{
		tintArea(x,y,w,h1,colorList.get(21));
		y+=h1;
	}
	
	// selection background
	tintArea(x,y,w,TEXT_H,colorList.get(22));
	y+=TEXT_H;

	// lower background
	int numItemsLeft = items.size() - curMenu->selection - 1;
	if(numItemsLeft>0)
	{
		tintArea(x,y,w,numItemsLeft*TEXT_H,colorList.get(21));
	}

	// draw frame
	register int c = 200;
	gEngfuncs.pfnFillRGBA(x,frame_y,w,1        ,c,c,c,c);
	gEngfuncs.pfnFillRGBA(x,frame_y,1,frame_h  ,c,c,c,c);
	gEngfuncs.pfnFillRGBA(x+w,frame_y,1,frame_h,c,c,c,c);
	gEngfuncs.pfnFillRGBA(x,frame_y+frame_h,w,1,c,c,c,c);
	gEngfuncs.pfnFillRGBA(x,cvar.menu_y,w+1,1  ,c,c,c,c);

	// title text
	gEngfuncs.pfnDrawSetTextColor (255,255,0);
	gEngfuncs.pfnDrawConsoleString(cvar.menu_x+10,frame_y+5, const_cast<char*>(curMenu->name.c_str()));
	

	// draw text
	y=cvar.menu_y+4;
	x+=4;
	for(int i=0;i<items.size();i++)
	{
		OgcCommandMenu::MenuEntry& item = items[i];

		ColorEntry* clr = colorList.get(23); // "menu_text"
		gEngfuncs.pfnDrawSetTextColor(clr->onebased_r,clr->onebased_g,clr->onebased_b);
		gEngfuncs.pfnDrawConsoleString(x,y,const_cast<char*>(item.name.c_str()));
		y+=TEXT_H;
	}
}

//========================================================================================
void menu_describe_current()
{
	if(!menu_active||!curMenu) return;
	OgcCommandMenu::MenuEntry& entry = curMenu->items[curMenu->selection];
	strcpy(gHudMessage, entry.description.c_str());
	gHudTimer.countdown(6);
}

//========================================================================================
void sendScriptSignal(const char* name)
{
	char signal[32];
	sprintf(signal,"sig%s",name);
	cmd.exec(signal);
}

//========================================================================================
inline void drawPlayerEsp(int ax)
{
	if( !vPlayers[ax].getPVS() ) { return; }
	
	cl_entity_s* ent = vPlayers[ax].getEnt();
	ColorEntry* color = PlayerColor(ax);

	int r = color->r * cvar.esp_trans;
	int g = color->g * cvar.esp_trans;
	int b = color->b * cvar.esp_trans;
	int a = color->a * cvar.esp_trans;

	oglSubtractive = true;
	switch(cvar.radar)
	{
		case 0: break;
		case 1: drawRadarPoint(ent->origin,color->r,color->g,color->b); break;
		case 2: if( isEnemy(ax)) drawRadarPoint(ent->origin,color->r,color->g,color->b); break;
		case 3: if(!isEnemy(ax)) drawRadarPoint(ent->origin,color->r,color->g,color->b); break;
	}
	oglSubtractive = false;

	float vecScreen[2];
	if (!CalcScreen(ent->origin,vecScreen)){ return; }
	
	// distance and boxradius also needed for esp offset
	float distance = vPlayers[ax].distance/22.0f;
	int   boxradius = (300.0*90.0) / (distance*fCurrentFOV);
	BOUND_VALUE(boxradius,1,200);

	int x = vecScreen[0];
	int y = vecScreen[1];
	
	switch(cvar.box)
	{
	case 1:
		gDrawBoxAtScreenXY(x,y,r,g,b,a,boxradius);
		break;
	case 2:
		gDrawBoxAtScreenXY(x,y,r,g,b,a,10);
		break;
	}
	
	// ====== show visibility
	const char* format_string; 
	const char* format_int;
	if (vPlayers[ax].visible) { format_string="%s";  format_int="%i"; }
	else                      { format_string="-%s-";format_int="-%i-"; }

	// ===== prepare text esp:
	enum{ CHAR_HEIGHT = 16 };
	int text_dist = (int)(cvar.esp_offs*(double)boxradius);
	int ystep = -CHAR_HEIGHT;

	if(cvar.esp_fix)
	{
		x = vecScreen[0];
		y = vecScreen[1]-text_dist-CHAR_HEIGHT;
		if( cvar.esp_wrap && y<0 )
		{
			ystep = CHAR_HEIGHT;
			y = vecScreen[1]+text_dist;
		}
	}

	if (cvar.esp_line)
	{
		if(ystep>0) // stack down
		{
			gEngfuncs.pfnFillRGBA(x,vecScreen[1],1,text_dist,r,g,b,255);
		}
		else
		{
			gEngfuncs.pfnFillRGBA(x,y+CHAR_HEIGHT,1,text_dist,r,g,b,255);
		}
	}
	
	if (cvar.weapon)
	{
		if (cvar.reload && mod==MOD_CSTRIKE)
		{
			int seqinfo = Cstrike_SequenceInfo[ent->curstate.sequence];
			if( seqinfo>1)
			{
				DrawHudStringCenter(x,y,100,255,100,Cstrike_infotext[seqinfo]); 
			} 
			else
			{
				DrawHudStringCenter(x,y,r,g,b,format_string,gGetWeaponName(ent->curstate.weaponmodel));
			}
		}
		else
		{
			DrawHudStringCenter(x,y,r,g,b,format_string,gGetWeaponName(ent->curstate.weaponmodel));
		}
		y += ystep;
	}
	
	if (cvar.name )
	{
		char displayname[32];
		int len = (cvar.name==1) ? 8 : cvar.name;
		if(len<0 || len>30) { len = 30; }
		strncpy(displayname,vPlayers[ax].entinfo.name,len);
		displayname[len] = 0;
		
		DrawHudStringCenter(x,y,r,g,b,format_string,displayname); 
		y += ystep;
	}

	if (cvar.distance)
	{
		DrawHudStringCenter(x,y,r,g,b,format_int,  (short int)distance);
		y += ystep;
	}

	if (cvar.health_esp==1 && vPlayers[ax].getAlive())
	{
		int numBars = ceil(vPlayers[ax].hitpoints/2.0);
		ColorEntry* bar = colorList.get(43 + numBars);
		int offset = (numBars * 4) / 2;
		for(int i=0;i<numBars;i++)
			gFillBoxAtScreenXY(x+(i*4)-offset,y,bar->r,bar->g,bar->b,bar->a,3);
	}
}

//========================================================================================
inline void drawEsp(int ab)
{
	float vecScreen[2];
	char weapon[64];
	vec3_t EntViewOrg;
	VectorCopy(vEntity[ab].ent->origin,EntViewOrg);
				
	memset(&weapon,0,50);
	
	if(!(    vEntity[ab].ent 
		&& vEntity[ab].ent->model
		&& vEntity[ab].ent->model->name ))   { return; }
	
	char*        text = vEntity[ab].ent->model->name;
	ColorEntry* color = colorList.get(9);
	bool      special = false;
	int           len = strlen(text);
	if( len<8 ) { return; }
				
	char* w_pos = strstr(text, "w_");
	if (len>10 && w_pos)
	{
		// models/w_awp.mdl
		strcpy(weapon,text+9);
		if( (w_pos[2]=='c'&&w_pos[3]=='4')||  // "c4"
			(w_pos[2]=='b'&&w_pos[3]=='a'&&w_pos[4]=='c')  ) // "backpack"
		{ 
			color = colorList.get(3); special=true; 
		}
	}
	else 
	{
		// models/scientist.mdl models/hostage.mdl
		strcpy(weapon,text+7);
		vec3_t forward,right,up;
		gEngfuncs.pfnAngleVectors (vEntity[ab].ent->angles, forward, right, up);
		
		EntViewOrg = EntViewOrg + up * 75;
		color   = colorList.get(3); 
		special = true;
	}	
	
	if(cvar.radar&&special){ drawRadarPoint(EntViewOrg,color->r,color->g,color->b,special); }
				
	if(cvar.esp && CalcScreen(EntViewOrg,vecScreen) && (!special||blinkPhase) )
	{
		weapon[strlen(weapon)-4]=(char)0;
		DrawHudStringCenter(vecScreen[0],vecScreen[1],color->r,color->g,color->b,weapon);
	}
}

//========================================================================================
void ogc_exec(const char* filename)
{
	string file = getOgcDirFile(filename);
	file += ".cfg";
	cmd.execFile( file.c_str() );
}

//========================================================================================
void playerCalcExtraData(int ax, cl_entity_s* ent)
{
	PlayerInfo& r = vPlayers[ax];

	// entity trash filter
	if( ent->curstate.effects&EF_NODRAW ) { r.clearPVS(); return; }
	if( ent==me.ent                     ) { r.clearPVS(); return; }
	switch(ent->curstate.rendermode)
	{
	case kRenderTransColor:
	case kRenderTransTexture: 
		if( ent->curstate.renderamt<128 ) { r.clearPVS(); return; }
		break;
	case kRenderNormal:
		break;
	default:
		r.clearPVS();
		return;
	}
	if(!r.getPVS()) return; // dont calc data for non visible players

	r.distance  = GetDistanceFrom (ent->origin); if(r.distance<1) r.distance=1; // avoid division by zero
	gAimbot.calcFovangleAndVisibility(ax);
	r.points    = 0;

	// calc points
	float points      = 0.0;
	bool  aimingatme  = IsAimingAtMe(ax);
	int   seqinfo = (mod==MOD_CSTRIKE) ? Cstrike_SequenceInfo[ent->curstate.sequence] : 0;
	if(aimingatme)
	{
		if(seqinfo==SEQUENCE_SHOOT) points += pointSystem[0].points; // "shoot_at_me"
		else                        points += pointSystem[1].points; // "aim_at_me"  
	}
	if( r.visible                )  points += pointSystem[2].points; // "visible"
	if( r.bestplayer             )  points += pointSystem[3].points; // "bestplayer"
	if( seqinfo==SEQUENCE_ARM_C4 )  points += pointSystem[4].points; // "arm_c4"
	if( r.fovangle > 180.0       )  points += pointSystem[5].points; // "behind_me"
	points += pointSystem[6].points/r.distance; // r.distance cannot be 0 here (see above)
	points += pointSystem[7].points*r.fovangle;

	// add an awp bonus
	if(aimingatme)
	{
		// see if he has an awp
		model_s* mdl = IEngineStudio.GetModelByIndex( ent->curstate.weaponmodel );
		if( mdl && mdl->name )
		{
			// "weapons/p_awp.mdl"
			int len = strlen(mdl->name);
			if(len>10 && mdl->name[len-7]=='a' && mdl->name[len-6]=='w' && mdl->name[len-5]=='p')
			{
                points += pointSystem[8].points; // "awp_bonus"
			}
		}
	}

	// store points
	r.points = points;
}

//========================================================================================
void drawSound()
{
	int old = cvar.confont;cvar.confont = 1;
	for(int pri=0;pri<3;pri++)
	for(int i=0;i<cvar.soundmax;i++)
	{
		SoundMarker& ref = vecSoundMarker[i];
		if( ref.priority==pri && ref.timer.running() )
		{  
			ColorEntry* clr = colorList.get(6);

			if(cvar.sounddisplay==1)
			{
				float timeleft = ref.timer.timeleft();
				int alpha = (int)( (255.0f/cvar.soundtime)*(timeleft) );
				
				float distance = GetPseudoDistance(me.pmEyePos, ref.origin);
				int boxradius = (int)(3000.0f/distance);
				BOUND_VALUE(boxradius,2,40);

				clr->compiled &= 0xFFFFFF00;
				clr->compiled += alpha;

				gDrawFilledBoxAtLocation( ref.origin, clr->compiled, boxradius );
			} else if(cvar.sounddisplay==2) {
				float vecScreen[2];
				if (CalcScreen(ref.origin,vecScreen))
				{
					DrawHudStringCenter(vecScreen[0],vecScreen[1],ref.color->r,ref.color->g,ref.color->b,ref.description);
				}
			}

			if(cvar.soundradar)
			{
				int screenx, screeny;
				calcRadarPoint(ref.origin, screenx,screeny);
				gEngfuncs.pfnFillRGBA(screenx,screeny,2,2,ref.color->r,ref.color->g,ref.color->b,255);
			}
		}
	}
	cvar.confont = old;
}

//=======================================================================================
void hudDrawInfoTexts()
{
	gEngfuncs.pNetAPI->Status(&ns);
	int line_y = displayCenterY*2-180;
	int line_x = 20;
	#define DECREMENT_LINE_Y { line_y-=16; }

	if (cvar.wa_title && winamp.title.size())
	{
		ColorEntry* clr=colorList.get(13);
		int old = cvar.text_background; cvar.text_background = 1;
		DrawHudString(line_x,line_y,clr->r,clr->g,clr->b, winamp.title.c_str() );
		DECREMENT_LINE_Y
		cvar.text_background = old;
	}

	if (cvar.info)
	{
		ColorEntry* clr=colorList.get(13);

		if(gHudTimer.running())
		{
			DrawHudString(line_x,line_y,clr->r,clr->g,clr->b, gHudMessage );
			DECREMENT_LINE_Y
		}
        
		if( gHudTimer2.running() )
		{
			DrawHudString(line_x,line_y,clr->r,clr->g,clr->b,gHudMessage2 );
			DECREMENT_LINE_Y
		}
	}

	if(cvar.ugc_stats)
	{
		PlayerInfo& r = vPlayers[me.ent->index];
		int xf = 20;
		int yf = displayCenterY*2-180-64;
		ColorEntry* clrf = colorList.get(49);
		DrawHudString(xf, yf, clrf->r, clrf->g, clrf->b, "-] KILLS: %d | DEATHS: %d | HEADSHOTS: %d (%4.1f) [-" , r.frags,r.deaths,me.headshots,me.hspercent);
	}
	
	if(cvar.ugc_ping)
	{
		int xp = 20;
		int yp = displayCenterY*2-180-48;
		static float ping = ns.latency * 1000;
		if(!pingTimer.running())
		{
			pingTimer.countdown(1);
			ping = ns.latency * 1000;
		}
		ColorEntry* clrp = colorList.get(49);
		DrawHudString(xp, yp, clrp->r, clrp->g, clrp->b, "-] PING: %4.0f | -[UGC]- "UGC_VERSION" [-" , ping);		
	}
	
	if (bBombIsPlanted && cvar.bombtimer && blinkPhase)
	{
		int planted;
		if (!iC4Timer)  { planted = (ClientTime::current - fBombPlantedTime); }
		else            { planted = ((iC4Timer - (ClientTime::current - fBombPlantedTime))+1); }
		
		int planted_sec = planted % 60;
		int planted_min = planted / 60;
        
		char tmbuf[32];
		sprintf (tmbuf, "-] BOMB: %01d:%02d [-", planted_min, planted_sec); 

		ColorEntry* clr = colorList.get(7);
		DrawHudString(line_x,line_y, clr->r, clr->g, clr->b, tmbuf);
		DECREMENT_LINE_Y
	}         

	if( cvar.death && deathInfos.active )
	{
		ColorEntry* color = colorList.get(29);
		
		float vecScreen[2];
		if (CalcScreen(deathInfos.from,vecScreen))
		{ 
			DrawHudStringCenter(vecScreen[0],vecScreen[1]-10,color->r,color->g,color->b,"%s",deathInfos.killerName.c_str());
			DrawHudStringCenter(vecScreen[0],vecScreen[1]+10,color->r,color->g,color->b,"%s",deathInfos.weaponName.c_str());
		}
	} 

	if( cvar.frag && myKills.size() > 0 )
	{
		ColorEntry* color = colorList.get(49);
		
		for(int f=0;f<myKills.size();f++)
		{
			float vecScreen[2];
			if (CalcScreen(myKills[f].from,vecScreen))
			{ 
				DrawHudStringCenter(vecScreen[0],vecScreen[1]-10,color->r,color->g,color->b,"%s",myKills[f].victimName.c_str());
				DrawHudStringCenter(vecScreen[0],vecScreen[1]+10,color->r,color->g,color->b,"%s",myKills[f].weaponName.c_str());
			}
		}
	}
	
	if (cvar.systime)
	{
		SYSTEMTIME SysTime;
		GetLocalTime(&SysTime);
		if (SysTime.wHour>12)  SysTime.wHour-=12;

		ColorEntry* clr = colorList.get(8);
		int xx = displayCenterX*2 - 50;
		int yy = displayCenterY*2 - 95;
		char* formatstring   =   (SysTime.wMinute<10) ?  "%d:0%d" :  "%d:%d";

		DrawHudString(xx, yy, clr->r, clr->g, clr->b, formatstring, SysTime.wHour, SysTime.wMinute); 
	}

	if ( cvar.speed>1 )
	{
		extern DWORD speed_ticks_left;
		extern DWORD phase_ticks_left;

		int x = displayCenterX*2 - 100;
		int y = displayCenterY*2 - 195;
		ColorEntry* clr=colorList.get(13);
		
		if(cvar.speedoff == 0 )
			DrawHudString(x,y,120,120,255, "speed: nolimit");
		else
		{
		if(speed_ticks_left) DrawHudString(x,y   ,clr->r,clr->g,clr->b, "speed: %d",speed_ticks_left );
		else                 DrawHudString(x,y-20,clr->r,clr->g,clr->b, "wait.. %d",phase_ticks_left );
		}	
	}
}

//========================================================================================
static void gStopMovement()
{
	bStopMovement = true;
}

//========================================================================================
static void gFreeMovement()
{
	bStopMovement = false;	
}

//========================================================================================
static void gAttackHandling()
{
	static double timer=0;
	
	if(!( me.alive && cvar.aim && cvar.shoot && me.iClip )){ return; }

	if(cvar.burstflags & BurstInfo::BURSTFLAG_NOAIM) 
	{
		bAttacking = false;
		bFireCycle = true;
		nFireSequence = FIRESEQ_INACTIVE;
		return; 
	}

	if( !cvar.ftime ) { cvar.ftime = 0.1f; }
	if( cvar.burstflags&BurstInfo::BURSTFLAG_GROUND )
	{
		if(!(me.pmFlags&FL_ONGROUND)) // in mid air?
		{
			bAttacking = false;
			nFireSequence = FIRESEQ_INACTIVE; // delay shooting until landed
		}
	}

	if (gAimbot.target!=-1)
	{
		switch (nFireSequence)
		{
		case FIRESEQ_INACTIVE: 
			if( cvar.sdelay )
			{ 
				nFireSequence =  FIRESEQ_STARTDELAY; 
				timer = ClientTime::current; 
				gStopMovement();
				
			} else { 
				nFireSequence =  FIRESEQ_ACTIVE;     
				timer = ClientTime::current;
				bFireCycle  = true;
				bAttacking = true;
			}
			break;
		case FIRESEQ_STARTDELAY:
			if( ClientTime::current >= (timer+cvar.sdelay) )  
			{
				// start delay time's up, start firing
				nFireSequence =  FIRESEQ_ACTIVE;     
				timer = ClientTime::current;
				bFireCycle  = true;
				bAttacking = true;

			}
			break;
		case FIRESEQ_ACTIVE:  
			if( bFireCycle ) 
			{
				if( (ClientTime::current >= (timer+cvar.ftime)) && (cvar.dtime!=0)) 
				{
					bAttacking = false;
					timer = ClientTime::current;
					bFireCycle = false;
				}
				
			} else {    
				if( ClientTime::current >= (timer+cvar.dtime) )
				{
					bAttacking = true;
					timer = ClientTime::current;
					bFireCycle = true;
				}
			}
			break;
		case FIRESEQ_RELEASEDELAY:
			nFireSequence =  FIRESEQ_ACTIVE;     
			timer = ClientTime::current;
			bFireCycle  = true;
			bAttacking = true;
			break;
		}
		
	} else { 
		switch (nFireSequence)
		{
		case FIRESEQ_INACTIVE: 
			break;
		case FIRESEQ_STARTDELAY: 
			nFireSequence =  FIRESEQ_INACTIVE;     
			gFreeMovement();
			break;
		case FIRESEQ_ACTIVE:  
			gFreeMovement();
			if( !bFireCycle || (cvar.rdelay==0) )
			{                   
				bAttacking = false;
				nFireSequence =  FIRESEQ_INACTIVE;     
				
			} else {        
				timer = ClientTime::current;
				nFireSequence = FIRESEQ_RELEASEDELAY;
			}
			break;
		case FIRESEQ_RELEASEDELAY: 
			gFreeMovement();
			if( ClientTime::current >= (timer+cvar.rdelay) ) 
			{
				bAttacking = false;
				nFireSequence =  FIRESEQ_INACTIVE;     
			}
			break;
		}
	}
}

//========================================================================================
void UpdateScreenInfo(void)
{
	screeninfo.iSize=sizeof(SCREENINFO);
	gEngfuncs.pfnGetScreenInfo(&screeninfo);
	displayCenterX = screeninfo.iWidth/2;
	displayCenterY = screeninfo.iHeight/2;
}

int HUD_Redraw (float x, int y)
{
	int nResult=0;
	pHUD_Redraw(x,y);

	if(cvar.chase_cam) 
	{	
		cmd.exec("chase_active hide 1;r_drawviewmodel hide 0");
		cvar.chaseofs = -100;
	} else {
		cmd.exec("chase_active hide 0;r_drawviewmodel hide 1");
		cvar.chaseofs = 0.0;
	}
	
	me.ent = gEngfuncs.GetLocalPlayer();
	if (cvar.cross)       { drawCrosshair       (); }
	if (cvar.czoom)	      { handleContinuousZoom(); }
	hudDrawInfoTexts();
	if (cvar.radar)	      { drawRadarFrame      (); }
	if (menu_active)      { drawMenu            (); }
	if (cvar.soundfilter) { drawSound           (); }
	
	// === check for scheduled commands:
	for( ScheduleList::iterator pos = scheduleList.begin();
		 pos != scheduleList.end();
		 ++pos)
	{
		if( (*pos).timer.expired() )
		{
			cmd.exec( const_cast<char*>((*pos).cmd.c_str()) );
			scheduleList.erase(pos);
			break;
		}	
	}

	// get screen info
	static bool done=false;
	if(!done)
	{ 
		UpdateScreenInfo(); 
		done=true; 
	}

	// === dead/alive signals
	static bool previous_alive_state=false;
	if     (  previous_alive_state && !me.alive )  { sendScriptSignal("dead" );  }
	else if( !previous_alive_state &&  me.alive )  { sendScriptSignal("alive" ); }
	previous_alive_state = me.alive;

	// === blink 
	if(blinkTimer.expired())
	{
		blinkPhase = !blinkPhase;
		if(blinkPhase){ blinkTimer.countdown(0.2); }
		else          { blinkTimer.countdown(0.1); }
	}

	// === flash removal
	if (cvar.flash)
	{
		static screenfade_t sf = { 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0 }; 
		gEngfuncs.pfnSetScreenFade( &sf ); 
	}

	// item esp + reset
	if(cvar.esp||cvar.radar) for (int ab=0;ab<vEntity.size();ab++) drawEsp(ab);
	vEntity.clear();

	// player esp
	for (int ax=0;ax<vPlayers.size();ax++) if (vPlayers[ax].getPVS() ) drawPlayerEsp(ax);
	if(cvar.avdraw) gAimbot.draw();

	// winamp
	if(cvar.wa_active) winamp.frame();

	// console
	gConsole.draw(cvar.con_x,cvar.con_y,cvar.con_w,cvar.con_h);

	// == handle autoshoot
	gAttackHandling();

	return nResult;
} 

//========================================================================================
void ActivatePlayer(struct cl_entity_s *ent)
{
	int px = ent->index;

	if(cvar.alive_method==1)
	{
		if(ent->curstate.solid) { vPlayers[px].setAlive(); }
		
		if(ent==me.ent) { me.alive = vPlayers[px].getAlive(); }
	}
	else if(cvar.alive_method==2)
	{
		if(ent->curstate.solid) { vPlayers[px].setAlive(); }
		else                    { vPlayers[px].setAlive(false);}
		
		if(ent==me.ent) { me.alive = vPlayers[px].getAlive(); }
	}


	vPlayers[px].setPVS();
	gEngfuncs.pfnGetPlayerInfo(ent->index, &vPlayers[px].entinfo);

	if(ent == me.ent) 
	{ 
		vPlayers[px].distance = 100000.0;
		vPlayers[px].visible  = false;
		vPlayers[px].fovangle = 360;
		vPlayers[px].points   = -999999;
		vPlayers[px].clearPVS(); 
		return;
	}
	
	// filter trash entities, calc distance/visibility, points ...
	playerCalcExtraData(px,ent);
}

//========================================================================================
void AddEntity(struct cl_entity_s *ent)
{
	hostage_info dummy;
	dummy.ent = ent;
	if(vEntity.size()<64) vEntity.push_back(dummy);
}

//========================================================================================
inline void drawLine(float*from, float*to, ColorEntry*color)
{
	gEngfuncs.pEfxAPI->R_BeamPoints(from,to,beamindex,0.001f,0.9f,0.5,32,2,0,10,color->fr,color->fg,color->fb);
}

void HUD_TempEntUpdate (
		double frametime,   			// Simulation time
		double client_time, 			// Absolute time on client
		double cl_gravity,  			// True gravity on client
		TEMPENTITY **ppTempEntFree,   	// List of freed temporary ents
		TEMPENTITY **ppTempEntActive, 	// List 
		int		( *Callback_AddVisibleEntity )( cl_entity_t *pEntity ),
		void	( *Callback_TempEntPlaySound )( TEMPENTITY *pTemp, float damp ) )
{
	typedef void (*HUD_TempEntUpdateFunc)(double,double,double,TEMPENTITY**,TEMPENTITY**, int(*)(cl_entity_t*),void(*)(TEMPENTITY*,float));	

	(*(HUD_TempEntUpdateFunc)(pHUD_TempEntUpdate))(
		frametime,
		client_time,
		cl_gravity,
		ppTempEntFree,
		ppTempEntActive,
		Callback_AddVisibleEntity,
		Callback_TempEntPlaySound 
		);
		

	me.ent = gEngfuncs.GetLocalPlayer();
	if(cvar.light)
	{
		dlight_t *dl = gEngfuncs.pEfxAPI->CL_AllocDlight (0);
		VectorCopy (me.pmEyePos, dl->origin);
		dl->radius = cvar.light;
		dl->color.r = 255;
		dl->color.g = 255;
		dl->color.b = 255;
		dl->die = client_time + 0.01;
	}

	if(cvar.tlight && gAimbot.target!=-1 )
	{
		ColorEntry* color = colorList.get(29);
		dlight_t *dl = gEngfuncs.pEfxAPI->CL_AllocDlight (0);
		VectorCopy ( vPlayers[gAimbot.target].getEnt()->origin, dl->origin );
		dl->radius = cvar.tlight;
		dl->color.r = color->r;
		dl->color.g = color->g;
		dl->color.b = color->b;
		dl->die = client_time + 0.01;
	}

	// == provide time handler with timing info
	ClientTime::reportMapTime( client_time );

	// find beamindex if needed 
	if(!beamindex) { beamindex = gEngfuncs.pEventAPI->EV_FindModelIndex("sprites/laserbeam.spr"); }
    		
	if( cvar.death && deathInfos.active )
	{
		ColorEntry* color = colorList.get(29);
		gEngfuncs.pEfxAPI->R_BeamPoints(deathInfos.from,deathInfos.to,beamindex,0.001f,0.9f,0,32,2,0,0,color->fr,color->fg,color->fb);
	}

	if( cvar.frag && myKills.size() > 0 )
	{
		ColorEntry* color = colorList.get(49);
		for(int f=0;f<myKills.size();f++)
			gEngfuncs.pEfxAPI->R_BeamPoints(myKills[f].from,myKills[f].to,beamindex,0.001f,0.9f,0,32,2,0,0,color->fr,color->fg,color->fb);
	}
}

//========================================================================================
void PreV_CalcRefdef (struct ref_params_s *pparams)
{
	// recoil
	VectorCopy(pparams->punchangle,me.punchangle);
	if (cvar.recoil) for (int i=0; i<3; i++) pparams->punchangle[i] = 0; 	

	if( pparams->nextView == 0 )
	{
		if (cvar.spec_fix)
		{	
			gAimbot.findTarget();
		} else {
			gAimbot.target = -1;
		}
		
		if( gAimbot.target != -1 && cvar.autoburst && mod==MOD_CSTRIKE)
		{
			// == update burst settings
			int dist = vPlayers[gAimbot.target].distance/22;
			BurstInfo::RangeInfo* range = burstInfo.get(currentWeaponID,dist );

			// update burst variables
			cvar.ftime  = range->ftime;
			cvar.dtime  = range->dtime;
			cvar.sdelay = range->sdelay;
			cvar.burstflags = range->flags;
		} 


		if ( cvar.aim ) 
		if ( (me.iClip && me.pmMoveType!=5) || !me.alive ) // out of ammo / on ladder / dead
		if ( gAimbot.target!=-1 )
		if (! (cvar.burstflags&BurstInfo::BURSTFLAG_NOAIM) )
		if ( pparams->nextView == 0 )
		{
			Aimbot_active = true;
			gAimbot.CalcViewAngles();
			VectorCopy(gAimbot.aim_viewangles, pparams->cl_viewangles);
			VectorCopy(gAimbot.aim_viewangles, pparams->viewangles);
		} 

		// decrement PVS+reset
		for (int ax=0;ax<vPlayers.size();ax++) 
		{
			vPlayers[ax].decrPVS();
			vPlayers[ax].numTargetSpots = 0; // gets filled again in gAimbot 
		}
	}
}

//========================================================================================
void PostV_CalcRefdef (struct ref_params_s *pparams)
{
	if( pparams->nextView == 0 )
	{
		// Primary Screen, below all other Viewports Sent
		// update vectors for CalcScreen
		VectorCopy(pparams->viewangles,mainViewAngles);
		VectorCopy(pparams->vieworg,mainViewOrigin);

		if(cvar.chaseofs)
		{
			vec3_t vecEnd, up, right, forward, EntViewOrg;
			gEngfuncs.pfnAngleVectors (mainViewAngles, forward, right, up);
			mainViewOrigin[0] += cvar.chaseofs*forward[0];
			mainViewOrigin[1] += cvar.chaseofs*forward[1];
			mainViewOrigin[2] -= cvar.chaseofs*forward[2];
		}
	}
}

void V_CalcRefdef (struct ref_params_s *pparams)
{
	me.ent = gEngfuncs.GetLocalPlayer();

	PreV_CalcRefdef(pparams);
	
	if (pparams->nextView == 0)
	{
		pV_CalcRefdef(pparams);
	}

	PostV_CalcRefdef(pparams);
}

void opengl_ClearBackground();

void CL_CreateMove (float frametime, struct usercmd_s *usercmd, int active)
{
	me.ent = gEngfuncs.GetLocalPlayer();
	bNewFrame = true;

	// wireframe, asus wh, nosky
	if(cvar.glextra==3 || cvar.wall==2)  { opengl_ClearBackground(); }

	pCL_CreateMove(frametime,usercmd,active);

	if(bStopMovement)
	{
		if(usercmd->forwardmove==0 && usercmd->sidemove==0)
		{
			// user is already aware, no need to interfere further...
			bStopMovement = false;
		}
		usercmd->forwardmove = 0;
		usercmd->sidemove = 0;
	}
	else
	{

	}
	
	// aim
	if(Aimbot_active)
	{
			// auto aim
			VectorCopy              ( gAimbot.aim_viewangles, usercmd->viewangles );
			gEngfuncs.SetViewAngles ( gAimbot.aim_viewangles);
	}

	// handle autoaim/autoshoot
	if(Aimbot_active)
	{
			// auto shoot
			if (cvar.shoot && bAttacking)
			{
				if ( (cvar.burstflags & BurstInfo::BURSTFLAG_ZOOM) && !me.inZoomMode )
				{   
					usercmd->buttons |= IN_ATTACK2;
				} else{
					usercmd->buttons |= IN_ATTACK;
				}
			}
			
			// last use of Aimbot_viewangles, prevent further use
			Aimbot_active = false;
	}

	// autoswitch to pistol
	if (usercmd->buttons&IN_ATTACK && me.alive)
	{ 
		// note: there was a wbwg manual detection method, that worked this way:
		//    send the client "unbindall;+attack", while he's holding a pistol.
		//    then the admin has to see wether more than one shoot is fired.
		//    thus we check for mouse1 before activating rapid fire.
		bool mouse1pressed = GetAsyncKeyState(VK_LBUTTON);

		if(mouse1pressed)
		{
			// auto-weaponswitch
			static StopTimer nextSwitchWait;
			if( !me.iClip && playerItems.canSwitch() && nextSwitchWait.expired() && mouse1pressed )
			{ 
				nextSwitchWait.countdown(6);
				sendScriptSignal("switch");
			}
		
			// pistol rapid-fire
			if(cvar.pistol && currentWeaponID>=WEAPON_USP && currentWeaponID<=WEAPON_FIVE7 && mouse1pressed )
			{
				static bool flipFlop = true;
				if(flipFlop){ usercmd->buttons |= IN_ATTACK;    }
				else        { usercmd->buttons &= (~IN_ATTACK); }

				flipFlop = !flipFlop;
			}

			// auto-speed
			if(cvar.aspeed>1 && currentWeaponID!=WEAPON_C4)
			{
				cvar.speed = cvar.aspeed;
			}
			
			// auto-reload
			if (!me.iClip)
			{
				usercmd->buttons &= (~IN_ATTACK);
			}
		}
	} else {
		if(cvar.aspeed>1)
		{ 
			cvar.speed = 1.0; 
		}
	}

	if(me.alive)
	{
		// anti-recoil
		if( cvar.recoil )
		{
			usercmd->viewangles[0] -= (me.punchangle[0] * cvar.recoil);
			usercmd->viewangles[1] -= (me.punchangle[1] * cvar.recoil);
		}

		// bunny hop
		if ( cvar.jump  && (usercmd->buttons & IN_JUMP) && !(me.pmFlags & FL_ONGROUND)) usercmd->buttons &= ~IN_JUMP;
	    
		// duck jump
		if ( cvar.duck && !(me.pmFlags & FL_ONGROUND)  && me.pmVelocity[2]>0)  usercmd->buttons |= IN_DUCK; 
	}
}

void HUD_PlayerMove (struct playermove_s *a, int b)
{
	pHUD_PlayerMove(a,b);

	me.ent = gEngfuncs.GetLocalPlayer();

	me.pmFlags = a->flags;
	me.pmMoveType = a->movetype;
    VectorCopy(a->velocity,me.pmVelocity);
	
	// get origin
	gEngfuncs.pEventAPI->EV_LocalPlayerViewheight(me.pmEyePos);
	if(me.alive) me.pmEyePos[2] += cvar.height_correction;

	if(cvar.info==11)
	{
		// shows a discrepancy between mainViewOrigin and me.pmEyePos
		// needed to set cvar.height_correction.
		char tmp[111];
		sprintf(tmp,"(%4.2f,%4.2f,%4.2f) ang(%4.2f,%4.2f)",me.pmEyePos[0],me.pmEyePos[1],me.pmEyePos[2],me.viewAngles[0],me.viewAngles[1]);
		gSetHudMessage(tmp);
		sprintf(tmp,"(%4.2f,%4.2f,%4.2f) ang(%4.2f,%4.2f)",mainViewOrigin[0],mainViewOrigin[1],mainViewOrigin[2],mainViewAngles[0],mainViewAngles[1]);
		gSetHudMessage2(tmp);
	}

    me.pmEyePos[0]+= a->origin[0];
	me.pmEyePos[1]+= a->origin[1];
	me.pmEyePos[2]+= a->origin[2];

	// precalc yaw sin/cos
	float yaw = a->angles[1]*(M_PI/180.0);
	me.sin_yaw       =  sin(yaw); 
	me.minus_cos_yaw = -cos(yaw);
	me.viewAngles[0] = a->angles[0];
	me.viewAngles[1] = a->angles[1];
	me.viewAngles[2] = a->angles[2];
}

//========================================================================================
static void playerRenderOptions(cl_entity_s* ent)
{
	ColorEntry* color = PlayerColor(ent->index);

	if(cvar.glow)//&& aim)
	{
		ent->curstate.renderamt = cvar.glow;
		ent->curstate.rendercolor.r = color->r;
		ent->curstate.rendercolor.b = color->b;
		ent->curstate.rendercolor.g = color->g;
		ent->curstate.renderfx = kRenderFxGlowShell;
	}
	
	if (cvar.barrel)//&& aim)
	{
		vec3_t vecEnd, up, right, forward, EntViewOrg;
		VectorCopy(ent->origin,EntViewOrg);
		
		if (ent->curstate.usehull == 0)
			EntViewOrg[2] += 17;
		else if (ent->curstate.usehull == 1)
			EntViewOrg[2] += 12;
		
		
		gEngfuncs.pfnAngleVectors (ent->angles, forward, right, up);
		forward[2] = -forward[2];
		vec3_t vecBegin(EntViewOrg);
		
		vecBegin[0]+=forward[0]*22;
		vecBegin[1]+=forward[1]*22;
		vecBegin[2]+=forward[2]*22;
		
		vecEnd = vecBegin + forward * cvar.barrel;
		drawLine(vecBegin,vecEnd,color);
	}
	
	if (cvar.spiked)//&& aim)
	{
		vec3_t vecEnd, up, right, forward, EntViewOrg;
		VectorCopy(ent->origin,EntViewOrg);
		
		gEngfuncs.pfnAngleVectors (ent->angles, forward, right, up);
		forward[2] = -forward[2];
		vec3_t vecBegin(EntViewOrg);
		vecEnd   = vecBegin + forward * cvar.spiked;
		vecBegin = vecBegin - forward * cvar.spiked;
		drawLine(vecBegin,vecEnd,color);

		vecBegin = EntViewOrg;
		vecEnd   = vecBegin + right *cvar.spiked;
		vecBegin = vecBegin - right *cvar.spiked;
		drawLine(vecBegin,vecEnd,color);
		
		vecBegin = EntViewOrg;
		vecEnd   = vecBegin + up * cvar.spiked;
		vecBegin = vecBegin - up *cvar.spiked;
		drawLine(vecBegin,vecEnd,color);
	}
}

int HUD_AddEntity (int type, struct cl_entity_s *ent, const char *modelname)
{
	static int beamindex = 0;
	if(!beamindex) beamindex = gEngfuncs.pEventAPI->EV_FindModelIndex("sprites/laserbeam.spr");

	if(bNewFrame)
	{
		bNewFrame = false;

		if( cvar.death && deathInfos.active )
		{
			ColorEntry* color = colorList.get(29);
			gEngfuncs.pEfxAPI->R_BeamPoints(deathInfos.from,deathInfos.to,beamindex,0.001f,0.9f,0,32,2,0,0,color->fr,color->fg,color->fb);
		}
	}

	if (ent->player)
	{
		ActivatePlayer(ent);
		if(vPlayers[ent->index].getPVS()) 
		{
			ent->curstate.rendermode = 0;
			playerRenderOptions(ent);
		}
	}
	else if(
		strstr(modelname, "w_") || 
		strstr(modelname, "hostage") || 
		strstr(modelname, "scientist") ||
		strstr(modelname, "sentry")
		)
	{
		AddEntity(ent);

		if(strstr(modelname,"hegrenade"))
		{
			if(cvar.grenadeshell)
			{
				ColorEntry* color = colorList.get(37);
				ent->curstate.renderamt = cvar.grenadeshell;
				ent->curstate.rendercolor.r = color->r;
				ent->curstate.rendercolor.b = color->b;
				ent->curstate.rendercolor.g = color->g;
				ent->curstate.renderfx = kRenderFxGlowShell;
			}
		}
	}
	else if (type==ET_NORMAL && cvar.trans && ent->curstate.solid==SOLID_BSP)
	{
			ent->curstate.rendermode=2;
			ent->curstate.renderamt=cvar.trans;
	}
	
	int nResult = pHUD_AddEntity(type,ent,modelname);

	if (cvar.smoke && (strstr(modelname, "black_smoke")||strstr(modelname, "fast_wallpuff"))) return 0;
	return nResult;
}

//========================================================================================
void func_zoom(void)
{
	int zoom; 
	zoom = atoi(cmd.argC(1));
	BOUND_VALUE(zoom,3,90);

	iCurrentFOV = zoom;
	fCurrentFOV = zoom;
	(*SetFOVOrg)("SetFOV", 1, &zoom);
}

//========================================================================================
HSPRITE	SPR_Load(const char *szPicName)
{
	HSPRITE spr = gEngfuncs.pfnSPR_Load (szPicName);
	
	if(strstr(szPicName,"sniper_scope"))   sniper_scope = spr;
	else if(strstr(szPicName,"ch_sniper")) ch_sniper = spr;

    add_sprite (spr, szPicName);
	
	return spr;
}

//========================================================================================
bool passesSoundFilter(const float* const origin,const int team)
{
	if( GetPseudoDistance(origin,me.pmEyePos)<cvar.soundtol ){ return false; }

	switch(cvar.soundfilter)
	{
	case 1:
		for (int ax=0;ax<vPlayers.size();ax++)
		{
			if( vPlayers[ax].getPVS()  ) 
			{
				if( GetPseudoDistance(vPlayers[ax].getEnt()->origin, origin)<cvar.soundtol )
				{
					return false;
				}
			}
		}
		return true;
	
	case 2:
		for (int ax=0;ax<vPlayers.size();ax++)
		{
			if( vPlayers[ax].getPVS() && !isEnemy(ax) )
			{
				if( GetPseudoDistance(vPlayers[ax].getEnt()->origin, origin)<cvar.soundtol )
				{
					return false;
				}
			}
		}
		return true;
		return (team!=me.team);
	default:
		return true;
	}
}

//========================================================================================
inline void strcpy_x(char* dest, const char* pos)
{
	do{ 
		while( *pos>='0' && *pos<='9' )  ++pos; // ignore numbers
		
		if(*pos=='.')              {*dest=0;break; } // cut off ".wav"
		*dest=*pos;
		++dest;
		++pos; 
	}
	while(*pos);
}

//========================================================================================
inline void strcpy_x2(char* dest, const char* pos)
{
	do{ 
		if(*pos=='.'||*pos=='-'||*pos=='_') {*dest=0;break; } // cut off ".wav"
		*dest=*pos;
		++dest;
		++pos; 
	}
	while(*pos);
}

//========================================================================================
void PreS_DynamicSound(const DWORD a,const DWORD b,const DWORD c,const char*const sample,
						 const float*const origin,DWORD f,const DWORD g,const DWORD h)
{
	BOUND_VALUE(soundMarkerCurrentIndex,0,64);
	SoundMarker& ref = vecSoundMarker[soundMarkerCurrentIndex];

	string theMessage;

	if(b>0 && b<=36)
	{
		theMessage = vPlayers[b].entinfo.name;
		ref.team = vPlayers[b].team;
	}

	// setup color and description of sound marker depending on it's type
	// or drop it if it doesn't pass the filter
	if(sample[0]=='p' && sample[3]=='y' && sample[6]=='/')  // "player/pl_step1.wav"
	{
		if(!passesSoundFilter(origin,b)) return;
		const char* pos = sample+7;
		if(pos[0]=='p' && pos[2]=='_') // pl_xx
		{
			if(pos[3]=='s' && pos[4]=='h' && pos[5]=='e' && pos[6]=='l' )  return; // pl_shell#      
	
			pos+=3;
			if(cvar.colorstep)
				ref.color = colorList.get(41+ref.team);
			else
				ref.color = colorList.get(30);  // "snd_step"

			ref.priority = 0;
		} else if( pos[0]=='b'&& pos[1]=='h' && pos[2]=='i' ) { // "bhit_..."
			vPlayers[b].hitpoints-=getPoints(sample);
			if(vPlayers[b].hitpoints<=0)
				vPlayers[b].hitpoints=1;
			pos+=5;
			ref.color    = colorList.get(32); // "snd_hit"
			ref.priority = 2;
		} else if( pos[3]=='s' && pos[7]=='t' ) { // "pl_shot1.wav"
			vPlayers[b].hitpoints--;
			if(vPlayers[b].hitpoints<=0)
				vPlayers[b].hitpoints=1;
		}
		strcpy_x(ref.description,pos);
	}
	else
	if( sample[0]=='w' && sample[1]=='e' && sample[7]=='/' ) // weapons/ric1.wav
	{
		const char* pos = sample+8;
		if(   (pos[0]=='r' && pos[1]=='i')    // ricxxx
			||(pos[0]=='b' && pos[1]=='u') )  // bullet-hit...
		{
			return;
		}
		if(!passesSoundFilter(origin,b)) return;

		ref.priority = 1;
		ref.color    = colorList.get(41+ref.team);   // snd_weapon
		strcpy_x2(ref.description,pos);
		for(char *pos1=ref.description;*pos1;++pos1){ *pos1 = toupper(*pos1); }// uppercase
	}
	else 
	if(sample[0]=='a' && sample[1]=='m' && sample[8]=='/')  // "ambience/3dmbridge.wav"
	{
		if(!strcmp(sample+9,"3dmbridge.wav")) strcpy(ref.description,"bridge");
		ref.color = colorList.get(33); // snd_special
		ref.priority = 2;
	}
	else
	if(sample[0]=='p' && sample[3]=='t' && sample[5]=='/') // "plats/vehicle.."
	{
		strcpy(ref.description,"vehicle");
		ref.color = colorList.get(33); // snd_special
		ref.priority = 2;
	}
	else
	if(sample[0]=='h' && sample[1]=='o' && sample[2]=='s') // "hostage/hos1.."
	{
		strcpy(ref.description,"rescue");
		ref.color = colorList.get(33); // snd_special
		ref.priority = 1;
	}
	else
	{
		return;
	}

	if(!cvar.soundfilter) { return; }

	// activate sound marker
	VectorCopy( origin, ref.origin );
	ref.timer.countdown( cvar.soundtime );
	
	// advance to next sound marker
	++soundMarkerCurrentIndex;
	if(soundMarkerCurrentIndex>=cvar.soundmax){ soundMarkerCurrentIndex=0;}
}

//========================================================================================
void logCommand(char* cmd, int size, bool blocked)
{
    if(!commandLogFileOpened)
	{
        enum{ NUM_HISTORY_LOG_FILES=3 }; // allowed: 2..11
		char historyname[] = "logx.txt";

		// delete last
		historyname[3] = '0'+(NUM_HISTORY_LOG_FILES-1);
		DeleteFile( getOgcDirFile(historyname).c_str() );

		// shift files
		char fromfile[256];
		for( int i=(NUM_HISTORY_LOG_FILES-2);i>=0;--i )
		{
			// prepare old filename
			historyname[3] = '0'+i;
			strcpy(fromfile,getOgcDirFile(historyname).c_str());
			
			// prepare new filename
			++historyname[3];

			MoveFile(fromfile,getOgcDirFile(historyname).c_str() );
		}
		strcpy(fromfile,getOgcDirFile("log_last.txt").c_str() );
		MoveFile(fromfile,getOgcDirFile("log0.txt").c_str() );

		commandLogFile.open(fromfile,ios::out);
		commandLogFileOpened = true;
	}

	// set string terminator+log
	char lastChar = cmd[size];
	cmd[size] = 0;
	if(blocked) commandLogFile << "BLOCKED: "<<cmd << endl;
	else		commandLogFile << cmd << endl;
	cmd[size] = lastChar;
}

//========================================================================================
inline static void lowercase(char* str)
{
	while(*str){ *str = tolower(*str); ++str; }
}

//========================================================================================
void semicolonToZero(string& s)
{
	for(string::iterator pos = s.begin();pos!=s.end();pos++)
	{
		if(*pos==';') { *pos=0; }
	}
}

//========================================================================================
void func_alias()
{
	const char*   name     = cmd.argC(1);
	string&       content  = cmd.argS(2);
	cmd.AddAlias(name,content);
}

//========================================================================================
void func_bind()
{
	keyBindManager.addBind(cmd.argC(1),cmd.argC(2));
}

//========================================================================================
void func_savebinds()
{
	keyBindManager.save();
}

//========================================================================================
void func_unbind()
{
	keyBindManager.removeBind(cmd.argC(1));
}

//========================================================================================
void func_remap()
{
	keyBindManager.remapKey(cmd.argC(1),cmd.argC(2));
}

//========================================================================================
void func_enable_sniff()
{
	Con_Echo("&rsniffer not available");
}

//========================================================================================
void func_query()
{
	Con_Echo("&rsniffer not available");
}

//========================================================================================
void hookCommandParser( char* cmdString, int size )
{
	char* pos       = cmdString;
	int   charsLeft = size;

	bool passThroughBlock = false;
	if( *(DWORD*)pos == cmd.excludePrefixDword )
	{
		passThroughBlock = true;
		pos[0]=pos[1]=pos[2]=pos[3]=' ';
		charsLeft-=4;
		pos+=4;
	}

	// skip whitespace
	while(charsLeft>0 && (*pos==' '||*pos=='\t')){pos++;charsLeft--;}
	if(charsLeft<=0){ return; }

	// extract command
	char  command[32];
	char* commandPos = command;
	int   commandCharsLeft = 30;

	while(charsLeft>0 && *pos>' ' && commandCharsLeft>0)
	{
		*commandPos = tolower(*pos);
		commandPos++;
		pos++;
		charsLeft--;
		commandCharsLeft--;
	}
	*commandPos = 0;
	if(!*command) return;

	if( !passThroughBlock && blockCommand.find(command) )
	{
		if(cvar.log) logCommand(cmdString,size,true); 

		if(blockCommand.num==CMDBLOCK_ALL)
		{
			for(int i=0;i<size;i++){ cmdString[i]=' ';}
			sprintf( gHudMessage, "[OGC] execution of \"%s\" blocked.",command);
			gHudTimer.countdown(3);
			sendScriptSignal("blocked");
		}
		return;
	}

	if(cvar.log && strcmp(command,"m_pitch"))
	{ 
		logCommand(cmdString,size,false); 
	}

	if(!strcmp(command,"messagemode")||!strcmp(command,"messagemode2"))
	{
		keyBindManager.messageMode = true;
	}
}

//========================================================================================
void func_patch(void)
{
	return;
}

//========================================================================================
void CB_KeyManager_Exec(const string& commandlist)
{
	cmd.exec( const_cast<char*>( commandlist.c_str() ) );
}

//========================================================================================
void CB_KeyManager_ConType(char ch)
{
	gConsole.key(ch);
}

//========================================================================================
void ( *R_BloodSprite_Org )				( float * org, int colorindex, int modelIndex, int modelIndex2, float size );
void R_BloodSpriteHook ( float * org, int colorindex, int modelIndex, int modelIndex2, float size )
{
	(*R_BloodSprite_Org)(org,colorindex,modelIndex,modelIndex2,size);

	if(cvar.blood)
	{
		float org2[3];
		for(int i=0;i<cvar.blood;i++)
		{
			org2[0]=org[0]+ float(rand()%20);
			org2[1]=org[1]+ float(rand()%20);
			org2[2]=org[2]+ float(rand()%40);
			(*R_BloodSprite_Org)(org2,colorindex,modelIndex,modelIndex2,size);
		}
	}
}

//========================================================================================
void gSetHudMessage2(const char* message)
{
	strcpy(gHudMessage2,message);
	gHudTimer2.countdown(4);
}

//========================================================================================
void gSetHudMessage(const char* message)
{
	strcpy(gHudMessage,message);
	gHudTimer.countdown(4);
}

//========================================================================================
void AtRoundStart(void)
{
	gAimbot.target = -1;
	cvar.spec_fix = true;
	deathInfos.active = false;
	playerItems.clearWeaponSlots(); // prepare for new curweapon messages

	if(me.team>0 && cvar.alive_method==1 )
	{
		me.alive = true;
	}

	myKills.clear();
	
	gEngfuncs.pfnGetPlayerInfo(me.ent->index, &vPlayers[me.ent->index].entinfo);

	vPlayers[0].entinfo.name = "Worldspawn";
	vPlayers[0].team = 0;
	vPlayers[0].setAlive(false);
	for(int ax=0;ax<vPlayers.size();ax++)
	{
		vPlayers[ax].hitpoints=MAX_HITPOINTS;
		if(vPlayers[ax].team)
			vPlayers[ax].health = 100;
		else
			vPlayers[ax].health = 0;
	}
	beamindex = gEngfuncs.pEventAPI->EV_FindModelIndex("sprites/laserbeam.spr");
	
	sendScriptSignal("round");
}

//========================================================================================
void AtMapChange()
{
	iRounds=0;
    
	bBombIsPlanted=false;
    fBombPlantedTime=0;
    iC4Timer=0;

	gAimbot.target = -1;
	myKills.clear();

	playerItems.clearWeaponSlots();
	gHudTimer.setExpired();
	gHudTimer2.setExpired();
}

//========================================================================================
void HlEngineCommand(const char* command)
{
	if(!gEngfuncs.pfnClientCmd) { return; }
	gEngfuncs.pfnClientCmd( const_cast<char*>(command) );
}

//========================================================================================
void HandleCvarInt(char* name, int* value)
{
	char* arg1 = cmd.argC(1); 
	if (!strcmp(arg1,"change")) 
	{
		if (*value) *value=0; else *value=1; 
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s changed to %d", name, *value );gHudTimer.countdown(3);}
		return;
	}
	if (!strcmp(arg1,"up"))     
	{
		*value += cmd.argI(2);
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s increased to %d", name, *value );gHudTimer.countdown(3);}
		return;
	}
    if (!strcmp(arg1,"down"))   
	{ 
		*value -= cmd.argI(2);
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s decreased to %d", name, *value );gHudTimer.countdown(3);}
		return;
	}
	if (!strcmp(arg1,"hide")) 
    { 
        *value = cmd.argI(2);
        return;
    }
	if (!*arg1) 
	{ 
		Con_Echo( "CVAR &w%s&a = %i\n",name,*value); 
		return;
	}
	*value = cmd.argI(1);
	if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s: %d", name, *value );gHudTimer.countdown(3);}
}

//========================================================================================
void HandleCvarFloat(char* name, float* value)
{
	char* arg1 = cmd.argC(1);  
	if (!strcmp(arg1,"change")) 
	{
		if (*value) *value=0; else *value=1; 
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s changed to %f", name, *value );gHudTimer.countdown(3); }
		return;
	}
	if (!strcmp(arg1,"up"))     
	{ 
		*value += cmd.argF(2); 
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s increased to %f", name, *value );gHudTimer.countdown(3);}
		return;
	}
    if (!strcmp(arg1,"down"))   
	{ 
		*value -= cmd.argF(2); 
		if(cvar.info==3||menu_active){ sprintf(gHudMessage,"%s decreased to %f", name, *value );gHudTimer.countdown(3);}
		return;
	}
	if (!strcmp(arg1,"hide")) 
    { 
        *value = cmd.argF(2);
        return;
    }
	if (!*arg1) 
	{ 
		Con_Echo( "CVAR &w%s&r = %f\n",name,*value); 
		return;
	}
    *value = cmd.argF(1);
	if(cvar.info==3||menu_active){sprintf(gHudMessage,"%s: %f", name, *value );gHudTimer.countdown(3);}
}

//========================================================================================
bool isHlCvar(char* name)
{
	if(!gEngfuncs.pfnGetCvarPointer) { return false; }
	
	cvar_s* test = gEngfuncs.pfnGetCvarPointer(name);
	return (test!=NULL);
}

//========================================================================================
void HandleHlCvar(char* name)
{
	if(!gEngfuncs.pfnGetCvarPointer) { return; }

	cvar_s* ptr = gEngfuncs.pfnGetCvarPointer(name);
	if(!ptr) return;

	HandleCvarFloat(name,&ptr->value);
}

//===================== COMMAND FUNCTIONS ================================================
void func_menu_back()
{
	if(!menu_active || !curMenu) return;
	curMenu = curMenu->parent;

	if(!curMenu) 
	{ 
		curMenu = ogcMenu.baseMenu; 
		menu_active=false; 
		gHudTimer.countdown(0.1);
		sendScriptSignal("menuoff");
	}
    	
	curMenu->boundSelection();
	menu_describe_current();
}

//========================================================================================
void func_menu_up()
{
	if(!menu_active || !curMenu) return;
	--curMenu->selection;
	curMenu->boundSelection();
	
	menu_describe_current();
}


//========================================================================================
void func_menu_off()
{
	menu_active=false;
	gHudTimer.countdown(0.1);
	sendScriptSignal("menuoff");
}

//========================================================================================
void func_menu_down()
{
	if(!menu_active || !curMenu) return;
	++curMenu->selection;
	curMenu->boundSelection();
	
	menu_describe_current();
}

//========================================================================================
void func_menu_select()
{
	if(!curMenu) 
	{
		curMenu = ogcMenu.baseMenu;
		menu_active = true;
		menu_describe_current();
		return;
	}

	if(menu_active)
	{
		OgcCommandMenu::MenuEntry& entry = curMenu->items[curMenu->selection];
		if(entry.menu)
		{
			curMenu = entry.menu;
			curMenu->boundSelection();
			menu_describe_current();
		}
		else
		{
			cmd.exec(const_cast<char*>(entry.content.c_str()));
		}
	}

	menu_active=true;
}

//========================================================================================
void func_echo()
{
	string str;
	cmd.collectArguments(str);
	Con_Echo ("%s\n",str.c_str());
}

//========================================================================================
void func_text()
{
	string s;
	cmd.collectArguments(s);
	gSetHudMessage2( s.c_str() );
}

//========================================================================================
void func_avadd(void)
{
	aimvec av;
	int duck = cmd.argI(1);

	int arg = 2;
	while(*cmd.argC(arg))
	{
		av.h = cmd.argF(arg++);
		av.f = cmd.argF(arg++);
		av.r = cmd.argF(arg++);
		if (duck)  gAimbot.AimVecsD.push_back(av);
		else       gAimbot.AimVecsS.push_back(av);
	}
}

//========================================================================================
void func_avclear(void)
{
	gAimbot.AimVecsD.clear();
	gAimbot.AimVecsS.clear();
}

//========================================================================================
void func_avlist(void)
{
	int dmmy = cmd.argI(1);
	if (dmmy)
	{
	   Con_Echo("Listing AimVecs(1) Ducking\n");
	   for (vector<aimvec>::iterator si = gAimbot.AimVecsD.begin(); si != gAimbot.AimVecsD.end();++si)
	   {
	    Con_Echo("Height: &w%f&a Forward: &w%f&a Right: &w%f&a\n",si->h,si->f,si->r);
	   }
	}
	else 
	{
	   Con_Echo("Listing AimVecs(0) Standing\n");
	   for (vector<aimvec>::iterator si = gAimbot.AimVecsS.begin(); si != gAimbot.AimVecsS.end();++si)
	   {
	    Con_Echo("Height: &w%f&a Forward: &w%f&a Right: &w%f&a\n",si->h,si->f,si->r);
	   }
	}
}

//========================================================================================
void func_lamer( void )
{
	gConsole.echo("&rdisabled");
}

//========================================================================================
void func_savecvar()
{
	string filepath = getOgcDirFile(cmd.argC(1));
	ofstream ofs( filepath.c_str(), ios::binary );
	if( !ofs ){ Con_Echo( "&rfile: &w%s &rsave failed.\n",filepath.c_str() ); return; }
	
	ofs.write( (char*)&cvar, sizeof(cvar) );
	ofs.close();
}

//========================================================================================
void func_loadcvar()
{
	string filepath = getOgcDirFile(cmd.argC(1));
	ifstream ifs( filepath.c_str(), ios::binary );
	if( !ifs ){ Con_Echo( "&rfile: &w%s &rload failed.\n",filepath.c_str() ); return; }
	
	ifs.read( (char*)&cvar, sizeof(cvar) );
	ifs.close();
}

//========================================================================================
void func_saveburst()
{
	string filepath = getOgcDirFile(cmd.argC(1));
	bool status = burstInfo.savecfg(filepath.c_str(), "" );
	if(status) { Con_Echo("file: &w%s&a saved.", filepath.c_str()); }
	else       { Con_Echo("&rfile: &w%s&r save failed.",filepath.c_str()); }
}

//========================================================================================
void func_setburst()
{
	if(!strcmp(cmd.argC(1),"copy"))
	{ 
		bool status = burstInfo.copy(cmd.argC(2),cmd.argC(3)); 
		if(!status) { Con_Echo( "&rcopy failed.\n"); }
		return;
	}
	
	char* weaponString = cmd.argC(1);
	int weapon = burstInfo.getWeaponID( weaponString );
	if( weapon==0 ){ Con_Echo( "&w%s&a no such weapon.\n",weaponString); return; }
	
	if( !*cmd.argC(2) ) // just weapon given, then enlist
	{    
		Con_Echo( "Listing for weapon: &w%s\n", weaponString);
		for(int i=0;i<5;i++)
		{
			char* desc = burstInfo.description(weapon,i);
			Con_Echo( " &w%d&a: %s",i+1,desc);
		}
		return;
	}
	
	int index=0, range=0, flags=0;
	float ftime=1,dtime=0,sdelay=0.05f;
	index  = cmd.argI(2)-1;
	range  = cmd.argI(3);
	ftime  = cmd.argF(4);
	dtime  = cmd.argF(5);
	sdelay = cmd.argF(6);
	flags  = cmd.argI(7);

	if(index>5||index<0) { index = 0; }
	BurstInfo::RangeInfo r;
	r.range  = range;
	r.ftime  = ftime;
	r.dtime  = dtime;
	r.sdelay = sdelay;
	r.flags  = flags;

	burstInfo.set( weapon,&r, index );
}

//========================================================================================
void func_soundmax()
{
	HandleCvarInt  ("displayed sounds", &cvar.soundmax);
	BOUND_VALUE  (cvar.soundmax,0,64);
	for( int i=0;i<cvar.soundmax;i++)
	{
		vecSoundMarker[i].timer.setExpired();
	}
}

//========================================================================================
void func_catchexec()
{
	sprintf(gHudMessage, "[OGC] You are lagging on a server protected by Disolusion Anti-Cheat.");
	gHudTimer.countdown(8);
}

//========================================================================================
void func_later()
{
	if(cmd.argS(1).empty())
	{
		list<ScheduledCommand>::iterator pos;
		for(pos=scheduleList.begin();pos!=scheduleList.end();++pos)
		{
			Con_Echo("%6.2f sec: \"%s\"", (*pos).timer.timeleft(), (*pos).cmd.c_str() );
		}

		return;
	}

	if(cmd.argS(1)=="clear")
	{
		scheduleList.erase(scheduleList.begin(),scheduleList.end());
		return;
	}

	float time = cmd.argF(1);

	ScheduledCommand tmp;
	cmd.collectArguments( tmp.cmd, 2); 
	tmp.timer.countdown(time);
	if(scheduleList.size()<32) scheduleList.push_back(tmp);
}

//========================================================================================
void func_block()
{
	const char* arg1 = cmd.argC(1);
	const char* arg2 = cmd.argC(2);
	if( !strcmp(arg1,"cmd" ) )
	{
		blockCommand.add(arg2,CMDBLOCK_ALL);
	} 
	else if( !strcmp(arg1,"relpath" ) )
	{
		blockCommand.add(arg2,CMDBLOCK_RELPATH);
		Con_Echo("command: &w%s&a added (relative paths only).\n",arg2);
	}
	else if( !strcmp(arg1,"clear" ) )
	{
		blockCommand.clear();
		Con_Echo("block list cleared.\n");
	}
	else if( !strcmp(arg1,"list" ) )
	{
		for(blockCommand.it_start();
		    blockCommand.it_running();
			blockCommand.it_next())
			{
				if(blockCommand.num==CMDBLOCK_ALL)
					Con_Echo("blocking Command: %s\n",blockCommand.str );
				else
					Con_Echo("blocking Command: %s (relative paths only)\n",blockCommand.str);
			}
	} else {
		Con_Echo("usage: $block <cmd|relpath|clear|list> <command_to_block>\n");
	}
}

//========================================================================================
void func_color()
{
	int arg=1;
	while(*cmd.argC(arg)){
		colorList.set( cmd.argC(arg  ), cmd.argC(arg+1), 
			 cmd.argC(arg+2),  cmd.argC(arg+3),  cmd.argC(arg+4));
		arg+=5;
	}
}

//========================================================================================
void func_init()
{
	static int antirecursion=false;
	if(antirecursion) return;
	antirecursion=true;

	char* arg1 = cmd.argC(1);
	static vector<string> initCommands;
	static StringFinder   id;

	// remember commands
	if(!strcmp("save",arg1))
	{
		ofstream ofs( getOgcDirFile("init.cfg").c_str() );
		
		ofs<<"// this file is generated automatically. do not edit it.\n";
		for(id.it_start();id.it_running();id.it_next())
		{
			if(!initCommands[id.num].empty())
			{
				ofs << initCommands[id.num] 
				    <<"; init \""<<id.str<<"\" \""<<initCommands[id.num]<< "\" \x0D\x0A";
			}
		}
		ofs.close();
	
	} else if (!strcmp("list",arg1)) {
		for(int i=0;i<initCommands.size();i++) Con_Echo("&w#%d&a: %s\n",i,initCommands[i].c_str());
	
	} else if (!strcmp("clear", arg1)) {
		for(int i=0;i<initCommands.size();i++) initCommands[i].erase();
		Con_Echo("cleared");
	
	} else if( *arg1 ) {
		if( id.find(arg1) ) cmd.collectArguments(initCommands[id.num],2);
		else
		{
			if(initCommands.size()<256)
			{
				int index = initCommands.size();
				initCommands.push_back("");
				cmd.collectArguments(initCommands[index],2);
				id.add(arg1,index);
			}
		}
	}
	
	antirecursion=false;
}

//========================================================================================
bool func_eval_condition(int& refArgIndex)
{
	char* test  = cmd.argC(refArgIndex++);
	char* szArg = cmd.argC(refArgIndex++);

	if(!*test||!*szArg){ return false; }

	if( !strcmp(test,"has"   )) { return  playerItems.hasWeapon(szArg);}
	if( !strcmp(test,"hasno" )) { return !playerItems.hasWeapon(szArg);}
	if( !strcmp(test,"is"    ))  
	{ 
		if(!strcmp(szArg,"t"    )) { return me.team==1; }
		if(!strcmp(szArg,"ct"   )) { return me.team!=1; }
	    if(!strcmp(szArg,"alive")) { return (me.alive); }
	}

	if(!strcmp(test,"money<" )){ return (playerItems.money<atoi(szArg)); }
	if(!strcmp(test,"money>="))
	{ 
		bool condition = (playerItems.money>=atoi(szArg)); 
		if(!condition){ strcpy(gHudMessage,"[OGC] Not enough money.");gHudTimer.countdown(1.5); }
		return condition;
	}
	if( !strcmp(test,"armor<" ) ){ return (playerItems.armor< atoi(szArg));}
	if( !strcmp(test,"armor>=") ){ return (playerItems.armor>=atoi(szArg));}
	return false;
}

//========================================================================================
void func_if()
{
	int  pos = 1;
	bool final_condition = func_eval_condition(pos);
	while(1){ 
		if( *cmd.argC(pos)=='&' ) {
			final_condition &= func_eval_condition(pos);
		} else if( *cmd.argC(pos)=='|' ) {
			final_condition |= func_eval_condition(pos);
		} else {
			break;
		} 
	}

	int truepos = pos;
	int elsepos = 0;
	while(1){
		char* arg = cmd.argC(pos);
		if(!*arg) { break; }
		if(!strcmp(arg,"else")) { elsepos = pos; break; }
		pos++;
	}

	string command;
	if( !elsepos )
	{
		if(final_condition)
		{
			cmd.collectArguments(command,truepos);
			cmd.exec( const_cast<char*>( command.c_str() ) );
		}
	} else {
		if(final_condition)
		{
			cmd.collectArguments(command,truepos,elsepos-1);
			cmd.exec( const_cast<char*>( command.c_str() ) );
		} else {
			cmd.collectArguments(command,elsepos+1);
			cmd.exec( const_cast<char*>( command.c_str() ) );
		}
	}
}

//========================================================================================
void func_quit()     { ExitProcess(0); }
void func_exec()     { ogc_exec( cmd.argC(1) ); }
void func_mdl()      { gAimbot.command(); }
void func_colortag() { gConsole.setcolortag(cmd.argC(1)[0],cmd.argI(2),cmd.argI(3),cmd.argI(4)); }
void func_console_toggle() { gConsole.active = !gConsole.active; }

//========================================================================================
void func_turn()
{
	double degrees = cmd.argF(1);
	mainViewAngles[1] += degrees;
	if( mainViewAngles[1]>360.0 ) mainViewAngles[1] -= 360.0;
	gEngfuncs.SetViewAngles(mainViewAngles);

}

//========================================================================================
void func_entinfo()
{
	int index = cmd.argI(1);
	Con_Echo("info:" );
	Con_Echo("name=%s pvs=%d alive=%d team=%d", vPlayers[index].entinfo.name, vPlayers[index].getPVS(), vPlayers[index].getAlive(),vPlayers[index].team);
	Con_Echo("solid: %d me: alive=%d team=%d",vPlayers[index].getEnt()->curstate.solid,me.alive,me.team);
	
}

//========================================================================================
void func_points()
{
	if(cmd.argS(1)=="list")
	{
		int i=0;
		while(1){ 
			float pts   = pointSystem[i].points;
			char* desc  = pointSystem[i].description;
			if(!*desc) break;
			if(pts>0) gConsole.echo("&w#%d&a: %s = &g%f",i,desc,pts);
			else      gConsole.echo("&w#%d&a: %s = &r%f",i,desc,pts);
			i++;
		}
		return;
	}

	if(!( cmd.argC(1)[0]>='0' && cmd.argC(1)[0]<='9' && 
		  cmd.argC(2)[0]>='0' && cmd.argC(2)[0]<='9' ))
	{
		gConsole.echo("syntax: points <&wnumber&a> <&wvalue&a>");
		return;
	}

	int idx  = cmd.argI(1);
	int size = sizeof(pointSystem)/sizeof(pointSystem[0]);
	if(idx<0 || idx>=size)
	{
		gConsole.echo("&rnumber out of bounds.");
		return;
	}
    
	pointSystem[idx].points = cmd.argF(2);
}

//========================================================================================
void func_play(){ sndPlaySound(getOgcDirFile(cmd.argC(1)).c_str(), SND_ASYNC); }

//========================================================================================
void func_winamp() { winamp.command(cmd.argS(1),cmd.argS(2),cmd.argS(3),cmd.argS(4)); }

//========================================================================================
void collectArgumentsX(string& dest,int from=1, int to=1000000)
{
	while(from<=to){
		char* arg = gEngfuncs.Cmd_Argv(from++);
		if(!*arg) { break;}
		dest += '\"';
		dest += arg ;
		dest += '\"';
		if(from!=to)dest += " ";
	};
}

//========================================================================================
void interface_func()
{
	string ogc_command = gEngfuncs.Cmd_Argv(1);
	ogc_command += " ";
	collectArgumentsX(ogc_command,2);
	cmd.exec( ogc_command.c_str() );
}

//========================================================================================
void func_interface() 
{
	if(cmd.argS(1).empty()) return;

	gEngfuncs.pfnAddCommand( strdup(cmd.argC(1)), interface_func );
}

//========================================================================================
void func_press()
{
}

//========================================================================================
void func_release()
{
}

//========================================================================================
void Init_Command_Interpreter()
{
	cmd.init();

	// register & init all cvars
	cvar.init();

	// additional cvars
	cmd.AddCvarInt( "bone",  &gAimbot.my_curSelectedBone ); 
	cmd.AddCvarInt( "alive", (int*)&me.alive );
	cmd.AddCvarInt( "target", (int*)&gAimbot.target );

	// register all commands
	#define REGISTER_COMMAND(c) cmd.AddCommand( #c, func_##c);
	REGISTER_COMMAND(zoom       )  REGISTER_COMMAND(avclear    )  
	REGISTER_COMMAND(avadd      )  REGISTER_COMMAND(avlist     )  REGISTER_COMMAND(lamer      )  
	REGISTER_COMMAND(loadcvar   )  REGISTER_COMMAND(savecvar   )  REGISTER_COMMAND(saveburst  )  
	REGISTER_COMMAND(setburst   )  REGISTER_COMMAND(echo       )  REGISTER_COMMAND(text       )  
	REGISTER_COMMAND(later      )  REGISTER_COMMAND(block      )  REGISTER_COMMAND(color      )  
	REGISTER_COMMAND(init       )  REGISTER_COMMAND(if         )  REGISTER_COMMAND(quit       )  
	REGISTER_COMMAND(alias      )  REGISTER_COMMAND(bind       )  REGISTER_COMMAND(unbind     )  
	REGISTER_COMMAND(remap      )  REGISTER_COMMAND(exec       )                                 
	REGISTER_COMMAND(menu_up    )  REGISTER_COMMAND(menu_down  )  
	REGISTER_COMMAND(menu_select)  REGISTER_COMMAND(menu_back  )  REGISTER_COMMAND(console_toggle)  
	REGISTER_COMMAND(enable_sniff) REGISTER_COMMAND(query      )  REGISTER_COMMAND(colortag   )
    REGISTER_COMMAND(soundmax   )  REGISTER_COMMAND(turn       )
	REGISTER_COMMAND(mdl        )  REGISTER_COMMAND(entinfo    )  REGISTER_COMMAND(points     )  
	REGISTER_COMMAND(play       )  REGISTER_COMMAND(winamp     )  REGISTER_COMMAND(savebinds  )
	REGISTER_COMMAND(menu_off   )  REGISTER_COMMAND(interface  )

	cmd.AddCommand("txt"     , func_text);   // for compatibility reasons only
}

#pragma warning (disable:4800)

//========================================================================================
int Initialize (cl_enginefunc_t *pEnginefuncs, int iVersion)
{
	assert(pEnginefuncs);

	// save engine funcs, setup hooks
	memcpy (&gEngfuncs,       pEnginefuncs, sizeof(cl_enginefunc_t));
	memcpy (&gHookedEngfuncs, pEnginefuncs, sizeof(cl_enginefunc_t));

	gHookedEngfuncs.pfnSPR_Set          = SPR_Set;
	gHookedEngfuncs.pfnSPR_DrawHoles    = SPR_DrawHoles;
	gHookedEngfuncs.pfnSPR_DrawAdditive = SPR_DrawAdditive;
	gHookedEngfuncs.pfnSPR_Draw         = SPR_Draw;
	gHookedEngfuncs.pfnSPR_Load         = SPR_Load;
	gHookedEngfuncs.pfnDrawCharacter    = DrawCharacter;
	gHookedEngfuncs.pfnFillRGBA         = FillRGBA;
	gHookedEngfuncs.pfnHookUserMsg      = HookUserMsg;

	// con_visible...
	keyBindManager.con_visible = gEngfuncs.Con_IsVisible;

	// determine mod
	mod = MOD_UNKNOWN;
	const char* gamedir = gEngfuncs.pfnGetGameDirectory();
	const char* tespos = gamedir + strlen(gamedir) - 10;
	if(strstr(tespos,"cstrike")) mod=MOD_CSTRIKE;
	if(strstr(tespos,"dod"))     mod=MOD_DOD;

	int nResult = pInitialize(&gHookedEngfuncs,iVersion);

	static bool firstInitialize = true;
	if(firstInitialize)
	{
		firstInitialize = false;
		// randomize timer
		srand((unsigned)time(NULL));

		keyBindManager.init();
		keyBindManager.CallBack_Execute = CB_KeyManager_Exec;
		keyBindManager.CallBack_ConType = CB_KeyManager_ConType;
		keyBindManager.con_visible = gEngfuncs.Con_IsVisible;

		gConsole.setcolortag( 'b', 128,128,255);
		gConsole.setcolortag( 'r', 255,128,128);
		gConsole.setcolortag( 'g',  80,222,140);
		gConsole.setcolortag( 'w', 255,255,255);
		gConsole.setcolortag( 'y', 255,255, 50);

		gConsole.echo("&b*** &w[U]NITED [G]AMING [C]HEATS &b***");
		gConsole.echo("&b*** &y http://ugc.cscheats.net     &b***");
		gConsole.echo("");

		cleanupWindowsTempFiles();

		ogcMenu.init(getOgcDirFile("commandmenu.txt").c_str());

		Init_Command_Interpreter();

		// execute configs
		ogc_exec( "startup" );
		ogc_exec( "main" );

		if(mod!=MOD_CSTRIKE) { cvar.alive_method = 1; }

		pEnginefuncs->pfnConsolePrint("-] [U]NITED [G]AMING [C]HEATS [-\n");
		pEnginefuncs->pfnConsolePrint("-]  http://ugc.cscheats.net     [-\n");
		cmd.exec("play sounds\\activated.wav");
		cmd.exec("wall 0");
		cvar.spec_fix = false;
		detour_S_DynamicSound();
		soundPatch.apply();
	}
	return nResult;
}

void CAM_Think( void )
{
	pCAM_Think( );
}

void CL_CameraOffset( float *ofs )
{
	pCL_CameraOffset( ofs );
}

int CL_IsThirdPerson( void )
{
	int retval = NULL;

	retval = pCL_IsThirdPerson( );
	return( retval );
}

void Demo_ReadBuffer( int size, unsigned char *buffer )
{
	pDemo_ReadBuffer( size, buffer );
}

int HUD_ConnectionlessPacket( struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size )
{
	int retval = NULL;

	retval = pHUD_ConnectionlessPacket( net_from, args, response_buffer, response_buffer_size );
	return( retval );
}

void HUD_CreateEntities( void )
{
	pHUD_CreateEntities( );
}

int HUD_DirectorEvent( unsigned char command, unsigned int firstObject, unsigned int secondObject, unsigned int flags )
{
	int retval = NULL;

	retval = pHUD_DirectorEvent( command, firstObject, secondObject, flags );
	return( retval );
}

void HUD_DrawNormalTriangles( void )
{
	pHUD_DrawNormalTriangles( );
}

void HUD_DrawTransparentTriangles( void )
{
	pHUD_DrawTransparentTriangles( );
}

void HUD_Frame( double time )
{
	pHUD_Frame( time );
}

int HUD_GetHullBounds( int hullnumber, float *mins, float *maxs )
{
	int retval = NULL;

	retval = pHUD_GetHullBounds( hullnumber, mins, maxs );
	return( retval );
}

struct cl_entity_s *HUD_GetUserEntity( int index )
{
	struct cl_entity_s *retval = NULL;

	retval = pHUD_GetUserEntity( index );
	return( retval );
}

int HUD_Init( void )
{
	int retval = NULL;

	retval = pHUD_Init( );

	return( retval );
}

int HUD_Key_Event( int eventcode, int keynum, const char *pszCurrentBinding )
{
	int retval = NULL;
 
	if (keynum >= 239 && keynum <= 244)
	{
		switch (keynum) {
			case 239:
				keyBindManager.notifyMouseEvent("mwheeldown", (eventcode == 1));
				break;
			case 240:
				keyBindManager.notifyMouseEvent("mwheelup", (eventcode ==1));
				break;
			case 241:
				keyBindManager.notifyMouseEvent("mouse1", (eventcode == 1));
				break;
			case 242:
				keyBindManager.notifyMouseEvent("mouse2", (eventcode == 1));
				break;
			case 243:
				keyBindManager.notifyMouseEvent("mouse3", (eventcode == 1));
				break;
			case 244:
				keyBindManager.notifyMouseEvent("mouse4", (eventcode == 1));
				break;
		}
	}
	else
		// don't pass event code straight in, for some reason it b0rks
		if (eventcode)
			keyBindManager.notifyKeyEvent(keynum, true, false);
		else
			keyBindManager.notifyKeyEvent(keynum, false, false);

	if (!keyBindManager.keyBlocked(keynum, eventcode))
		retval = pHUD_Key_Event( eventcode, keynum, pszCurrentBinding );
	else
		retval = 0;
	if( gMenuActive() && (keynum >= 239 && keynum <= 244))
		retval = 0;
	return( retval );
}

void HUD_PlayerMoveInit( struct playermove_s *ppmove )
{
	pHUD_PlayerMoveInit( ppmove );
}

char HUD_PlayerMoveTexture( char *name )
{
	char retval = NULL;

	retval = pHUD_PlayerMoveTexture( name );
	return( retval );
}

void HUD_PostRunCmd( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed )
{
	pHUD_PostRunCmd( from, to, cmd, runfuncs, time, random_seed );
}

void HUD_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src )
{
	pHUD_ProcessPlayerState( dst, src );
}

int HUD_Reset( void )
{
	int retval = NULL;

	retval = pHUD_Reset( );
	return( retval );
}

void HUD_Shutdown( void )
{
	pHUD_Shutdown( );
}

void HUD_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity )
{
	pHUD_StudioEvent( event, entity );
}

void HUD_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client )
{
	pHUD_TxferLocalOverrides( state, client );
}

void HUD_TxferPredictionData( struct entity_state_s *ps, const struct entity_state_s *pps, struct clientdata_s *pcd, const struct clientdata_s *ppcd, struct weapon_data_s *wd, const struct weapon_data_s *pwd )
{
	pHUD_TxferPredictionData( ps, pps, pcd, ppcd, wd, pwd );
}

int HUD_UpdateClientData( client_data_t *cdata, float flTime )
{
	int retval = NULL;

	retval = pHUD_UpdateClientData( cdata, flTime );
	return( retval );
}

int HUD_VidInit( void )
{
	int retval = NULL;

	retval = pHUD_VidInit( );
	return( retval );
}

int HUD_VoiceStatus( int entindex, qboolean bTalking )
{
	int retval = NULL;

	retval = pHUD_VoiceStatus( entindex, bTalking );
	return( retval );
}

void IN_Accumulate( void )
{
	pIN_Accumulate( );
}

void IN_ActivateMouse( void )
{
	pIN_ActivateMouse( );
}

void IN_ClearStates( void )
{
	pIN_ClearStates( );
}

void IN_DeactivateMouse( void )
{
	pIN_DeactivateMouse( );
}

void IN_MouseEvent( int mstate )
{
	pIN_MouseEvent( mstate );
}

struct kbutton_s *KB_Find( const char *name )
{
	struct kbutton_s *retval = NULL;

	retval = pKB_Find( name );
	return( retval );
}

void HUD_DirectorMessage(unsigned char command, unsigned int firstObject, unsigned int secondObject, unsigned int flags)
{
	pHUD_DirectorEvent(command, firstObject, secondObject, flags);
}

void CheckClientHook(FARPROC* pProc,LPCTSTR lpProcName)
{
	if (!strcmp(lpProcName,"Initialize")) 
	{
		pInitialize = (fInitialize)*pProc;
		*pProc = (FARPROC)Initialize;
	} 
	else if (!strcmp(lpProcName, "HUD_Init"))
	{
		pHUD_Init = (fHUD_Init)*pProc;
		*pProc = (FARPROC)HUD_Init;
	}
	else if (!strcmp(lpProcName, "HUD_VidInit"))
	{
		pHUD_VidInit = (fHUD_VidInit)*pProc;
		*pProc = (FARPROC)HUD_VidInit;
	}
	else if (!strcmp(lpProcName, "HUD_Redraw"))
	{
		pHUD_Redraw = (fHUD_Redraw)*pProc;
		*pProc = (FARPROC)HUD_Redraw;
	}
	else if (!strcmp(lpProcName, "HUD_UpdateClientData"))
	{
		pHUD_UpdateClientData = (fHUD_UpdateClientData)*pProc;
		*pProc = (FARPROC)HUD_UpdateClientData;
	}
	else if (!strcmp(lpProcName, "HUD_Reset"))
	{
		pHUD_Reset = (fHUD_Reset)*pProc;
		*pProc = (FARPROC)HUD_Reset;
	}
	else if (!strcmp(lpProcName, "HUD_PlayerMove"))
	{
		pHUD_PlayerMove = (fHUD_PlayerMove)*pProc;
		*pProc = (FARPROC)HUD_PlayerMove;
	}
	else if (!strcmp(lpProcName, "HUD_PlayerMoveInit"))
	{
		pHUD_PlayerMoveInit = (fHUD_PlayerMoveInit)*pProc;
		*pProc = (FARPROC)HUD_PlayerMoveInit;
	}
	else if (!strcmp(lpProcName, "HUD_PlayerMoveTexture"))
	{
		pHUD_PlayerMoveTexture = (fHUD_PlayerMoveTexture)*pProc;
		*pProc = (FARPROC)HUD_PlayerMoveTexture;
	}
	else if (!strcmp(lpProcName, "IN_ActivateMouse"))
	{
		pIN_ActivateMouse = (fIN_ActivateMouse)*pProc;
		*pProc = (FARPROC)IN_ActivateMouse;
	}
	else if (!strcmp(lpProcName, "IN_DeactivateMouse"))
	{
		pIN_DeactivateMouse = (fIN_DeactivateMouse)*pProc;
		*pProc = (FARPROC)IN_DeactivateMouse;
	}
	else if (!strcmp(lpProcName, "IN_MouseEvent"))
	{
		pIN_MouseEvent = (fIN_MouseEvent)*pProc;
		*pProc = (FARPROC)IN_MouseEvent;
	}
	else if (!strcmp(lpProcName, "IN_ClearStates"))
	{
		pIN_ClearStates = (fIN_ClearStates)*pProc;
		*pProc = (FARPROC)IN_ClearStates;
	}
	else if (!strcmp(lpProcName, "IN_Accumulate"))
	{
		pIN_Accumulate = (fIN_Accumulate)*pProc;
		*pProc = (FARPROC)IN_Accumulate;
	}
	else if (!strcmp(lpProcName, "CL_CreateMove"))
	{
		pCL_CreateMove = (fCL_CreateMove)*pProc;
		*pProc = (FARPROC)CL_CreateMove;
	}
	else if (!strcmp(lpProcName, "CL_IsThirdPerson"))
	{
		pCL_IsThirdPerson = (fCL_IsThirdPerson)*pProc;
		*pProc = (FARPROC)CL_IsThirdPerson;
	}
	else if (!strcmp(lpProcName, "CL_CameraOffset"))
	{
		pCL_CameraOffset = (fCL_CameraOffset)*pProc;
		*pProc = (FARPROC)CL_CameraOffset;
	}
	else if (!strcmp(lpProcName, "CAM_Think"))
	{
		pCAM_Think = (fCAM_Think)*pProc;
		*pProc = (FARPROC)CAM_Think;
	}
	else if (!strcmp(lpProcName, "KB_Find"))
	{
		pKB_Find = (fKB_Find)*pProc;
		*pProc = (FARPROC)KB_Find;
	}
	else if (!strcmp(lpProcName, "V_CalcRefdef"))
	{
		pV_CalcRefdef = (fV_CalcRefdef)*pProc;
		*pProc = (FARPROC)V_CalcRefdef;
	}
	else if (!strcmp(lpProcName, "HUD_AddEntity"))
	{
		pHUD_AddEntity = (fHUD_AddEntity)*pProc;
		*pProc = (FARPROC)HUD_AddEntity;
	}
	else if (!strcmp(lpProcName, "HUD_CreateEntities"))
	{
		pHUD_CreateEntities = (fHUD_CreateEntities)*pProc;
		*pProc = (FARPROC)HUD_CreateEntities;
	}
	else if (!strcmp(lpProcName, "HUD_DrawNormalTriangles"))
	{
		pHUD_DrawNormalTriangles = (fHUD_DrawNormalTriangles)*pProc;
		*pProc = (FARPROC)HUD_DrawNormalTriangles;
	}
	else if (!strcmp(lpProcName, "HUD_DrawTransparentTriangles"))
	{
		pHUD_DrawTransparentTriangles = (fHUD_DrawTransparentTriangles)*pProc;
		*pProc = (FARPROC)HUD_DrawTransparentTriangles;
	}
	else if (!strcmp(lpProcName, "HUD_StudioEvent"))
	{
		pHUD_StudioEvent = (fHUD_StudioEvent)*pProc;
		*pProc = (FARPROC)HUD_StudioEvent;
	}
	else if (!strcmp(lpProcName, "HUD_Shutdown"))
	{
		pHUD_Shutdown = (fHUD_Shutdown)*pProc;
		*pProc = (FARPROC)HUD_Shutdown;
	}
	else if (!strcmp(lpProcName, "HUD_TxferLocalOverrides"))
	{
		pHUD_TxferLocalOverrides = (fHUD_TxferLocalOverrides)*pProc;
		*pProc = (FARPROC)HUD_TxferLocalOverrides;
	}
	else if (!strcmp(lpProcName, "HUD_ProcessPlayerState"))
	{
		pHUD_ProcessPlayerState = (fHUD_ProcessPlayerState)*pProc;
		*pProc = (FARPROC)HUD_ProcessPlayerState;
	}
	else if (!strcmp(lpProcName, "HUD_TxferPredictionData"))
	{
		pHUD_TxferPredictionData = (fHUD_TxferPredictionData)*pProc;
		*pProc = (FARPROC)HUD_TxferPredictionData;
	}
	else if (!strcmp(lpProcName, "Demo_ReadBuffer"))
	{
		pDemo_ReadBuffer = (fDemo_ReadBuffer)*pProc;
		*pProc = (FARPROC)Demo_ReadBuffer;
	}
	else if (!strcmp(lpProcName, "HUD_ConnectionlessPacket"))
	{
		pHUD_ConnectionlessPacket = (fHUD_ConnectionlessPacket)*pProc;
		*pProc = (FARPROC)HUD_ConnectionlessPacket;
	}
	else if (!strcmp(lpProcName, "HUD_GetHullBounds"))
	{
		pHUD_GetHullBounds = (fHUD_GetHullBounds)*pProc;
		*pProc = (FARPROC)HUD_GetHullBounds;
	}
	else if (!strcmp(lpProcName, "HUD_Frame"))
	{
		pHUD_Frame = (fHUD_Frame)*pProc;
		*pProc = (FARPROC)HUD_Frame;
	}
	else if (!strcmp(lpProcName, "HUD_Key_Event"))
	{
		pHUD_Key_Event = (fHUD_Key_Event)*pProc;
		*pProc = (FARPROC)HUD_Key_Event;
	}
	else if (!strcmp(lpProcName, "HUD_PostRunCmd"))
	{
		pHUD_PostRunCmd = (fHUD_PostRunCmd)*pProc;
		*pProc = (FARPROC)HUD_PostRunCmd;
	}
	else if (!strcmp(lpProcName, "HUD_TempEntUpdate"))
	{
		pHUD_TempEntUpdate = (fHUD_TempEntUpdate)*pProc;
		*pProc = (FARPROC)HUD_TempEntUpdate;
	}
	else if (!strcmp(lpProcName, "HUD_GetUserEntity"))
	{
		pHUD_GetUserEntity = (fHUD_GetUserEntity)*pProc;
		*pProc = (FARPROC)HUD_GetUserEntity;
	}
	else if (!strcmp(lpProcName, "HUD_VoiceStatus"))
	{
		pHUD_VoiceStatus = (fHUD_VoiceStatus)*pProc;
		*pProc = (FARPROC)HUD_VoiceStatus;
	}
	else if (!strcmp(lpProcName, "HUD_DirectorMessage"))
	{
		pHUD_DirectorMessage = (fHUD_DirectorMessage)*pProc;
		*pProc = (FARPROC)HUD_DirectorMessage;
	}
	else if (!strcmp(lpProcName, "HUD_GetStudioModelInterface"))
	{
		pHUD_GetStudioModelInterface = (fHUD_GetStudioModelInterface)*pProc;
		*pProc = (FARPROC)HUD_GetStudioModelInterface;
	}
}

void PatchClientFunc( DWORD *ClientFunc_ADDR )
{
	*( DWORD * )&pInitialize = *ClientFunc_ADDR;
	*ClientFunc_ADDR = ( DWORD )&Initialize;

	*( ( DWORD * )&pHUD_Init ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x04 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x04 ) ) = ( DWORD )&HUD_Init;

	*( ( DWORD * )&pHUD_VidInit ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x08 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x08 ) ) = ( DWORD )&HUD_VidInit;

	*( ( DWORD * )&pHUD_Redraw ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x0C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x0C ) ) = ( DWORD )&HUD_Redraw;

	*( ( DWORD * )&pHUD_UpdateClientData ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x10 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x10 ) ) = ( DWORD )&HUD_UpdateClientData;

	*( ( DWORD * )&pHUD_Reset ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x14 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x14 ) ) = ( DWORD )&HUD_Reset;

	*( ( DWORD * )&pHUD_PlayerMove ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x18 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x18 ) ) = ( DWORD )&HUD_PlayerMove;

	*( ( DWORD * )&pHUD_PlayerMoveInit ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x1C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x1C ) ) = ( DWORD )&HUD_PlayerMoveInit;

	*( ( DWORD * )&pHUD_PlayerMoveTexture ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x20 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x20 ) ) = ( DWORD )&HUD_PlayerMoveTexture;

	*( ( DWORD * )&pIN_ActivateMouse ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x24 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x24 ) ) = ( DWORD )&IN_ActivateMouse;

	*( ( DWORD * )&pIN_DeactivateMouse ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x28 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x28 ) ) = ( DWORD )&IN_DeactivateMouse;

	*( ( DWORD * )&pIN_MouseEvent ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x2C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x2C ) ) = ( DWORD )&IN_MouseEvent;

	*( ( DWORD * )&pIN_ClearStates ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x30 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x30 ) ) = ( DWORD )&IN_ClearStates;

	*( ( DWORD * )&pIN_Accumulate ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x34 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x34 ) ) = ( DWORD )&IN_Accumulate;

	*( ( DWORD * )&pCL_CreateMove ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x38 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x38 ) ) = ( DWORD )&CL_CreateMove;

	*( ( DWORD * )&pCL_IsThirdPerson ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x3C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x3C ) ) = ( DWORD )&CL_IsThirdPerson;

	*( ( DWORD * )&pCL_CameraOffset ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x40 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x40 ) ) = ( DWORD )&CL_CameraOffset;

	*( ( DWORD * )&pKB_Find ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x44 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x44 ) ) = ( DWORD )&KB_Find;

	*( ( DWORD * )&pCAM_Think ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x48 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x48 ) ) = ( DWORD )&CAM_Think;

	*( ( DWORD * )&pV_CalcRefdef ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x4C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x4C ) ) = ( DWORD )&V_CalcRefdef;

	*( ( DWORD * )&pHUD_AddEntity ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x50 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x50 ) ) = ( DWORD )&HUD_AddEntity;

	*( ( DWORD * )&pHUD_CreateEntities ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x54 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x54 ) ) = ( DWORD )&HUD_CreateEntities;

	*( ( DWORD * )&pHUD_DrawNormalTriangles ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x58 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x58 ) ) = ( DWORD )&HUD_DrawNormalTriangles;

	*( ( DWORD * )&pHUD_DrawTransparentTriangles ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x5C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x5C ) ) = ( DWORD )&HUD_DrawTransparentTriangles;

	*( ( DWORD * )&pHUD_StudioEvent ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x60 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x60 ) ) = ( DWORD )&HUD_StudioEvent;

	*( ( DWORD * )&pHUD_PostRunCmd ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x64 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x64 ) ) = ( DWORD )&HUD_PostRunCmd;

	*( ( DWORD * )&pHUD_Shutdown ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x68 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x68 ) ) = ( DWORD )&HUD_Shutdown;

	*( ( DWORD * )&pHUD_TxferLocalOverrides ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x6C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x6C ) ) = ( DWORD )&HUD_TxferLocalOverrides;

	*( ( DWORD * )&pHUD_ProcessPlayerState ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x70 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x70 ) ) = ( DWORD )&HUD_ProcessPlayerState;

	*( ( DWORD * )&pHUD_TxferPredictionData ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x74 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x74 ) ) = ( DWORD )&HUD_TxferPredictionData;

	*( ( DWORD * )&pDemo_ReadBuffer ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x78 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x78 ) ) = ( DWORD )&Demo_ReadBuffer;

	*( ( DWORD * )&pHUD_ConnectionlessPacket ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x7C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x7C ) ) = ( DWORD )&HUD_ConnectionlessPacket;

	*( ( DWORD * )&pHUD_GetHullBounds ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x80 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x80 ) ) = ( DWORD )&HUD_GetHullBounds;

	*( ( DWORD * )&pHUD_Frame ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x84 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x84 ) ) = ( DWORD )&HUD_Frame;

	*( ( DWORD * )&pHUD_Key_Event ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x88 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x88 ) ) = ( DWORD )&HUD_Key_Event;

	*( ( DWORD * )&pHUD_TempEntUpdate ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x8C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x8C ) ) = ( DWORD )&HUD_TempEntUpdate;

	*( ( DWORD * )&pHUD_GetUserEntity ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x90 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x90 ) ) = ( DWORD )&HUD_GetUserEntity;

	*( ( DWORD * )&pHUD_VoiceStatus ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x94 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x94 ) ) = ( DWORD )&HUD_VoiceStatus;

	*( ( DWORD * )&pHUD_DirectorMessage ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x98 ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x98 ) ) = ( DWORD )&HUD_DirectorMessage;

	*( ( DWORD * )&pHUD_GetStudioModelInterface ) = *( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x9C ) );
	*( ( DWORD * )( *( ( DWORD * )&ClientFunc_ADDR ) + 0x9C ) ) = ( DWORD )&HUD_GetStudioModelInterface;	
}
