// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
//

#include "g_local.h"
#include "g_utils.h"
#include "ctype.h"
#include "worldspawn.h"
#include "scriptmaster.h"
#include "windows.h"

cvar_t *g_numdebuglines;

debugline_t *DebugLines = NULL;
Vector currentVertex( 0, 0, 0 );
Vector vertColor( 1, 1, 1 );
float vertAlpha = 1;
float vertexIndex = 0;

/*
   ============
   G_TouchTriggers

   ============
 */
void G_TouchTriggers( Entity *ent )
{
	int i;
	int num;
	edict_t *touch[ MAX_EDICTS ];
	edict_t *hit;
	Event       *ev;

	// dead things don't activate triggers!
	if ( ( ent->client || ( ent->edict->svflags & SVF_MONSTER ) ) && ( ent->health <= 0 ) )
	{
		return;
	}

	num = gi.BoxEdicts( ent->absmin.vec3(), ent->absmax.vec3(), touch, MAX_EDICTS, AREA_TRIGGERS );

	// be careful, it is possible to have an entity in this
	// list removed before we get to it (killtriggered)
	for( i = 0; i < num; i++ )
	{
		hit = touch[ i ];
		if ( !hit->inuse || ( hit->entity == ent ) )
		{
			continue;
		}

		assert( hit->entity );

		// FIXME
		// should we post the events on the list with zero time
		ev = new Event( EV_Touch );
		ev->AddEntity( ent );
		hit->entity->ProcessEvent( ev );
	}
}

/*
   ============
   G_TouchSolids

   Call after linking a new trigger in during gameplay
   to force all entities it covers to immediately touch it
   ============
 */
void G_TouchSolids( Entity *ent )
{
	int i;
	int num;
	edict_t *touch[ MAX_EDICTS ];
	edict_t *hit;
	Event       *ev;

	num = gi.BoxEdicts( ent->absmin.vec3(), ent->absmax.vec3(), touch, MAX_EDICTS, AREA_SOLID );

	// be careful, it is possible to have an entity in this
	// list removed before we get to it (killtriggered)
	for( i = 0; i < num; i++ )
	{
		hit = touch[ i ];
		if ( !hit->inuse )
		{
			continue;
		}

		assert( hit->entity );

		//FIXME
		// should we post the events so that we don't have to worry about any entities going away
		ev = new Event( EV_Touch );
		ev->AddEntity( ent );
		hit->entity->ProcessEvent( ev );
	}
}

EXPORT_FROM_DLL void G_ShowTrace( trace_t *trace,edict_t *passent,const char *reason )
{
	str text;
	str pass;
	str hit;

	assert( reason );
	assert( trace );

	if ( passent )
	{
		pass = va( "'%s'(%d)", passent->entname, passent->s.number );
	}
	else
	{
		pass = "NULL";
	}

	if ( trace->ent )
	{
		hit = va( "'%s'(%d)", trace->ent->entname, trace->ent->s.number );
	}
	else
	{
		hit = "NULL";
	}

	text = va( "%0.1f : Pass %s Frac %f Hit %s : '%s'\n",
	           level.time, pass.c_str(), trace->fraction, hit.c_str(), reason ? reason : "" );

	if ( sv_traceinfo->value == 3 )
	{
		G_DebugPrintf( text.c_str() );
	}
	else
	{
		gi.dprintf( "%s", text.c_str() );
	}
}

EXPORT_FROM_DLL void G_CalcBoundsOfMove( Vector &start,Vector &end,Vector &mins,Vector &maxs,Vector *minbounds,Vector *maxbounds )
{
	Vector bmin;
	Vector bmax;

	ClearBounds( bmin.vec3(), bmax.vec3() );
	AddPointToBounds( start.vec3(), bmin.vec3(), bmax.vec3() );
	AddPointToBounds( end.vec3(), bmin.vec3(), bmax.vec3() );
	bmin += mins;
	bmax += maxs;

	if ( minbounds )
	{
		*minbounds = bmin;
	}

	if ( maxbounds )
	{
		*maxbounds = bmax;
	}
}

EXPORT_FROM_DLL trace_t G_Trace( vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,edict_t *passent,int contentmask,const char *reason )
{
	trace_t trace;

	trace = gi.trace( start, mins, maxs, end, passent, contentmask );
	assert( !trace.ent || trace.ent->entity );

	if ( sv_traceinfo->value > 1 )
	{
		G_ShowTrace( &trace, passent, reason );
	}
	sv_numtraces++;

	if ( sv_drawtrace->value )
	{
		G_DebugLine( Vector( start ), Vector( end ), 1, 1, 0, 1 );
	}

	return trace;
}

EXPORT_FROM_DLL trace_t G_Trace( Vector &start,Vector &mins,Vector &maxs,Vector &end,Entity *passent,int contentmask,const char *reason )
{
	edict_t *ent;
	trace_t trace;

	assert( reason );

	if ( passent == NULL )
	{
		ent = NULL;
	}
	else
	{
		ent = passent->edict;
	}

	trace = gi.trace( start.vec3(), mins.vec3(), maxs.vec3(), end.vec3(), ent, contentmask );

	assert( !trace.ent || trace.ent->entity );

	if ( sv_traceinfo->value > 1 )
	{
		G_ShowTrace( &trace, ent, reason );
	}
	sv_numtraces++;

	if ( sv_drawtrace->value )
	{
		G_DebugLine( start, end, 1, 1, 0, 1 );
	}

	return trace;
}

EXPORT_FROM_DLL trace_t G_FullTrace( Vector &start,Vector &mins,Vector &maxs,Vector &end,float radius,Entity *passent,int contentmask,const char *reason )
{
	edict_t *ent;
	trace_t trace;

	if ( passent == NULL )
	{
		ent = NULL;
	}
	else
	{
		ent = passent->edict;
	}

	trace = gi.fulltrace( start.vec3(), mins.vec3(), maxs.vec3(), end.vec3(), radius, ent, contentmask );
	assert( !trace.ent || trace.ent->entity );

	if ( sv_traceinfo->value > 1 )
	{
		G_ShowTrace( &trace, ent, reason );
	}
	sv_numtraces++;

	if ( sv_drawtrace->value )
	{
		G_DebugLine( start, end, 0, 1, 1, 1 );
	}

	return trace;
}

EXPORT_FROM_DLL trace_t G_FullTrace( vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,float radius,edict_t *passent,int contentmask,const char *reason )
{
	trace_t trace;

	trace = gi.fulltrace( start, mins, maxs, end, radius, passent, contentmask );
	assert( !trace.ent || trace.ent->entity );

	if ( sv_traceinfo->value > 1 )
	{
		G_ShowTrace( &trace, passent, reason );
	}
	sv_numtraces++;

	if ( sv_drawtrace->value )
	{
		G_DebugLine( Vector( start ), Vector( end ), 0, 1, 1, 1 );
	}

	return trace;
}

/*
   =======================================================================

   SelectSpawnPoint

   =======================================================================
 */

/*
   ================
   PlayersRangeFromSpot

   Returns the distance to the nearest player from the given spot
   ================
 */
float   PlayersRangeFromSpot( Entity *spot )
{
	Entity  *player;
	float bestplayerdistance;
	Vector v;
	int n;
	float playerdistance;

	bestplayerdistance = 9999999;
	for( n = 1; n <= maxclients->value; n++ )
	{
		if ( !g_edicts[ n ].inuse || !g_edicts[ n ].entity )
		{
			continue;
		}

		player = g_edicts[ n ].entity;
		if ( player->health <= 0 )
		{
			continue;
		}

		v = spot->worldorigin - player->worldorigin;
		playerdistance = v.length();

		if ( playerdistance < bestplayerdistance )
		{
			bestplayerdistance = playerdistance;
		}
	}

	return bestplayerdistance;
}

/*
   ================
   SelectRandomDeathmatchSpawnPoint

   go to a random point, but NOT the two points closest
   to other players
   ================
 */
Entity *SelectRandomDeathmatchSpawnPoint( void )
{
	Entity  *spot, *spot1, *spot2;
	int count = 0;
	int selection;
	int num;
	float range, range1, range2;

	spot = NULL;
	range1 = range2 = 99999;
	spot1 = spot2 = NULL;

	num = 0;
	while( ( num = G_FindClass( num, "info_player_deathmatch" ) ) != 0 )
	{
		spot = G_GetEntity( num );
		count++;
		range = PlayersRangeFromSpot( spot );
		if ( range < range1 )
		{
			range1 = range;
			spot1 = spot;
		}
		else if (range < range2)
		{
			range2 = range;
			spot2 = spot;
		}
	}

	if ( !count )
	{
		return NULL;
	}

	if ( count <= 2 )
	{
		spot1 = spot2 = NULL;
	}
	else
	{
		count -= 2;
	}

	selection = rand() % count;

	spot = NULL;
	num = 0;
	do
	{
		num = G_FindClass( num, "info_player_deathmatch" );
		spot = G_GetEntity( num );
		if ( spot == spot1 || spot == spot2 )
		{
			selection++;
		}
	}
	while( selection-- );

	return spot;
}

/*
   ================
   SelectFarthestDeathmatchSpawnPoint

   ================
 */
Entity *SelectFarthestDeathmatchSpawnPoint( void )
{
	Entity  *bestspot;
	float bestdistance;
	float bestplayerdistance;
	Entity  *spot;
	int num;

	spot = NULL;
	bestspot = NULL;
	bestdistance = 0;
	num = 0;
	while( ( num = G_FindClass( num, "info_player_deathmatch" ) ) != NULL )
	{
		spot = G_GetEntity( num );

		bestplayerdistance = PlayersRangeFromSpot( spot );
		if ( bestplayerdistance > bestdistance )
		{
			bestspot = spot;
			bestdistance = bestplayerdistance;
		}
	}

	if ( bestspot )
	{
		return bestspot;
	}

	// if there is a player just spawned on each and every start spot
	// we have no choice to turn one into a telefrag meltdown
	num = G_FindClass( 0, "info_player_deathmatch" );
	spot = G_GetEntity( num );

	return spot;
}

Entity *SelectDeathmatchSpawnPoint( void )
{
	if ( DM_FLAG( DF_SPAWN_FARTHEST ) )
	{
		return SelectFarthestDeathmatchSpawnPoint();
	}
	else
	{
		return SelectRandomDeathmatchSpawnPoint();
	}
}

Entity *SelectCoopSpawnPoint( void )
{
	const char *tname;
	Entity *spot = NULL;
	int num;

	num = 0;
	while( ( num = G_FindClass( num, "info_player_coop" ) ) != 0 )
	{
		spot = G_GetEntity( num );
		tname = spot->TargetName();
		if ( !game.spawnpoint.length() || !tname || !tname[ 0 ] )
		{
			break;
		}

		if ( Q_stricmp( game.spawnpoint.c_str(), spot->TargetName() ) == 0 )
		{
			break;
		}
	}

	return spot;
}


/*
   ===========
   SelectSpawnPoint

   Chooses a player start, deathmatch start, coop start, etc
   ============
 */
void SelectSpawnPoint( Vector &origin,Vector &angles,int *gravaxis,int *startonbike //###
                       )
{
	const char * tname;
	Entity *spot = NULL;
	Entity *spot2 = NULL;
	int num;

	if ( ( level.training == 2 ) && game.spawnpoint.length() )
	{
		num = 0;
		while( ( num = G_FindClass( num, "info_player_progressivestart" ) ) != 0 )
		{
			spot2 = G_GetEntity( num );
			tname = spot2->TargetName();
			if ( !tname || !tname[ 0 ] )
			{
				break;
			}

			if ( Q_stricmp( game.spawnpoint.c_str(), spot2->TargetName() ) == 0 )
			{
				spot = spot2;
				break;
			}
		}
	}
	else if ( deathmatch->value || level.training == 1 )
	{
		spot = SelectDeathmatchSpawnPoint();
	}
	else if ( coop->value )
	{
		spot = SelectCoopSpawnPoint();
	}

	// find a single player start spot
	if ( !spot )
	{
		num = 0;
		while( ( num = G_FindClass( num, "info_player_start" ) ) != 0 )
		{
			spot = G_GetEntity( num );
			tname = spot->TargetName();
			if ( !game.spawnpoint.length() || !tname || !tname[ 0 ] )
			{
				break;
			}

			if ( Q_stricmp( game.spawnpoint.c_str(), spot->TargetName() ) == 0 )
			{
				break;
			}
		}

		if ( !spot )
		{
			if ( !game.spawnpoint.length() )
			{
				// there wasn't a spawnpoint without a target, so use any
				num = G_FindClass( 0, "info_player_start" );
				spot = G_GetEntity( num );
			}

			if ( !spot || !spot->entnum )
			{
				gi.error( "Couldn't find spawn point %s\n", game.spawnpoint.c_str() );
			}
		}
	}

	origin = spot->worldorigin + "0 0 9";
	angles = spot->angles;
	if ( gravaxis )
	{
		*gravaxis = spot->gravaxis;
	}

	//###
	if(startonbike)
	{
		if((spot->spawnflags & 1) || level.bikesonly)
		{
			// player should be started on a bike
			*startonbike = 1;
		}
		else
		{
			*startonbike = 0;
		}
	}
	else
	{
		*startonbike = 0;
	}
	//###
}

/*
   =============
   M_CheckBottom

   Returns false if any part of the bottom of the entity is off an edge that
   is not a staircase.

   =============
 */
int c_yes, c_no;

qboolean M_CheckBottom( Entity *ent )
{
	Vector mins, maxs, start, stop;
	trace_t trace;
	int x, y;
	float mid, bottom;

//### added check for ceiling movetype
	if(ent->movetype == MOVETYPE_CEILINGSTEP)
	{
		return M_CeilingCheckBottom( ent );
	}
//###

	mins = ent->worldorigin + ent->mins * 0.5;
	maxs = ent->worldorigin + ent->maxs * 0.5;

	// if all of the points under the corners are solid world, don't bother
	// with the tougher checks
	// the corners must be within 16 of the midpoint
	start[ 2 ] = mins[ 2 ] - 1;

	for( x = 0; x <= 1; x++ )
	{
		for( y = 0; y <= 1; y++ )
		{
			start[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
			start[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];
			if ( gi.pointcontents( start.vec3() ) != CONTENTS_SOLID )
			{
				goto realcheck;
			}
		}
	}

	c_yes++;
	return true;        // we got out easy

realcheck:

	c_no++;

	//
	// check it for real...
	//
	start[ 2 ] = mins[ 2 ];

	// the midpoint must be within 16 of the bottom
	start[ 0 ] = stop[ 0 ] = ( mins[ 0 ] + maxs[ 0 ] ) * 0.5;
	start[ 1 ] = stop[ 1 ] = ( mins[ 1 ] + maxs[ 1 ] ) * 0.5;
	stop[ 2 ] = start[ 2 ] - 3 * STEPSIZE;//2 * STEPSIZE;

	trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, "M_CheckBottom 1" );

	if ( trace.fraction == 1.0 )
	{
		return false;
	}

	mid = bottom = trace.endpos[ 2 ];

	// the corners must be within 16 of the midpoint
	for( x = 0; x <= 1; x++ )
	{
		for( y = 0; y <= 1; y++ )
		{
			start[ 0 ] = stop[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
			start[ 1 ] = stop[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];

			trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, "M_CheckBottom 2" );

			if ( trace.fraction != 1.0 && trace.endpos[ 2 ] > bottom )
			{
				bottom = trace.endpos[ 2 ];
			}

			if ( trace.fraction == 1.0 || mid - trace.endpos[ 2 ] > STEPSIZE )
			{
				return false;
			}
		}
	}

	c_yes++;
	return true;
}

char *G_CopyString( const char *in )
{
	char    *newb;
	char    *new_p;
	int i,l;

	assert( in );

	l = strlen( in ) + 1;

	newb = ( char * )gi.TagMalloc( l, TAG_LEVEL );
	new_p = newb;

	for( i = 0; i < l; i++ )
	{
		if ( ( in[ i ] == '\\' ) && ( i < l - 1 ) )
		{
			i++;
			if ( in[ i ] == 'n' )
			{
				*new_p++ = '\n';
			}
			else
			{
				*new_p++ = '\\';
			}
		}
		else
		{
			*new_p++ = in[ i ];
		}
	}

	return newb;
}

int G_FindClass( int entnum,const char *classname )
{
	edict_t *from;

	for ( from = &g_edicts[ entnum + 1 ]; from < &g_edicts[ globals.num_edicts ]; from++ )
	{
		if ( !from->inuse )
		{
			continue;
		}
		if ( !Q_stricmp ( from->entity->getClassID(), classname ) )
		{
			return from->s.number;
		}
	}

	return 0;
}

int G_FindTarget( int entnum,const char *name )
{
	edict_t  *from;
	Entity   *next;

	if ( name && name[ 0 ] )
	{
		from = &g_edicts[ entnum ];
		next = world->GetNextEntity( str( name ), from->entity );
		if ( next )
		{
			return next->entnum;
		}
	}

	return 0;
}

EXPORT_FROM_DLL Entity *G_NextEntity( Entity *ent )
{
	edict_t *from;

	if ( !g_edicts )
	{
		return NULL;
	}

	if ( !ent )
	{
		ent = world;
	}

	if ( !ent )
	{
		return NULL;
	}

	for ( from = ent->edict + 1; from < &g_edicts[ globals.num_edicts ]; from++ )
	{
		if ( !from->inuse || !from->entity )
		{
			continue;
		}

		return from->entity;
	}

	return NULL;
}

//
// QuakeEd only writes a single float for angles (bad idea), so up and down are
// just constant angles.
//
Vector G_GetMovedir( void )
{
	float angle;

	angle = G_GetFloatArg( "angle" );
	if ( angle == -1 )
	{
		return Vector( 0, 0, 1 );
	}
	else if ( angle == -2 )
	{
		return Vector( 0, 0, -1 );
	}

	angle *= ( M_PI * 2 / 360 );
	return Vector( cos( angle ), sin( angle ), 0 );
}

/*
   =================
   KillBox

   Kills all entities that would touch the proposed new positioning
   of ent.  Ent should be unlinked before calling this!
   =================
 */
qboolean KillBox( Entity *ent )
{
	int i;
	int num;
	edict_t *touch[ MAX_EDICTS ];
	edict_t *hit;
	Vector min;
	Vector max;
	int fail;

	fail = 0;

	min = ent->worldorigin + ent->mins;
	max = ent->worldorigin + ent->maxs;
	num = gi.BoxEdicts( min.vec3(), max.vec3(), touch, MAX_EDICTS, AREA_SOLID );
	for( i = 0; i < num; i++ )
	{
		hit = touch[ i ];

		if ( !hit->inuse || ( hit->entity == ent ) || !hit->entity || ( hit->entity == world ) )
		{
			continue;
		}

		hit->entity->Damage( ent, ent, hit->entity->health + 100000, ent->worldorigin, vec_zero, vec_zero,
		                     0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG, -1, -1, 1.0f );

		//
		// if we didn't kill it, fail
		//
		if ( hit->entity->getSolidType() != SOLID_NOT )
		{
			fail++;
		}
	}

	//
	// all clear
	//
	return !fail;
}

qboolean IsNumeric( const char *str )
{
	int len;
	int i;
	qboolean dot;

	if ( *str == '-' )
	{
		str++;
	}

	dot = false;
	len = strlen( str );
	for( i = 0; i < len; i++ )
	{
		if ( !isdigit( str[ i ] ) )
		{
			if ( ( str[ i ] == '.' ) && !dot )
			{
				dot = true;
				continue;
			}
			return false;
		}
	}

	return true;
}

void G_InitEdict( edict_t *e )
{
	e->inuse = true;
	e->s.number = e - g_edicts;

	// make sure a default scale gets set
	e->s.scale = 1.0f;
	e->s.renderfx |= RF_FRAMELERP;
	e->spawntime = level.time;
	e->s.frame = 0;
	e->s.prevframe = -1;
}

/*
   =================
   findradius

   Returns entities that have origins within a spherical area

   findradius (origin, radius)
   =================
 */
Entity *findradius( Entity *startent,Vector org,float rad )
{
	Vector eorg;
	edict_t *from;
	float r2;

	if ( !startent )
	{
		startent = world;
	}

	if ( !startent )
	{
		return NULL;
	}

	// square the radius so that we don't have to do a square root
	r2 = rad * rad;

	assert( startent->edict );
	for ( from = startent->edict + 1; from < &g_edicts[ globals.num_edicts ]; from++ )
	{
		if ( !from->inuse )
		{
			continue;
		}

		assert( from->entity );

		eorg = org - from->entity->centroid;

		// dot product returns length squared
		if ( ( eorg * eorg ) <= r2 )
		{
			return from->entity;
		}
	}

	return NULL;
}

/*
   =================
   findclientinradius

   Returns clients that have origins within a spherical area

   findclientinradius (origin, radius)
   =================
 */
Entity *findclientsinradius( Entity *startent,Vector org,float rad )
{
	Vector eorg;
	edict_t *ed;
	float r2;
	int i;

	// square the radius so that we don't have to do a square root
	r2 = rad * rad;

	for( i = startent->entnum; i < game.maxclients; i++ )
	{
		ed = &g_edicts[ 1 + i ];

		if ( !ed->inuse || !ed->entity )
		{
			continue;
		}

		eorg = org - ed->entity->centroid;

		// dot product returns length squared
		if ( ( eorg * eorg ) <= r2 )
		{
			return ed->entity;
		}
	}

	return NULL;
}

const char *G_GetNameForSurface( csurface_t *s )
{
	switch( s->flags & MASK_SURF_TYPE )
	{
	case SURF_TYPE_WOOD:
		return "wood";

	case SURF_TYPE_METAL:
		return "metal";

	case SURF_TYPE_STONE:
		return "stone";

	case SURF_TYPE_CONCRETE:
		return "concrete";

	case SURF_TYPE_DIRT:
		return "dirt";

	case SURF_TYPE_FLESH:
		return "flesh";

	case SURF_TYPE_GRILL:
		return "grill";

	case SURF_TYPE_GLASS:
		return "glass";

	case SURF_TYPE_FABRIC:
		return "fabric";

	case SURF_TYPE_MONITOR:
		return "monitor";

	case SURF_TYPE_GRAVEL:
		return "gravel";

	case SURF_TYPE_VEGETATION:
		return "vegetation";

	case SURF_TYPE_PAPER:
		return "paper";

	case SURF_TYPE_DUCT:
		return "duct";

	case SURF_TYPE_WATER:
		return "water";
	}

	return "";
}

void G_InitDebugLines( void )
{
	*gi.DebugLines = DebugLines;
	*gi.numDebugLines = 0;

	currentVertex = vec_zero;
	vertColor = Vector( 1, 1, 1 );
	vertAlpha = 1;
	vertexIndex = 0;
}

void G_AllocDebugLines( void )
{
	g_numdebuglines = gi.cvar( "g_numdebuglines", "4096", CVAR_LATCH );

	DebugLines = ( debugline_t * )gi.TagMalloc( ( int )g_numdebuglines->value * sizeof( debugline_t ), TAG_GAME );

	G_InitDebugLines();
}

void G_DebugLine( Vector start,Vector end,float r,float g,float b,float alpha )
{
	debugline_t *line;

	if ( !g_numdebuglines )
	{
		return;
	}

	if ( *gi.numDebugLines >= g_numdebuglines->value )
	{
		gi.dprintf( "G_DebugLine: Exceeded MAX_DEBUG_LINES\n" );
		return;
	}

	line = &DebugLines[ *gi.numDebugLines ];
	( *gi.numDebugLines )++;

	VectorCopy( start.vec3(), line->start );
	VectorCopy( end.vec3(), line->end );
	VectorSet( line->color, r, g, b );
	line->alpha = alpha;
}

void G_Color3f( float r,float g,float b )
{
	vertColor = Vector( r, g, b );
}

void G_Color3v( Vector color )
{
	vertColor = color;
}

void G_Color4f( float r,float g,float b,float alpha )
{
	vertColor = Vector( r, g, b );
	vertAlpha = alpha;
}

void G_Color3vf( Vector color,float alpha )
{
	vertColor = color;
	vertAlpha = alpha;
}

void G_BeginLine( void )
{
	currentVertex = vec_zero;
	vertexIndex = 0;
}

void G_Vertex( Vector v )
{
	vertexIndex++;
	if ( vertexIndex > 1 )
	{
		G_DebugLine( currentVertex, v, vertColor[ 0 ], vertColor[ 1 ], vertColor[ 2 ], vertAlpha );
	}
	currentVertex = v;
}

void G_EndLine( void )
{
	currentVertex = vec_zero;
	vertexIndex = 0;
}

void G_DebugBBox( Vector origin,Vector mins,Vector maxs,float r,float g,float b,float alpha )
{
	int i;
	Vector points[8];

	/*
	** compute a full bounding box
	*/
	for ( i = 0; i < 8; i++ )
	{
		Vector tmp;

		if ( i & 1 )
			tmp[0] = origin[0] +  mins[0];
		else
			tmp[0] = origin[0] + maxs[0];

		if ( i & 2 )
			tmp[1] = origin[1] + mins[1];
		else
			tmp[1] = origin[1] + maxs[1];

		if ( i & 4 )
			tmp[2] = origin[2] + mins[2];
		else
			tmp[2] = origin[2] + maxs[2];

		points[i] = tmp;
	}

	G_Color4f( r, g, b, alpha );

	G_BeginLine();
	G_Vertex( points[0] );
	G_Vertex( points[1] );
	G_Vertex( points[3] );
	G_Vertex( points[2] );
	G_Vertex( points[0] );
	G_EndLine();

	G_BeginLine();
	G_Vertex( points[4] );
	G_Vertex( points[5] );
	G_Vertex( points[7] );
	G_Vertex( points[6] );
	G_Vertex( points[4] );
	G_EndLine();

	for ( i = 0; i < 4; i++ )
	{
		G_BeginLine();
		G_Vertex( points[i] );
		G_Vertex( points[4 + i] );
		G_EndLine();
	}
}

//
// LED style digits
//
// ****1***
// *      *		8 == /
// 6     *4
// *    * *
// ****2***
// *  *   *
// 7 *--8 5
// **     *
// ****3***
//

static int Numbers[ 10 ][ 8 ] =
{
	{ 1, 3, 4, 5, 6, 7, 0, 0 },     // 0
	{ 4, 5, 0, 0, 0, 0, 0, 0 },     // 1
	{ 1, 4, 2, 7, 3, 0, 0, 0 },     // 2
	{ 1, 4, 2, 5, 3, 0, 0, 0 },     // 3
	{ 6, 4, 2, 5, 0, 0, 0, 0 },     // 4
	{ 1, 6, 2, 5, 3, 0, 0, 0 },     // 5
	{ 1, 6, 2, 5, 7, 3, 0, 0 },     // 6
	{ 1, 8, 0, 0, 0, 0, 0, 0 },     // 7
	{ 1, 2, 3, 4, 5, 6, 7, 0 },     // 8
	{ 1, 6, 4, 2, 5, 3, 0, 0 },     // 9
};

static float Lines[ 9 ][ 4 ] =
{
	{ 0, 0, 0, 0 },         // Unused
	{ -4, 8, 4, 8 },            // 1
	{ -4, 4, 4, 4 },            // 2
	{ -4, 0, 4, 0 },            // 3
	{ 4, 8, 4, 4 },         // 4
	{ 4, 4, 4, 0 },         // 5
	{ -4, 8, -4, 4 },           // 6
	{ -4, 4, -4, 0 },           // 7
	{ 4, 8, -4, 0 },            // 8
};

void G_DrawDebugNumber( Vector origin,int number,float scale,float r,float g,float b )
{
	int i;
	int j;
	int l;
	int num;
	Vector up;
	Vector right;
	Vector pos;
	Vector start;
	Vector ang;
	str text;
	Vector delta;

	// only draw entity numbers within a certain radius
	delta = Vector( g_edicts[ 1 ].s.origin ) - origin;
	if ( ( delta * delta ) > ( 1000 * 1000 ) )
	{
		return;
	}

	G_Color4f( r, g, b, 1.0 );

	ang = game.clients[ 0 ].ps.viewangles;
	ang.AngleVectors( NULL, &right, &up );

	up *= scale;
	right *= scale;

	text = va( "%d", number );
	start = origin - ( text.length() - 1 ) * 5 * right;

	for( i = 0; i < text.length(); i++ )
	{
		num = text[ i ] - '0';
		for( j = 0; j < 8; j++ )
		{
			l = Numbers[ num ][ j ];
			if ( l == 0 )
			{
				break;
			}

			G_BeginLine();

			pos = start + Lines[ l ][ 0 ] * right + Lines[ l ][ 1 ] * up;
			G_Vertex( pos );

			pos = start + Lines[ l ][ 2 ] * right + Lines[ l ][ 3 ] * up;
			G_Vertex( pos );

			G_EndLine();
		}

		start += 10 * right;
	}
}

Vector G_CalculateImpulse( Vector start,Vector end,float speed,float gravity )
{
	float traveltime, vertical_speed;
	Vector dir, xydir, velocity;

	dir = end - start;
	xydir = dir;
	xydir.z = 0;
	traveltime = xydir.length() / speed;
	vertical_speed = ( dir.z / traveltime ) + ( 0.5f * gravity * sv_gravity->value * traveltime );
	xydir.normalize();

	velocity = speed * xydir;
	velocity.z = vertical_speed;
	return velocity;
}

Vector G_PredictPosition( Vector start,Vector target,Vector targetvelocity,float speed )
{
	Vector projected;
	float traveltime;
	Vector dir, xydir;

	dir = target - start;
	xydir = dir;
	xydir.z = 0;
	traveltime = xydir.length() / speed;
	projected = target + ( targetvelocity * traveltime );

	return projected;
}

const char *ExpandLocation( const char *location )
{
	if ( !strcmpi( location, "all" ) )
		return NULL;

	if ( !strnicmp( location, "torso", 5 ) )
	{
		if ( location[6] == 'u' )
		{
			return ( "upper chest" );
		}
		else if (location[6] == 'l')
		{
			return ( "lower chest" );
		}
		else
		{
			return ( "chest" );
		}
	}
	else if ( !strnicmp( location, "leg", 3 ) )
	{
		if ( location[9] == 'u' )
		{
			return ( "upper leg" );
		}
		else if (location[9] == 'l')
		{
			return ( "lower leg" );
		}
		else
		{
			return ( "leg" );
		}
	}
	else if ( !strnicmp( location, "arm", 3 ) )
	{
		return ( "arm" );
	}
	else if ( !strnicmp( location, "head", 4 ) )
	{
		return ( "head" );
	}

	return NULL;
}

char *ClientTeam( edict_t *ent )
{
	static char value[512];

	value[0] = 0;

	if (!ent->client)
		return value;

//### MFD
	if(deathmatch->value == DEATHMATCH_MFD || deathmatch->value == DEATHMATCH_MOB)
	{
		if(ent->client->resp.informer)
			strcpy( value, "informer" );
		else
			strcpy( value, "mobster" );
	}
	else
	{
//###
		if ( DM_FLAG( DF_MODELTEAMS ) )
			COM_StripExtension( Info_ValueForKey( ent->client->pers.userinfo, "model" ), value );
		else if ( DM_FLAG( DF_SKINTEAMS ) )
			COM_StripExtension( Info_ValueForKey( ent->client->pers.userinfo, "skin" ), value );
	} //###

	return( value );
}


qboolean OnSameTeam( Entity *ent1,Entity *ent2 )
{
	char ent1Team [512];
	char ent2Team [512];

	if ( !DM_FLAG( DF_MODELTEAMS | DF_SKINTEAMS ) )
//### MFD
		if((!DM_FLAG( DF_MODELTEAMS|DF_SKINTEAMS )) &&
		   (deathmatch->value != DEATHMATCH_MFD && deathmatch->value != DEATHMATCH_MOB)
		   )
			return false;

	strcpy ( ent1Team, ClientTeam ( ent1->edict ));
	strcpy ( ent2Team, ClientTeam ( ent2->edict ));

	if ( !strcmp( ent1Team, ent2Team ) )
		return true;

	return false;
}

/*
   ==============
   G_LoadAndExecScript

   Like the man says...
   ==============
 */
void G_LoadAndExecScript( const char *filename,const char *label )
{
	ScriptThread *pThread;

	if ( gi.LoadFile( filename, NULL, 0 ) != -1 )
	{
		pThread = Director.CreateThread( filename, LEVEL_SCRIPT, label );
		if ( pThread )
		{
			// start right away
			pThread->Start( -1 );
		}
		else
		{
			gi.dprintf( "G_LoadAndExecScript : %s could not create thread.", filename );
		}
	}
}

ScriptThread * ExecuteThread( str thread_name,qboolean start )
{
	GameScript * s;

	if ( thread_name.length() )
	{
		ScriptThread * pThread;

		s = ScriptLib.GetScript( ScriptLib.GetGameScript() );
		if ( !s )
		{
			gi.dprintf( "StartThread::Null game script\n" );
			return false;
		}
		pThread = Director.CreateThread( s, thread_name.c_str() );
		if ( pThread )
		{
			if ( start )
			{
				// start right away
				pThread->Start( -1 );
			}
		}
		else
		{
			gi.dprintf( "StartThread::unable to go to %s\n", thread_name.c_str() );
			return NULL;
		}
		return pThread;
	}
	return NULL;
}

/*
   ==============
   G_ArchiveEdict
   ==============
 */
void G_ArchiveEdict( Archiver &arc,edict_t *edict )
{
	assert( edict );

	if ( edict->client )
	{
		arc.WriteRaw( edict->client, sizeof( *edict->client ) );
	}

	arc.WriteVector( Vector( edict->s.origin ) );
	arc.WriteVector( Vector( edict->s.angles ) );

	arc.WriteQuat( Quat( edict->s.quat ) );
	arc.WriteQuat( Quat( edict->s.mat ) );

	arc.WriteVector( Vector( edict->s.old_origin ) );
	arc.WriteInteger( edict->s.modelindex );
	arc.WriteInteger( edict->s.frame );
	arc.WriteInteger( edict->s.prevframe );

	arc.WriteVector( Vector( edict->s.vieworigin ) );
	arc.WriteVector( Vector( edict->s.viewangles ) );

	arc.WriteInteger( edict->s.anim );
	arc.WriteFloat( edict->s.scale );
	arc.WriteFloat( edict->s.alpha );
	arc.WriteFloat( edict->s.color_r );
	arc.WriteFloat( edict->s.color_g );
	arc.WriteFloat( edict->s.color_b );
	arc.WriteInteger( edict->s.radius );
	arc.WriteRaw( &edict->s.bone, sizeof( edict->s.bone ) );
	arc.WriteInteger( edict->s.parent );
	arc.WriteInteger( edict->s.numgroups );
	arc.WriteRaw( &edict->s.groups, sizeof( edict->s.groups ) );
	arc.WriteInteger( edict->s.gunanim );
	arc.WriteInteger( edict->s.gunframe );
	// index into configstrings
	arc.WriteInteger( edict->s.gunmodelindex );
	arc.WriteInteger( edict->s.lightofs );
	arc.WriteInteger( edict->s.skinnum );
	arc.WriteInteger( edict->s.effects );
	arc.WriteInteger( edict->s.renderfx );
	arc.WriteInteger( edict->s.solid );
	// index into configstrings
	arc.WriteInteger( edict->s.sound );
//   arc.WriteInteger( edict->s.event );

	arc.WriteInteger( edict->svflags );
	arc.WriteVector( Vector( edict->mins ) );
	arc.WriteVector( Vector( edict->maxs ) );
	arc.WriteVector( Vector( edict->absmin ) );
	arc.WriteVector( Vector( edict->absmax ) );
	arc.WriteVector( Vector( edict->size ) );
	arc.WriteVector( Vector( edict->fullmins ) );
	arc.WriteVector( Vector( edict->fullmaxs ) );
	arc.WriteFloat( edict->fullradius );
	arc.WriteVector( Vector( edict->centroid ) );
	arc.WriteInteger( ( int )edict->solid );
	arc.WriteInteger( edict->clipmask );
	if ( edict->owner )
	{
		// s.number may be cleared out, so write the actual number
		arc.WriteInteger( edict->owner - g_edicts );
	}
	else
	{
		arc.WriteInteger( -1 );
	}

	arc.WriteFloat( edict->freetime );
	arc.WriteFloat( edict->spawntime );
	arc.WriteString( str( edict->entname ) );
}

/*
   ==============
   G_UnarchiveEdict
   ==============
 */
void G_UnarchiveEdict( Archiver &arc,edict_t *edict )
{
	Vector tempvec;
	str tempstr;
	Quat q;
	int temp;

	assert( edict );

	//
	// edict will already be setup as far as entnum is concerned
	//
	if ( edict->client )
	{
		arc.ReadRaw( edict->client, sizeof( *edict->client ) );
	}

	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->s.origin );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->s.angles );

	q = arc.ReadQuat();
	edict->s.quat[ 0 ] = q.x;
	edict->s.quat[ 1 ] = q.y;
	edict->s.quat[ 2 ] = q.z;
	edict->s.quat[ 3 ] = q.w;

	q = arc.ReadQuat();
	QuatToMat( q.vec4(), edict->s.mat );

	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->s.old_origin );
	arc.ReadInteger( &edict->s.modelindex );
	arc.ReadInteger( &edict->s.frame );
	arc.ReadInteger( &edict->s.prevframe );

	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->s.vieworigin );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->s.viewangles );

	arc.ReadInteger( &edict->s.anim );
	arc.ReadFloat( &edict->s.scale );
	arc.ReadFloat( &edict->s.alpha );
	arc.ReadFloat( &edict->s.color_r );
	arc.ReadFloat( &edict->s.color_g );
	arc.ReadFloat( &edict->s.color_b );
	arc.ReadInteger( &edict->s.radius );
	arc.ReadRaw( &edict->s.bone, sizeof( edict->s.bone ) );
	arc.ReadInteger( &edict->s.parent );
	arc.ReadInteger( &edict->s.numgroups );
	arc.ReadRaw( &edict->s.groups, sizeof( edict->s.groups ) );
	arc.ReadInteger( &edict->s.gunanim );
	arc.ReadInteger( &edict->s.gunframe );
	// index into configstrings
	arc.ReadInteger( &edict->s.gunmodelindex );
	arc.ReadInteger( &edict->s.lightofs );
	arc.ReadInteger( &edict->s.skinnum );
	arc.ReadInteger( &edict->s.effects );
	arc.ReadInteger( &edict->s.renderfx );
	arc.ReadInteger( &edict->s.solid );
	// index into configstrings
	arc.ReadInteger( &edict->s.sound );
//   arc.ReadInteger( &edict->s.event );

	arc.ReadInteger( &edict->svflags );

	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->mins );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->maxs );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->absmin );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->absmax );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->size );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->fullmins );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->fullmaxs );
	arc.ReadFloat( &edict->fullradius );
	tempvec = arc.ReadVector();
	tempvec.copyTo( edict->centroid );

	edict->solid = ( solid_t )arc.ReadInteger();
	arc.ReadInteger( &edict->clipmask );

	temp = arc.ReadInteger();
	if ( temp < 0 )
	{
		edict->owner = NULL;
	}
	else
	{
		edict->owner = &g_edicts[ temp ];
	}

	arc.ReadFloat( &edict->freetime );
	arc.ReadFloat( &edict->spawntime );
	tempstr = arc.ReadString();
	strcpy( edict->entname, tempstr.c_str() );

	gi.linkentity( edict );
}

/*
   ================
   G_Milliseconds
   ================
 */
int G_Milliseconds( void )
{
#ifdef _WIN32
	static int base;
	static qboolean initialized = false;

	if ( !initialized )
	{
		// let base retain 16 bits of effectively random data
		base = timeGetTime() & 0xffff0000;
		initialized = true;
	}

	return timeGetTime() - base;
#else
	//FIXME
	return 0;
#endif
}

/*
   ===============
   G_DebugPrintf

   Outputs a string to the debug window
   ===============
 */
void G_DebugPrintf( const char *fmt,... )
{
	va_list argptr;
	char message[ 1024 ];

	va_start( argptr, fmt );
	vsprintf( message, fmt, argptr );
	va_end( argptr );

#ifdef _WIN32
	OutputDebugString( message );
#else
	gi.dprintf( message );
#endif
}

//### M_CheckBottom for walking on the ceiling
qboolean M_CeilingCheckBottom( Entity *ent )
{
	Vector mins, maxs, start, stop;
	trace_t trace;
	int x, y;
	float mid, bottom;
	const gravityaxis_t &grav = gravity_axis[ ent->gravaxis ];

	mins = ent->worldorigin + ent->mins * 0.5;
	maxs = ent->worldorigin + ent->maxs * 0.5;

	// if all of the points under the corners are solid world, don't bother
	// with the tougher checks
	// the corners must be within 16 of the midpoint
	start[ 2 ] = maxs[ 2 ] + 1;

	for( x = 0; x <= 1; x++ )
	{
		for( y = 0; y <= 1; y++ )
		{
			start[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
			start[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];
			if ( gi.pointcontents( start.vec3() ) != CONTENTS_SOLID )
			{
				goto realcheck;
			}
		}
	}

	c_yes++;
	return true;        // we got out easy

realcheck:

	c_no++;

	//
	// check it for real...
	//
	start[ 2 ] = maxs[ 2 ];

	// the midpoint must be within 16 of the bottom
	start[ 0 ] = stop[ 0 ] = ( mins[ 0 ] + maxs[ 0 ] ) * 0.5;
	start[ 1 ] = stop[ 1 ] = ( mins[ 1 ] + maxs[ 1 ] ) * 0.5;
	stop[ 2 ] = start[ 2 ] + 3 * STEPSIZE;//2 * STEPSIZE;

	trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, "M_AxisCheckBottom 1" );

	if(trace.fraction == 1.0)
	{
		return false;
	}

	mid = bottom = trace.endpos[ 2 ];

	// the corners must be within 16 of the midpoint
	for( x = 0; x <= 1; x++ )
	{
		for( y = 0; y <= 1; y++ )
		{
			start[ 0 ] = stop[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
			start[ 1 ] = stop[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];

			trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, "M_AxisCheckBottom 2" );

//			if(trace.fraction != 1.0 && trace.endpos[2] > bottom)
			if(trace.fraction != 1.0 && trace.endpos[2] < bottom)
			{
				bottom = trace.endpos[ 2 ];
			}

//			if ( trace.fraction == 1.0 || mid - trace.endpos[ 2 ] > STEPSIZE )
			if ( trace.fraction == 1.0 || trace.endpos[ 2 ] - mid > STEPSIZE )
			{
				return false;
			}
		}
	}

	c_yes++;
	return true;
}
//###