/*
   Copyright (C) 2002-2003 Victor Luchits

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "cg_local.h"

cg_static_t cgs;
cg_state_t cg;

centity_t cg_entities[MAX_EDICTS];

cvar_t *cg_predict;
cvar_t *cg_showMiss;

cvar_t *model;
cvar_t *skin;
cvar_t *hand;

cvar_t *cg_addDecals;

cvar_t *cg_footSteps;

cvar_t *cg_gun;
cvar_t *cg_timedemo;

cvar_t *cg_thirdPerson;
cvar_t *cg_thirdPersonAngle;
cvar_t *cg_thirdPersonRange;

//splitmodels
cvar_t *cg_weaponFlashes;
cvar_t *cg_ejectBrass; //eject brass-debris
cvar_t *cg_gunx;
cvar_t *cg_guny;
cvar_t *cg_gunz;
cvar_t *cg_debugPlayerModels;
cvar_t *cg_debugWeaponModels;
cvar_t *cg_gunbob;

cvar_t *developer;

// wsw : jal
cvar_t *cg_handOffset;
cvar_t *cg_gun_fov;
cvar_t *cg_volume_players;
cvar_t *cg_volume_effects;
cvar_t *cg_volume_announcer;
cvar_t *cg_volume_voicechats;
cvar_t *cg_grenadeTrail;
cvar_t *cg_rocketTrail;
cvar_t *cg_rocketFireTrail;
cvar_t *cg_bloodTrail;
cvar_t *cg_showBloodTrail;
cvar_t *cg_grenadeTrailAlpha;
cvar_t *cg_rocketTrailAlpha;
cvar_t *cg_bloodTrailAlpha;
cvar_t *cg_explosionsRing;
cvar_t *cg_gibs;
cvar_t *cg_outlineModels;
cvar_t *cg_outlineWorld;
cvar_t *cg_outlineItemsBlack;
cvar_t *cg_outlinePlayersBlack;
cvar_t *cg_drawEntityBoxes;
cvar_t *cg_fov;
cvar_t *cg_oldMovement;
cvar_t *cg_zoomSens;
cvar_t *cg_predictLaserBeam;
cvar_t *cg_voiceChats;
#ifdef CGAMEGETLIGHTORIGIN
cvar_t *cg_shadows;
#endif
cvar_t *cg_showSelfShadow;
cvar_t *cg_showPlayerTrails;
cvar_t *cg_playerTrailsColor;
cvar_t *cg_laserBeamSubdivisions;
cvar_t *cg_raceGhosts;
cvar_t *cg_raceGhostsAlpha;
cvar_t *cg_chatBeep;
cvar_t *cg_chatFilter;
cvar_t *cg_chatFilterTV;

// wsw : blx
cvar_t *cg_cartoonRockets;
cvar_t *cg_dashEffect;
cvar_t *cg_fallEffect;

// wsw : pb
cvar_t *cg_volume_hitsound;
cvar_t *cg_autoaction_demo;
cvar_t *cg_autoaction_screenshot;
cvar_t *cg_autoaction_stats;
cvar_t *cg_autoaction_spectator;
cvar_t *cg_simpleItems;
cvar_t *cg_simpleItemsSize;
cvar_t *cg_showObituaries;     // wsw : pb : where are obituaries printed
cvar_t *cg_particles;
cvar_t *cg_showhelp;
cvar_t *cg_scoreboardStats;
cvar_t *cg_showClamp;

// wsw : mdr
cvar_t *cg_damage_kick;
cvar_t *cg_damage_blend;
cvar_t *cg_pickup_flash;

cvar_t *cg_weaponAutoswitch;

// force models
cvar_t *cg_teamPLAYERSmodel;
cvar_t *cg_teamALPHAmodel;
cvar_t *cg_teamBETAmodel;
cvar_t *cg_teamGAMMAmodel;
cvar_t *cg_teamDELTAmodel;

cvar_t *cg_teamPLAYERSskin;
cvar_t *cg_teamALPHAskin;
cvar_t *cg_teamBETAskin;
cvar_t *cg_teamGAMMAskin;
cvar_t *cg_teamDELTAskin;

cvar_t *cg_teamPLAYERScolor;
cvar_t *cg_teamALPHAcolor;
cvar_t *cg_teamBETAcolor;
cvar_t *cg_teamGAMMAcolor;
cvar_t *cg_teamDELTAcolor;

cvar_t *cg_forceMyTeamAlpha;
cvar_t *cg_forceTeamPlayersTeamBeta;
cvar_t *cg_teamColoredBeams;
//cvar_t			*cg_teamColorBeamMinimum;

cvar_t *cg_ebbeam_width;
cvar_t *cg_ebbeam_alpha;
cvar_t *cg_ebbeam_time;
cvar_t *cg_instabeam_width;
cvar_t *cg_instabeam_alpha;
cvar_t *cg_instabeam_time;

//============
//CG_API
//============
int CG_API( void )
{
	return CGAME_API_VERSION;
}

//============
//CG_Error
//============
void CG_Error( const char *format, ... )
{
	va_list	argptr;
	char msg[1024];

	va_start( argptr, format );
	Q_vsnprintfz( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	trap_Error( msg );
}

//============
//CG_Printf
//============
void CG_Printf( const char *format, ... )
{
	va_list	argptr;
	char msg[1024];

	va_start( argptr, format );
	Q_vsnprintfz( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	trap_Print( msg );
}

//============
//CG_GS_Malloc - Used only for gameshared linking
//============
static void *CG_GS_Malloc( size_t size )
{
	return CG_Malloc( size );
}

//============
//CG_GS_Free - Used only for gameshared linking
//============
static void CG_GS_Free( void *data )
{
	CG_Free( data );
}

#ifndef WSW_RELEASE
static int CG_GS_FS_FOpenFile( const char *filename, int *filenum, int mode )
{
	return trap_FS_FOpenFile( filename, filenum, mode );
}

static int CG_GS_FS_Read( void *buffer, size_t len, int file )
{
	return trap_FS_Read( buffer, len, file );
}

static int CG_GS_FS_Write( const void *buffer, size_t len, int file )
{
	return trap_FS_Write( buffer, len, file );
}

static void CG_GS_FS_FCloseFile( int file )
{
	trap_FS_FCloseFile( file );
}
#endif

//============
//CG_InitGameShared
//give gameshared access to some utilities
//============
static void CG_InitGameShared( void )
{
	gs_module = GS_MODULE_CGAME;
	GS_PredictedEvent = CG_PredictedEvent;
	GS_Error = CG_Error;
	GS_Printf = CG_Printf;
	GS_Malloc = CG_GS_Malloc;
	GS_Free = CG_GS_Free;
	GS_Trace = CG_Trace;
	GS_PointContents = CG_PointContents;
	GS_PMoveTouchTriggers = CG_Predict_TouchTriggers;
#ifndef WSW_RELEASE
	GS_FS_FOpenFile = CG_GS_FS_FOpenFile;
	GS_FS_Read = CG_GS_FS_Read;
	GS_FS_Write = CG_GS_FS_Write;
	GS_FS_FCloseFile = CG_GS_FS_FCloseFile;
#endif
	GS_InitWeapons();
}

/*
   =================
   CG_CopyString
   =================
 */
char *_CG_CopyString( const char *in, const char *filename, int fileline )
{
	char *out;

	out = trap_MemAlloc( strlen( in ) + 1, filename, fileline );
	strcpy( out, in );
	return out;
}

//=================
//CG_RegisterWeaponModels
//=================
static void CG_RegisterWeaponModels( void )
{
	int i;

	for( i = 0; i < cgs.numWeaponModels; i++ )
	{
		cgs.weaponInfos[i] = CG_RegisterWeaponModel( cgs.weaponModels[i], i );
	}

	//special case for weapon 0. Must always load the animation script
	if( !cgs.weaponInfos[0] )
		cgs.weaponInfos[0] = CG_CreateWeaponZeroModel( cgs.weaponModels[0] );
}

/*
   =================
   CG_RegisterModels
   =================
 */
static void CG_RegisterModels( void )
{
	int i;
	char *name;

	name = cgs.configStrings[CS_MODELS+1];
	if( name[0] )
	{
		trap_R_RegisterWorldModel( name );
		CG_LoadingString( name );
	}

	CG_LoadingString( "models" );

	cgs.numWeaponModels = 1;
	Q_strncpyz( cgs.weaponModels[0], "generic/generic.md3", sizeof( cgs.weaponModels[0] ) );

	for( i = 1; i < MAX_MODELS; i++ )
	{
		name = cgs.configStrings[CS_MODELS+i];
		if( !name[0] )
			break;
		if( name[0] == '#' )
		{               // special player weapon model
			if( cgs.numWeaponModels < WEAP_TOTAL )
			{
				Q_strncpyz( cgs.weaponModels[cgs.numWeaponModels], name+1, sizeof( cgs.weaponModels[cgs.numWeaponModels] ) );
				cgs.numWeaponModels++;
			}
			//splitmodels [start]
		}
		else if( name[0] == '$' )
		{                       // indexed pmodel
			cgs.pModelsIndex[i] = CG_RegisterPlayerModel( name+1 );
			//[end]
		}
		else
		{
			CG_LoadingFilename( name );
			cgs.modelDraw[i] = CG_RegisterModel( name ); // skelmod
		}
	}

	for( i = 1; i < trap_CM_NumInlineModels(); i++ )
		cgs.inlineModelDraw[i] = CG_RegisterModel( va( "*%i", i ) ); // skelmod (not needed, but let's keep a style)

	CG_RegisterMediaModels();
	CG_RegisterBasePModel(); // never before registering the weapon models
	CG_RegisterWeaponModels();

	// precache forcemodels if defined
	CG_RegisterForceModels();

	// create a tag to offset the weapon models when seen in the world as items
	VectorSet( cgs.weaponItemTag.origin, 0, 0, 0 );
	Matrix_Copy( axis_identity, cgs.weaponItemTag.axis );
	VectorMA( cgs.weaponItemTag.origin, -14, cgs.weaponItemTag.axis[0], cgs.weaponItemTag.origin );
}

/*
   =================
   CG_RegisterSounds
   =================
 */
static void CG_RegisterSounds( void )
{
	int i;
	char *name;

	CG_LoadingString( "sounds" );

	for( i = 1; i < MAX_SOUNDS; i++ )
	{
		name = cgs.configStrings[CS_SOUNDS+i];

		if( !name[0] )
			break;
		if( name[0] != '*' )
		{
			CG_LoadingFilename( name );
			cgs.soundPrecache[i] = trap_S_RegisterSound( name );
		}
	}

	CG_RegisterMediaSounds();
}

/*
   =================
   CG_RegisterShaders
   =================
 */
static void CG_RegisterShaders( void )
{
	int i;
	char *name;

	CG_LoadingString( "images" );

	for( i = 1; i < MAX_IMAGES; i++ )
	{
		name = cgs.configStrings[CS_IMAGES+i];
		if( !name[0] )
			break;

		CG_LoadingFilename( name );
		cgs.imagePrecache[i] = trap_R_RegisterPic( name );
	}

	CG_RegisterMediaShaders();
}

/*
   =================
   CG_RegisterSkinfiles
   =================
 */
static void CG_RegisterSkinFiles( void )
{
	int i;
	char *name;

	CG_LoadingString( "skins" );

	for( i = 1; i < MAX_SKINFILES; i++ )
	{
		name = cgs.configStrings[CS_SKINFILES+i];
		if( !name[0] )
			break;

		CG_LoadingFilename( name );
		cgs.skinPrecache[i] = trap_R_RegisterSkinFile( name );
	}
}

/*
   =================
   CG_RegisterClients
   =================
 */
static void CG_RegisterClients( void )
{
	int i;
	char *name;

	CG_LoadingFilename( "" );

	for( i = 0; i < MAX_CLIENTS; i++ )
	{
		name = cgs.configStrings[CS_PLAYERINFOS+i];
		if( !name[0] )
			continue;

		CG_LoadingString( va( "client %i", i ) );
		CG_LoadClientInfo( &cgs.clientInfo[i], name, i );
	}
}

/*
   =================
   CG_RegisterLightStyles
   =================
 */
static void CG_RegisterLightStyles( void )
{
	int i;
	char *name;

	CG_LoadingFilename( "" );

	for( i = 0; i < MAX_LIGHTSTYLES; i++ )
	{
		name = cgs.configStrings[CS_LIGHTS+i];
		if( !name[0] )
			continue;
		CG_SetLightStyle( i );
	}
}

/*
   =================
   CG_RegisterVariables
   =================
 */
static void CG_RegisterVariables( void )
{
	cg_predict =	    trap_Cvar_Get( "cg_predict", "1", 0 );
	cg_showMiss =	    trap_Cvar_Get( "cg_showMiss", "0", 0 );
	cg_timedemo =	    trap_Cvar_Get( "timedemo", "0", CVAR_CHEAT );

	cg_debugPlayerModels =	trap_Cvar_Get( "cg_debugPlayerModels", "0", CVAR_CHEAT|CVAR_ARCHIVE );
	cg_debugWeaponModels =	trap_Cvar_Get( "cg_debugWeaponModels", "0", CVAR_CHEAT|CVAR_ARCHIVE );

	model =		    trap_Cvar_Get( "model", DEFAULT_PLAYERMODEL, CVAR_USERINFO | CVAR_ARCHIVE );
	skin =		    trap_Cvar_Get( "skin", DEFAULT_PLAYERSKIN, CVAR_USERINFO | CVAR_ARCHIVE );
	hand =		    trap_Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE );
	cg_oldMovement =	trap_Cvar_Get( "cg_oldMovement", "0", CVAR_USERINFO | CVAR_ARCHIVE );
	cg_fov =	    trap_Cvar_Get( "fov", "100", CVAR_USERINFO | CVAR_ARCHIVE );
	cg_zoomSens =	    trap_Cvar_Get( "zoomsens", "0", CVAR_ARCHIVE );

	cg_addDecals =	    trap_Cvar_Get( "cg_decals", "1", CVAR_ARCHIVE );
	cg_footSteps =	    trap_Cvar_Get( "cg_footSteps", "1", 0 );

	cg_thirdPerson =	trap_Cvar_Get( "cg_thirdPerson", "0", CVAR_CHEAT );
	cg_thirdPersonAngle =	trap_Cvar_Get( "cg_thirdPersonAngle", "0", 0 );
	cg_thirdPersonRange =	trap_Cvar_Get( "cg_thirdPersonRange", "40", 0 );

	//skelmod
	cg_gun =		trap_Cvar_Get( "cg_gun", "1", CVAR_ARCHIVE );
	cg_gunx =		trap_Cvar_Get( "cg_gunx", "0", CVAR_ARCHIVE );
	cg_guny =		trap_Cvar_Get( "cg_guny", "0", CVAR_ARCHIVE );
	cg_gunz =		trap_Cvar_Get( "cg_gunz", "0", CVAR_ARCHIVE );
	cg_gunbob =		trap_Cvar_Get( "cg_gunbob", "1", CVAR_ARCHIVE );
	cg_gun_fov =		trap_Cvar_Get( "cg_gun_fov", "90", CVAR_ARCHIVE );
	cg_weaponFlashes =	trap_Cvar_Get( "cg_weaponFlashes", "2", CVAR_ARCHIVE );
	cg_ejectBrass =		trap_Cvar_Get( "cg_ejectBrass", "0", CVAR_DEVELOPER );

	// wsw
	cg_volume_players =	trap_Cvar_Get( "cg_volume_players", "1.0", CVAR_ARCHIVE );
	cg_volume_effects =	trap_Cvar_Get( "cg_volume_effects", "1.0", CVAR_ARCHIVE );
	cg_volume_announcer =	trap_Cvar_Get( "cg_volume_announcer", "1.0", CVAR_ARCHIVE );
	cg_volume_hitsound =	trap_Cvar_Get( "cg_volume_hitsound", "1.0", CVAR_ARCHIVE );
	cg_volume_voicechats =	trap_Cvar_Get( "cg_volume_voicechats", "1.0", CVAR_ARCHIVE );
	cg_handOffset =		trap_Cvar_Get( "cg_handOffset", "5", CVAR_ARCHIVE );
	cg_rocketTrail =	trap_Cvar_Get( "cg_rocketTrail", "40", CVAR_ARCHIVE );
	cg_rocketFireTrail =	trap_Cvar_Get( "cg_rocketFireTrail", "90", CVAR_ARCHIVE );
	cg_grenadeTrail =	trap_Cvar_Get( "cg_grenadeTrail", "20", CVAR_ARCHIVE );
	cg_bloodTrail =		trap_Cvar_Get( "cg_bloodTrail", "10", CVAR_ARCHIVE );
	cg_showBloodTrail =	trap_Cvar_Get( "cg_showBloodTrail", "1", CVAR_ARCHIVE );
	cg_rocketTrailAlpha =	trap_Cvar_Get( "cg_rocketTrailAlpha", "0.35", CVAR_ARCHIVE );
	cg_grenadeTrailAlpha =	trap_Cvar_Get( "cg_grenadeTrailAlpha", "0.5", CVAR_ARCHIVE );
	cg_bloodTrailAlpha =	trap_Cvar_Get( "cg_bloodTrailAlpha", "1.0", CVAR_ARCHIVE );
	cg_explosionsRing =	trap_Cvar_Get( "cg_explosionsRing", "1", CVAR_ARCHIVE );
	cg_gibs =		trap_Cvar_Get( "cg_gibs", "1", CVAR_ARCHIVE );
	cg_outlineModels =	trap_Cvar_Get( "cg_outlineModels", "1", CVAR_ARCHIVE );
	cg_outlineWorld =	trap_Cvar_Get( "cg_outlineWorld", "0", CVAR_ARCHIVE );
	cg_outlineItemsBlack =	trap_Cvar_Get( "cg_outlineItemsBlack", "1", CVAR_ARCHIVE );
	cg_outlinePlayersBlack = trap_Cvar_Get( "cg_outlinePlayersBlack", "0", CVAR_ARCHIVE );
	cg_drawEntityBoxes =	trap_Cvar_Get( "cg_drawEntityBoxes", "0", CVAR_DEVELOPER );
	cg_showObituaries =	trap_Cvar_Get( "cg_showObituaries", "3", CVAR_ARCHIVE );
	cg_autoaction_demo =	trap_Cvar_Get( "cg_autoaction_demo", "0", CVAR_ARCHIVE );
	cg_autoaction_screenshot =  trap_Cvar_Get( "cg_autoaction_screenshot", "0", CVAR_ARCHIVE );
	cg_autoaction_stats =	trap_Cvar_Get( "cg_autoaction_stats", "0", CVAR_ARCHIVE );
	cg_autoaction_spectator = trap_Cvar_Get( "cg_autoaction_spectator", "0", CVAR_ARCHIVE );
	cg_simpleItems =	trap_Cvar_Get( "cg_simpleItems", "0", CVAR_ARCHIVE );
	cg_simpleItemsSize =	trap_Cvar_Get( "cg_simpleItemsSize", "12", CVAR_ARCHIVE );
	cg_particles =		trap_Cvar_Get( "cg_particles", "1", CVAR_ARCHIVE );
	cg_showhelp =		trap_Cvar_Get( "cg_showhelp", "1", CVAR_ARCHIVE );
	cg_predictLaserBeam =	trap_Cvar_Get( "cg_predictLaserBeam", "1", CVAR_ARCHIVE );
	cg_showSelfShadow =	trap_Cvar_Get( "cg_showSelfShadow", "0", CVAR_ARCHIVE );

	cg_cartoonRockets =	trap_Cvar_Get( "cg_cartoonRockets", "0", CVAR_ARCHIVE );
	cg_dashEffect =		trap_Cvar_Get( "cg_dashEffect", "1", CVAR_ARCHIVE );
	cg_fallEffect =		trap_Cvar_Get( "cg_fallEffect", "1", CVAR_ARCHIVE );

	// only allow trail when watching a demos or in race mode
	cg_showPlayerTrails =	trap_Cvar_Get( "cg_showPlayerTrails", "0", CVAR_ARCHIVE );
	cg_playerTrailsColor =   trap_Cvar_Get( "cg_playerTrailsColor", "0.0 1.0 0.0", CVAR_ARCHIVE );

	cg_damage_kick =	trap_Cvar_Get( "cg_damage_kick", "0", CVAR_ARCHIVE );
	cg_damage_blend =	trap_Cvar_Get( "cg_damage_blend", "1", CVAR_ARCHIVE );
	cg_pickup_flash =	trap_Cvar_Get( "cg_pickup_flash", "1", CVAR_ARCHIVE );

	cg_weaponAutoswitch =	trap_Cvar_Get( "cg_weaponAutoswitch", "2", CVAR_ARCHIVE );

	cg_voiceChats =		trap_Cvar_Get( "cg_voiceChats", "1", CVAR_ARCHIVE );
#ifdef CGAMEGETLIGHTORIGIN
	cg_shadows =		trap_Cvar_Get( "cg_shadows", "1", CVAR_ARCHIVE );
#endif

	cg_laserBeamSubdivisions = trap_Cvar_Get( "cg_laserBeamSubdivisions", "10", CVAR_ARCHIVE );

	cg_raceGhosts =		trap_Cvar_Get( "cg_raceGhosts", "0", CVAR_ARCHIVE );
	cg_raceGhostsAlpha =	trap_Cvar_Get( "cg_raceGhostsAlpha", "0.25", CVAR_ARCHIVE );

	cg_chatBeep =		trap_Cvar_Get( "cg_chatBeep", "1", CVAR_ARCHIVE );
	cg_chatFilter =		trap_Cvar_Get( "cg_chatFilter", "0", CVAR_ARCHIVE );
	cg_chatFilterTV =	trap_Cvar_Get( "cg_chatFilterTV", "2", CVAR_ARCHIVE );

	cg_scoreboardStats =	trap_Cvar_Get( "cg_scoreboardStats", "1", CVAR_ARCHIVE );

	// developer cvars
	developer =		trap_Cvar_Get( "developer", "0", CVAR_CHEAT );
	cg_showClamp =		trap_Cvar_Get( "cg_showClamp", "0", CVAR_DEVELOPER );

	//team models
	cg_teamPLAYERSmodel =	trap_Cvar_Get( "cg_teamPLAYERSmodel", "", CVAR_ARCHIVE );
	cg_teamPLAYERSskin =	trap_Cvar_Get( "cg_teamPLAYERSskin", "default", CVAR_ARCHIVE );
	cg_teamPLAYERScolor =	trap_Cvar_Get( "cg_teamPLAYERScolor", "", CVAR_ARCHIVE );
	cg_teamPLAYERSmodel->modified = qtrue;
	cg_teamPLAYERSskin->modified = qtrue;
	cg_teamPLAYERScolor->modified = qtrue;

	cg_teamALPHAmodel =	trap_Cvar_Get( "cg_teamALPHAmodel", "", CVAR_ARCHIVE );
	cg_teamALPHAskin =	trap_Cvar_Get( "cg_teamALPHAskin", "default", CVAR_ARCHIVE );
	cg_teamALPHAcolor =	trap_Cvar_Get( "cg_teamALPHAcolor", DEFAULT_TEAMALPHA_COLOR, CVAR_ARCHIVE );
	cg_teamALPHAmodel->modified = qtrue;
	cg_teamALPHAskin->modified = qtrue;
	cg_teamALPHAcolor->modified = qtrue;

	cg_teamBETAmodel =	trap_Cvar_Get( "cg_teamBETAmodel", "", CVAR_ARCHIVE );
	cg_teamBETAskin =	trap_Cvar_Get( "cg_teamBETAskin", "default", CVAR_ARCHIVE );
	cg_teamBETAcolor =	trap_Cvar_Get( "cg_teamBETAcolor", DEFAULT_TEAMBETA_COLOR, CVAR_ARCHIVE );
	cg_teamBETAmodel->modified = qtrue;
	cg_teamBETAskin->modified = qtrue;
	cg_teamBETAcolor->modified = qtrue;

	cg_teamGAMMAmodel =	trap_Cvar_Get( "cg_teamGAMMAmodel", "", CVAR_ARCHIVE );
	cg_teamGAMMAskin =	trap_Cvar_Get( "cg_teamGAMMAskin", "default", CVAR_ARCHIVE );
	cg_teamGAMMAcolor =	trap_Cvar_Get( "cg_teamGAMMAcolor", DEFAULT_TEAMGAMMA_COLOR, CVAR_ARCHIVE );
	cg_teamGAMMAmodel->modified = qtrue;
	cg_teamGAMMAskin->modified = qtrue;
	cg_teamGAMMAcolor->modified = qtrue;

	cg_teamDELTAmodel = trap_Cvar_Get( "cg_teamDELTAmodel", "", CVAR_ARCHIVE );
	cg_teamDELTAskin =	trap_Cvar_Get( "cg_teamDELTAskin", "default", CVAR_ARCHIVE );
	cg_teamDELTAcolor = trap_Cvar_Get( "cg_teamDELTAcolor", DEFAULT_TEAMDELTA_COLOR, CVAR_ARCHIVE );
	cg_teamDELTAmodel->modified = qtrue;
	cg_teamDELTAskin->modified = qtrue;
	cg_teamDELTAcolor->modified = qtrue;

	cg_forceMyTeamAlpha =		trap_Cvar_Get( "cg_forceMyTeamAlpha", "0", CVAR_ARCHIVE );
	cg_forceTeamPlayersTeamBeta =	trap_Cvar_Get( "cg_forceTeamPlayersTeamBeta", "0", CVAR_ARCHIVE );

	// dmh - learn0more's team colored beams
	cg_teamColoredBeams = trap_Cvar_Get( "cg_teamColoredBeams", "1", CVAR_ARCHIVE );

	cg_ebbeam_width = trap_Cvar_Get( "cg_ebbeam_width", "16", CVAR_ARCHIVE );
	cg_ebbeam_alpha = trap_Cvar_Get( "cg_ebbeam_alpha", "0.4", CVAR_ARCHIVE );
	cg_ebbeam_time = trap_Cvar_Get( "cg_ebbeam_time", "0.6", CVAR_ARCHIVE );

	cg_instabeam_width = trap_Cvar_Get( "cg_instabeam_width", "6", CVAR_ARCHIVE );
	cg_instabeam_alpha = trap_Cvar_Get( "cg_instabeam_alpha", "0.4", CVAR_ARCHIVE );
	cg_instabeam_time = trap_Cvar_Get( "cg_instabeam_time", "0.4", CVAR_ARCHIVE );

	cg_damage_indicator_fade = trap_Cvar_Get( "cg_damage_indicator_fade", "50", CVAR_ARCHIVE );
	cg_damage_indicator_dir_fade = trap_Cvar_Get( "cg_damage_indicator_dir_fade", "50", CVAR_ARCHIVE );
	cg_damage_indicator = trap_Cvar_Get( "cg_damage_indicator", "1", CVAR_ARCHIVE );
	cg_damage_indicator_dir = trap_Cvar_Get( "cg_damage_indicator_dir", "1", CVAR_ARCHIVE );

	cg_showminimap =  trap_Cvar_Get( "cg_showMiniMap", "0", CVAR_ARCHIVE );
	cg_placebo =  trap_Cvar_Get( "cg_placebo", "0", CVAR_ARCHIVE );
}

// local cgame commands
typedef struct
{
	char *name;
	void ( *func )( void );
} cgcmd_t;

static cgcmd_t cgcmds[] =
{
	{ "score", CG_ToggleScores_f },
	{ "+scores", CG_ScoresOn_f },
	{ "-scores", CG_ScoresOff_f },
	{ "weaplast", CG_WeapLast_f },
	{ "weapprev", CG_WeapPrev_f },
	{ "weapnext", CG_WeapNext_f },
	{ "use", CG_Cmd_Use_f },
	{ "demoget", CG_Cmd_DemoGet_f },
	{ "demolist", NULL },

	{ NULL, NULL }
};


/*
   =================
   CG_RegisterCGameCommands
   =================
 */
void CG_RegisterCGameCommands( void )
{
	int i;
	char *name;
	cgcmd_t *cmd;

	CG_LoadingFilename( "" );

	// add game side commands

	for( i = 0; i < MAX_GAMECOMMANDS; i++ )
	{
		name = cgs.configStrings[CS_GAMECOMMANDS+i];
		if( !name[0] )
			continue;

		// check for local command overrides
		for( cmd = cgcmds; cmd->name; cmd++ )
		{
			if( !Q_stricmp( cmd->name, name ) )
				break;
		}
		if( cmd->name )
			continue;

		trap_Cmd_AddCommand( name, NULL );
	}

	// add local commands
	for( cmd = cgcmds; cmd->name; cmd++ )
	{
		trap_Cmd_AddCommand( cmd->name, cmd->func );
	}
}

/*
   =================
   CG_UnregisterCGameCommands
   =================
 */
void CG_UnregisterCGameCommands( void )
{
	int i;
	char *name;
	cgcmd_t *cmd;

	// remove game commands
	for( i = 0; i < MAX_GAMECOMMANDS; i++ )
	{
		name = cgs.configStrings[CS_GAMECOMMANDS+i];
		if( !name[0] )
			continue;

		// check for local command overrides so we don't try
		// to unregister them twice
		for( cmd = cgcmds; cmd->name; cmd++ )
		{
			if( !Q_stricmp( cmd->name, name ) )
				break;
		}
		if( cmd->name )
			continue;

		trap_Cmd_RemoveCommand( name );
	}

	// remove local commands
	for( cmd = cgcmds; cmd->name; cmd++ )
	{
		trap_Cmd_RemoveCommand( cmd->name );
	}
}

/*
   =================
   CG_RegisterDemoCommands
   =================
 */
void CG_RegisterDemoCommands( void )
{
	trap_Cmd_AddCommand( "score", CG_ToggleScores_f );
	trap_Cmd_AddCommand( "+scores", CG_ScoresOn_f );
	trap_Cmd_AddCommand( "-scores", CG_ScoresOff_f );
	trap_Cmd_AddCommand( "weapprev", CG_WeapPrev_f );
	trap_Cmd_AddCommand( "weapnext", CG_WeapNext_f );
}

/*
   =================
   CG_RegisterGameCommands
   =================
 */
void CG_UnregisterDemoCommands( void )
{
	trap_Cmd_RemoveCommand( "score" );
	trap_Cmd_RemoveCommand( "+scores" );
	trap_Cmd_RemoveCommand( "-scores" );
	trap_Cmd_RemoveCommand( "weapprev" );
	trap_Cmd_RemoveCommand( "weapnext" );
}

/*
   =================
   CG_ValidateItemDef
   Compares name and tag against the itemlist to make sure cgame and game lists match.
   =================
 */
void CG_ValidateItemDef( int tag, char *name )
{
	gitem_t *item;

	item = GS_FindItemByName( name );
	if( !item )
		CG_Error( "Client/Server itemlist missmatch (Game and Cgame version/mod differs). Item '%s' not found\n", name );

	if( item->tag != tag )
		CG_Error( "Client/Server itemlist missmatch (Game and Cgame version/mod differs).\n" );
}

/*
   =================
   CG_ValidateItemList
   =================
 */
static void CG_ValidateItemList( void )
{
	int i;

	for( i = CS_ITEMS; i < CS_ITEMS+MAX_ITEMS; i++ )
	{
		if( cgs.configStrings[i][0] )
			CG_ValidateItemDef( i - CS_ITEMS, cgs.configStrings[i] );
	}
}

//=================
//CG_UpdateServerSettings
//=================
void CG_UpdateServerSettings( void )
{
	char *tok;
	// if we got the server settings configstring, update our local copy of the data
	if( strlen( cgs.configStrings[CS_SERVERSETTINGS] ) )
	{
		char *settings = cgs.configStrings[CS_SERVERSETTINGS];
		tok = COM_Parse( &settings ); // cgs.serverRules.tv
		cgs.serverRules.has_challengers = ( atoi( COM_Parse( &settings ) ) != 0 );
		cgs.serverRules.maxteams = atoi( COM_Parse( &settings ) );
		Q_strncpyz( cgs.serverRules.gametype, GS_Gametype_ShortName( atoi( COM_Parse( &settings ) ) ),
		           sizeof( cgs.serverRules.gametype ) );
		cgs.serverRules.instagib = ( atoi( COM_Parse( &settings ) ) != 0 );
		cgs.serverRules.falldamage = ( atoi( COM_Parse( &settings ) ) != 0 );
	}
}

//=================
//CG_RegisterConfigStrings
//=================
static void CG_RegisterConfigStrings( void )
{
	int i;

	for( i = 0; i < MAX_CONFIGSTRINGS; i++ )
		trap_GetConfigString( i, cgs.configStrings[i], MAX_CONFIGSTRING_CHARS );

	// if we got the server settings configstring, update our local copy of the data
	CG_UpdateServerSettings();
}

/*
   =================
   CG_StartBackgroundTrack
   =================
 */
static void CG_StartBackgroundTrack( void )
{
	char *string;
	char intro[MAX_QPATH], loop[MAX_QPATH];

	string = cgs.configStrings[CS_AUDIOTRACK];
	Q_strncpyz( intro, COM_Parse( &string ), sizeof( intro ) );
	Q_strncpyz( loop, COM_Parse( &string ), sizeof( loop ) );

	trap_S_StartBackgroundTrack( intro, loop );
}

//=================
//CG_RegisterFonts
//=================
static void CG_RegisterFonts( void )
{
	cvar_t *con_fontSystemSmall = trap_Cvar_Get( "con_fontSystemSmall", DEFAULT_FONT_SMALL, CVAR_ARCHIVE );
	cvar_t *con_fontSystemMedium = trap_Cvar_Get( "con_fontSystemMedium", DEFAULT_FONT_MEDIUM, CVAR_ARCHIVE );
	cvar_t *con_fontSystemBig = trap_Cvar_Get( "con_fontSystemBig", DEFAULT_FONT_BIG, CVAR_ARCHIVE );

	cgs.fontSystemSmall = trap_SCR_RegisterFont( con_fontSystemSmall->string );
	if( !cgs.fontSystemSmall )
	{
		cgs.fontSystemSmall = trap_SCR_RegisterFont( DEFAULT_FONT_SMALL );
		if( !cgs.fontSystemSmall )
			CG_Error( "Couldn't load default font \"%s\"", DEFAULT_FONT_SMALL );
	}
	cgs.fontSystemMedium = trap_SCR_RegisterFont( con_fontSystemMedium->string );
	if( !cgs.fontSystemMedium )
		cgs.fontSystemMedium = trap_SCR_RegisterFont( DEFAULT_FONT_MEDIUM );

	cgs.fontSystemBig = trap_SCR_RegisterFont( con_fontSystemBig->string );
	if( !cgs.fontSystemBig )
		cgs.fontSystemBig = trap_SCR_RegisterFont( DEFAULT_FONT_BIG );
}

/*
* CG_SoftRestart
*/
void CG_SoftRestart( void )
{
	CG_ResetPModels();

	CG_ResetDamageIndicator ();

	CG_ClearDecals();
	CG_ClearPolys();
	CG_ClearEffects();
	CG_ClearLocalEntities();

	// start up announcer events queue from clean
	CG_ClearAnnouncerEvents();

	memset( cg_entities, 0, sizeof( cg_entities ) );
}

/*
* CG_Init
*/
void CG_Init( unsigned int playerNum, int vidWidth, int vidHeight, qboolean demoplaying,
              qboolean pure, qboolean tv, unsigned int baseServerTime, unsigned int snapFrameTime, int protocol )
{
	CG_InitGameShared();

	memset( &cg, 0, sizeof( cg_state_t ) );
	memset( &cgs, 0, sizeof( cg_static_t ) );

	memset( cg_entities, 0, sizeof( cg_entities ) );

	// save local player number
	cgs.playerNum = playerNum;

	// save current width and height
	cgs.vidWidth = vidWidth;
	cgs.vidHeight = vidHeight;

	// demo
	cgs.demoPlaying = demoplaying;

	// whether to only allow pure files
	cgs.pure = pure;

	// whether we are connected to a tv-server
	cgs.tv = tv;
	cgs.tvRequested = qfalse;

	// game protocol number
	cgs.gameProtocol = protocol;
	cgs.baseServerTime = baseServerTime;
	cgs.snapFrameTime = snapFrameTime;

	CG_RegisterVariables();
	CG_InitTemporaryBoneposesCache();
	CG_PModelsInit();

	CG_ScreenInit();

	// get configstrings
	CG_RegisterConfigStrings();

	// register fonts here so loading screen works
	CG_RegisterFonts();
	cgs.shaderWhite = trap_R_RegisterPic( "gfx/ui/white" );

	CG_RegisterLevelShot();
	CG_RegisterLevelMinimap();

	CG_RegisterModels();
	CG_RegisterSounds();
	CG_RegisterShaders();
	CG_RegisterSkinFiles();
	CG_RegisterClients();

	if( cgs.demoPlaying )
		CG_RegisterDemoCommands();
	else
		CG_RegisterCGameCommands();

	CG_ValidateItemList();

	CG_LoadStatusBar();

	CG_LoadingString( "-" ); // awaiting snapshot...
	CG_LoadingFilename( "" );

	CG_ClearDecals();
	CG_ClearPolys();
	CG_ClearEffects();
	CG_ClearLocalEntities();

	CG_RegisterLightStyles();

	// start background track
	CG_StartBackgroundTrack();

	// start up announcer events queue from clean
	CG_ClearAnnouncerEvents();

	cgs.precacheDone = qtrue;

	CG_DemocamInit();
}

/*
   ============
   CG_Shutdown
   ============
 */
void CG_Shutdown( void )
{
	CG_DemocamShutdown();
	CG_ScreenShutdown();

	if( cgs.demoPlaying )
		CG_UnregisterDemoCommands();
	else
		CG_UnregisterCGameCommands();
}

// helper for below
static int TDM_BitToTeam( int bit )
{
	switch( bit )
	{
		case AREA_TEAM_ALPHA: return TEAM_ALPHA;
		case AREA_TEAM_BETA: return TEAM_BETA;
		case AREA_TEAM_GAMMA: return TEAM_GAMMA;
		case AREA_TEAM_DELTA: return TEAM_DELTA;
	}

	return 0;
}
//====================
// CG_GetCaptureAreaInfo
// returns capture area information
//====================
void CG_GetCaptureAreaInfo( int areano, int *stateout, int *teamout, int *cteamout, float *percentageout )
{
	int area, team, state, cteam = 0;
	float percentage = 0;
	int areas = cg.frame.playerState.stats[STAT_TDM_CAPTURE_AREA_STATUS],
	    capturing = 0,
	    capturing1 = cg.frame.playerState.stats[STAT_TDM_CAPTURE_AREA_CAPTURING1],
	    capturing2 = cg.frame.playerState.stats[STAT_TDM_CAPTURE_AREA_CAPTURING2];

	// reset
	if( stateout ) *stateout = 0;
	if( teamout ) *teamout = 0;
	if( cteamout ) *cteamout = 0;
	if( percentageout ) *percentageout = 0;

	// invalid areano
	if( areano < 0 || areano >= MAX_CAPTURE_AREAS )
		return;

	// tdm only
	if( cg.frame.playerState.stats[STAT_GAMETYPE] != GAMETYPE_TDM )
		return;

	area = ( areas >> areano * AREA_TOTAL_SIZE ) & AREA_TOTAL_BITMASK;

	// this area doesnt exist
	if( !area )
		return;

	team = area & AREA_TEAM_BITMASK;
	state = area - team;


	// compute capturing status
	switch ( areano )
	{
		case 0:
			capturing = capturing1 & AREA_CAPTURING_BITMASK;
			break;

		case 1:
			capturing = ( capturing1 >> AREA_CAPTURING_SIZE ) & AREA_CAPTURING_BITMASK;
			break;

		case 2:
			capturing = capturing2 & AREA_CAPTURING_BITMASK;
			break;

		case 3:
			capturing = ( capturing2 >> AREA_CAPTURING_SIZE ) & AREA_CAPTURING_BITMASK;
			break;
	}
	
	// a team is capturing this flag
	if( capturing )
	{
		cteam = capturing & AREA_TEAM_BITMASK;
		percentage = ( ( capturing - cteam ) >> AREA_TEAM_SIZE ) & AREA_PERCENTAGE_BITMASK;
		percentage *= 2.0f / 100.0f; // convert from 0-50 to 0-100 and 0 to 1
	}

	// send capture area info
	if( stateout ) *stateout = state;
	if( teamout ) *teamout = TDM_BitToTeam( team );
	if( cteamout ) *cteamout = TDM_BitToTeam( cteam );
	if( percentageout ) *percentageout = percentage;
}

//======================================================================

#ifndef CGAME_HARD_LINKED
// this is only here so the functions in q_shared.c and q_math.c can link
void Sys_Error( const char *format, ... )
{
	va_list	argptr;
	char msg[1024];

	va_start( argptr, format );
	Q_vsnprintfz( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	trap_Error( msg );
}

void Com_Printf( const char *format, ... )
{
	va_list	argptr;
	char msg[1024];

	va_start( argptr, format );
	Q_vsnprintfz( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	trap_Print( msg );
}
#endif
