//-----------------------------------------------------------------------------
//
//  $Logfile:: /Code/DLLs/game/g_utils.cpp                                   $
// $Revision:: 84                                                             $
//   $Author:: Steven                                                         $
//     $Date:: 4/16/03 6:29p                                                  $
//
// 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 "_pch_cpp.h"
#include "g_utils.h"
#include "ctype.h"
#include "worldspawn.h"
#include "scriptmaster.h"
#include "player.h"
#include "PlayerStart.h"
#include "mp_manager.hpp"
#include <qcommon/gameplaymanager.h>

char means_of_death_strings[ MOD_TOTAL_NUMBER ][ 32 ] =
{
	"none",
	"drown",
	"suicide",
	"crush",
	"crush_every_frame",
	"telefrag",
	"lava",
	"slime",
	"falling",
	"last_self_inflicted",
	"explosion",
	"explodewall",
	"electric",
	"electricwater",
	"thrownobject",
	"beam",
	"rocket",
	"impact",
	"gas",
	"gas_blockable",
	"acid",
	"sword",
	"plasma",
	"plasmabeam",
	"plasmashotgun",
	"sting",
	"sting2",
	"sling",
	"bullet",
	"fast_bullet",
	"vehicle",
	"fire",
	"fire_blockable",
	"vortex",
	"lifedrain",
	"flashbang",
	"poo_explosion",
	"axe",
	"chainsword",
	"on_fire",
	"firesword",
	"electricsword",
	"circleofprotection",
	"radiation",
	"lightsword",
	"gib",
	"impale",
	"uppercut",
	"poison",
	"eat",
	"redemption",
	"stasis",
	
	// Added for EF
	"phaser",
	"vaporize",
	"comp_rifle",
	"vaporize_comp",
	"imod_primary",
	"imod_secondary",
	"small_explosion",
	"tetryon",
	"disruptor",	
	"vaporize_disruptor",
	"vaporize_photon",
	"sniper",
	"melee",
	"alien_melee",
	"klingon_melee",
	"turret",
	
	// Powerups/runes
		
	"deathQuad",
	"empathyShield",
	"armorPiercing"
};

char soundtype_strings[ SOUNDTYPE_TOTAL_NUMBER ][ 32 ] =
{
	// General Sound Types
	"none",
	"general",
	"explosion",
	"weaponfire",
	"alert",
	"footsteps_walk",
	"footsteps_run",
	"fall",
		
	// Context Dialog Sound Types
	"dialog_context_spotted_enemy",
	"dialog_context_injured",
	"dialog_context_in_combat",
	"dialog_context_weapon_useless",
	"dialog_context_investigating"
};

char context_strings[CONTEXT_TOTAL_NUMBER][32] =
{
	"spotted_enemy",
	"injured",
	"in_combat",
	"weapon_useless",
	"investigating"
};

int Soundtype_string_to_int ( const str &soundtype_string )
{
	for (int i = 0 ; i < SOUNDTYPE_TOTAL_NUMBER ; i++ )
	{
		if ( !soundtype_string.icmp( soundtype_strings[ i ] ) )
			return i;
	}
	
	gi.WDPrintf( "Unknown soundtype - %s\n", soundtype_string.c_str() );
	return -1;
}

int Context_string_to_int ( const str &context_string )
{
	for (int i = 0 ; i < CONTEXT_TOTAL_NUMBER ; i++ )
	{
		if ( !context_string.icmp( context_strings[ i ] ) )
			return i;
	}
	
	gi.WDPrintf( "Unknown context - %s\n", context_string.c_str() );
	return -1;
}

int MOD_NameToNum( const str &meansOfDeath )
{
	int i;
	
	for ( i = 0 ; i < MOD_TOTAL_NUMBER ; i++ )
	{
		if ( !meansOfDeath.icmp( means_of_death_strings[ i ] ) )
			return i;
	}
	
	gi.WDPrintf( "Unknown means of death - %s\n", meansOfDeath.c_str() );
	return -1;
}

const char *MOD_NumToName( int meansOfDeath )
{
	if ( ( meansOfDeath > MOD_TOTAL_NUMBER ) || ( meansOfDeath < 0 ) )
	{
		gi.WDPrintf( "Unknown means of death num - %d\n", meansOfDeath );
		return "";
	}

	return means_of_death_strings[ meansOfDeath ];
}

qboolean MOD_matches( int incoming_damage, int damage_type )
{
	if ( damage_type == -1 )
	{
		return true;
	}
	
	// special case the sword
	if ( damage_type == MOD_SWORD )
	{
		if (
            ( incoming_damage == MOD_SWORD ) ||
            ( incoming_damage == MOD_ELECTRICSWORD ) ||
            ( incoming_damage == MOD_LIGHTSWORD ) ||
            ( incoming_damage == MOD_FIRESWORD )
			)
		{
			return true;
		}
	}
	else if ( damage_type == incoming_damage )
	{
		return true;
	}
	
	return false;
}

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

============
*/
void G_TouchTriggers( Entity *ent )
{
	int		i;
	int		num;
	int      touch[ MAX_GENTITIES ];
	gentity_t  *hit;
	Event		*ev;
	
	// dead things don't activate triggers!
	if ( ( ent->client || ( ent->edict->svflags & SVF_MONSTER ) ) && ( ent->health <= 0.0f ) )
	{
		return;
	}
	
	// Get a list of g_entity_t's that this entity touched
	num = gi.AreaEntities( ent->absmin, ent->absmax, touch, MAX_GENTITIES, qfalse );
	
	Container<EntityPtr>	&lastTouchedList	= ent->GetLastTouchedList();
	Container<EntityPtr>	 newTouchedList ;
	Container<EntityPtr>	 newLastTouchedList ;
	int						 numTouchedEntities	= lastTouchedList.NumObjects();
	int						 touchedEntityIdx	= 1 ;
	
	// Make a new list of entities that are valid touch targets
	for( i = 0; i < num; i++ )
	{
		hit = &g_entities[ touch[ i ] ];
		if ( !hit->inuse || ( hit->entity == ent ) || ( hit->solid != SOLID_TRIGGER ) ) continue;
		
		assert( hit->entity );
		newTouchedList.AddObject( hit->entity );
	}
	
	// If there are entities in our lastTouchedList that we are no longer touching, notify them
	for ( touchedEntityIdx=1; touchedEntityIdx <= numTouchedEntities; ++touchedEntityIdx )
	{
		Entity *touchedEntity = lastTouchedList.ObjectAt( touchedEntityIdx );
		if (!touchedEntity) continue ;
		if ( !newTouchedList.ObjectInList( touchedEntity ) )
		{
			ev = new Event ( EV_LostContact ); // call me!
			ev->AddEntity( ent );
			touchedEntity->ProcessEvent( ev );
		}
		else
		{
			ev = new Event ( EV_Touch );
			ev->AddEntity( ent );
			touchedEntity->ProcessEvent( ev );
			newTouchedList.RemoveObject( touchedEntity );
			newLastTouchedList.AddObject( touchedEntity );
		}
	}
	
	
	// For each new object we're touching, notify it, and add it to our last touched list.
	lastTouchedList.ClearObjectList();
	numTouchedEntities = newTouchedList.NumObjects();
	for ( touchedEntityIdx = 1; touchedEntityIdx <= numTouchedEntities; ++touchedEntityIdx )
	{
		Entity *touchedEntity = newTouchedList.ObjectAt( touchedEntityIdx );
		lastTouchedList.AddObject( touchedEntity );
		
		// This is for legacy reasons
		ev = new Event( EV_Touch );
		ev->AddEntity( ent );
		touchedEntity->ProcessEvent( ev );
		
		ev = new Event( EV_Contact );
		ev->AddEntity( ent );
		touchedEntity->ProcessEvent( ev );
	}
	
	numTouchedEntities = newLastTouchedList.NumObjects();
	for ( touchedEntityIdx = 1; touchedEntityIdx <= numTouchedEntities; ++touchedEntityIdx )
	{
		lastTouchedList.AddObject( newLastTouchedList.ObjectAt( touchedEntityIdx ) );
	}
}

void G_TouchTeleporters( Entity *ent )
{
	int		i;
	int		num;
	int      touch[ MAX_GENTITIES ];
	gentity_t  *hit;
	Event		*ev;
	
	// dead things don't activate triggers!

	if ( ( ent->client || ( ent->edict->svflags & SVF_MONSTER ) ) && ( ent->health <= 0.0f ) )
	{
		return;
	}
	
	// Get a list of g_entity_t's that this entity touched

	num = gi.AreaEntities( ent->absmin, ent->absmax, touch, MAX_GENTITIES, qfalse );
	
	// Touch all in the list that are teleporters

	for( i = 0 ; i < num ; i++ )
	{
		hit = &g_entities[ touch[ i ] ];

		if ( !hit->inuse || ( hit->entity == ent ) || ( hit->solid != SOLID_TRIGGER ) ) 
			continue;
		
		if ( hit->entity && hit->entity->isSubclassOf( Teleporter ) )
		{
			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;
	int      touch[ MAX_GENTITIES ];
	gentity_t  *hit;
	Event		*ev;
	
	num = gi.AreaEntities( ent->absmin, ent->absmax, touch, MAX_GENTITIES, qfalse );
	
	// 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 = &g_entities[ 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 );
	}
}

void G_ShowTrace( const trace_t *trace, gentity_t *passent, const char *reason, Vector &start, Vector &end )
{
	str text;
	str pass;
	str hit;
	Vector delta;
	float dist;
	
	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";
	}
	
	delta = end - start;
	dist = delta.length();
	
	text = va( "%0.2f : Pass %s Frac %f Hit %s len %f: '%s'\n",
		level.time, pass.c_str(), trace->fraction, hit.c_str(), dist, reason ? reason : "" );
	
	if ( sv_traceinfo->integer == 3 )
	{
		gi.DebugPrintf( text.c_str() );
	}
	else
	{
		gi.DPrintf( "%s", text.c_str() );
	}
}

void G_CalcBoundsOfMove( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, Vector *minbounds,
		Vector *maxbounds )
{
	Vector bmin;
	Vector bmax;
	
	ClearBounds( bmin, bmax );
	AddPointToBounds( ( Vector )start, bmin, bmax );
	AddPointToBounds( ( Vector )end, bmin, bmax );
	bmin += mins;
	bmax += maxs;
	
	if ( minbounds )
	{
		*minbounds = bmin;
	}
	
	if ( maxbounds )
	{
		*maxbounds = bmax;
	}
}

trace_t G_FullTrace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, gentity_t *passent, int contentmask,
		qboolean cylinder, const char *reason )
{
	return G_Trace( start, mins, maxs, end, passent, contentmask, cylinder, reason, true );
}

trace_t G_FullTrace( const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, Entity *passent,
		int contentmask, qboolean cylinder, const char *reason )
{
	return G_Trace( start, mins, maxs, end, passent, contentmask, cylinder, reason, true );
}

trace_t G_Trace( vec3_t start, const vec3_t mins, const vec3_t maxs, vec3_t end, gentity_t *passent, int contentmask,
		qboolean cylinder, const char *reason, qboolean fulltrace )
{
	int entnum;
	trace_t trace;
	
	if ( passent )
	{
		entnum = passent->s.number;
	}
	else
	{
		entnum = ENTITYNUM_NONE;
	}
	
	if ( fulltrace )
		gi.fulltrace( &trace, start, mins, maxs, end, entnum, contentmask, cylinder );
	else
		gi.trace( &trace, start, mins, maxs, end, entnum, contentmask, cylinder );
	
	if ( trace.entityNum == ENTITYNUM_NONE )
	{
		trace.ent = NULL;
	}
	else
	{
		trace.ent = &g_entities[ trace.entityNum ];
	}
	
	if ( sv_traceinfo->integer > 1 )
	{
		Vector start_vector = start;
		Vector end_vector   = end;
		
		G_ShowTrace( &trace, passent, reason, start_vector, end_vector );
	}
	sv_numtraces++;
	
	if ( sv_drawtrace->integer )
	{
		G_DebugLine( Vector( start ), Vector( end ), 1.0f, 1.0f, 0.0f, 1.0f );
	}
	
	return trace;
}

trace_t G_Trace( const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, const Entity *passent,
		int contentmask, qboolean cylinder, const char *reason, qboolean fulltrace )
{
	gentity_t *ent;
	int entnum;
	trace_t trace;
	
	assert( reason );
	
	if ( passent == NULL )
	{
		ent = NULL;
		entnum = ENTITYNUM_NONE;
	}
	else
	{
		ent = passent->edict;
		entnum = ent->s.number;
	}
	
	if ( fulltrace )
		gi.fulltrace( &trace, ( Vector )start, ( Vector )mins, ( Vector )maxs, ( Vector )end, entnum, contentmask, cylinder );
	else
		gi.trace( &trace, ( Vector )start, ( Vector )mins, ( Vector )maxs, ( Vector )end, entnum, contentmask, cylinder );
	
	if ( trace.entityNum == ENTITYNUM_NONE )
	{
		trace.ent = NULL;
	}
	else
	{
		trace.ent = &g_entities[ trace.entityNum ];
	}
	
	if ( sv_traceinfo->integer > 1 )
	{
		Vector start_vector = start;
		Vector end_vector   = end;
		G_ShowTrace( &trace, ent, reason, start_vector, end_vector );
	}
	
	sv_numtraces++;
	
	if ( sv_drawtrace->integer )
	{
		G_DebugLine( start, end, 1.0f, 1.0f, 0.0f, 1.0f );
	}
	
	return trace;
}

void G_TraceEntities( const Vector &start, const Vector &mins, const Vector &maxs, const Vector &end, 
		Container<Entity *>*victimlist, int contentmask )
{
	trace_t trace;
	vec3_t boxmins;
	vec3_t boxmaxs;
	int num;
	int touchlist[MAX_GENTITIES];
	gentity_t *touch;
	int i;
	
	
	// Find the bounding box
	
	for ( i=0 ; i<3 ; i++ )
	{
		if ( end[i] > start[i] )
		{
			boxmins[i] = start[i] + mins[i] - 1.0f;
			boxmaxs[i] = end[i] + maxs[i] + 1.0f;
		}
		else
		{
			boxmins[i] = end[i] + mins[i] - 1.0f;
			boxmaxs[i] = start[i] + maxs[i] + 1.0f;
		}
	}
	
	// Find the list of entites
	
	num = gi.AreaEntities( boxmins, boxmaxs, touchlist, MAX_GENTITIES, qfalse );
	
	for ( i=0 ; i<num ; i++ )
	{
		touch = &g_entities[ touchlist[ i ] ];
		
		// see if we should ignore this entity
		if (touch->solid == SOLID_NOT)
			continue;
		if (touch->solid == SOLID_TRIGGER)
			continue;
		
		gi.ClipToEntity( &trace, ( Vector )start, ( Vector )mins, ( Vector )maxs, ( Vector )end, touchlist[i], contentmask );
		
		if ( trace.entityNum == touchlist[i] )
			victimlist->AddObject( touch->entity );
	}
}

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

Returns the distance to the nearest player from the given spot
================
*/
float	PlayersRangeFromSpot( const Entity *spot )
{
	Entity	*player;
	float		bestplayerdistance;
	Vector	v;
	int		n;
	float		playerdistance;
	
	bestplayerdistance = 9999999.0f;
	for( n = 0; n < maxclients->integer; n++ )
	{
		if ( !g_entities[ n ].inuse || !g_entities[ n ].entity )
		{
			continue;
		}
		
		player = g_entities[ n ].entity;
		if ( player->health <= 0.0f )
		{
			continue;
		}
		
		v = spot->origin - player->origin;
		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( const str &spawnpoint_type )
{
	Entity	*spot, *spot1, *spot2;
	int		count = 0;
	int		selection;
	float		range, range1, range2;
	
	spot = NULL;
	range1 = range2 = 99999;
	spot1 = spot2 = NULL;
	
	for( spot = G_FindClass( spot, spawnpoint_type ); spot ; spot = G_FindClass( spot, spawnpoint_type ) )
	{
		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;
	do
	{
		spot = G_FindClass( spot, spawnpoint_type );
		
		// if there are no more, break out
		if ( !spot )
			break;
		
		if ( ( spot == spot1 ) || ( spot == spot2 ) )
		{
			selection++;
		}
	}
	while( selection-- );
	
	return spot;
}

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

================
*/
Entity *SelectFarthestDeathmatchSpawnPoint( void )
{
	Entity	*bestspot;
	float		bestdistance;
	float		bestplayerdistance;
	Entity	*spot;
	
	spot = NULL;
	bestspot = NULL;
	bestdistance = 0;
	for( spot = G_FindClass( spot, "info_player_deathmatch" ); spot ; spot = G_FindClass( spot, "info_player_deathmatch" ) )
	{
		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
	spot = G_FindClass( NULL, "info_player_deathmatch" );
	
	return spot;
}

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

Chooses a player start, deathmatch start, etc
============
*/
void SelectSpawnPoint( Vector &org, Vector &ang, str &thread )
{
	Entity *spot = NULL;
	
	// find a single player start spot
	
	while( ( spot = G_FindClass( spot, "info_player_start" ) ) != NULL )
	{
		if ( level.spawnpoint.icmp( spot->TargetName() ) == 0 )
		{
			break;
		}
	}
	
	if ( !spot && !level.spawnpoint.length() )
	{
		// there wasn't a spawnpoint without a target, so use any
		spot = G_FindClass( NULL, "info_player_start" );
	}
	
	if ( !spot )
	{
		gi.Error( ERR_DROP, "No player spawn position named '%s'.  Can't spawn player.\n", level.spawnpoint.c_str() );
	}
	
	org = spot->origin;
	ang = spot->angles;
	//
	// see if we have a thread
	//
	if ( spot->isSubclassOf( PlayerStart ) )
	{
		thread = ( ( PlayerStart * )spot )->getThread();
	}
}

/*
=============
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;
	
	mins = ent->origin + ( ent->mins * 0.5f );
	maxs = ent->origin + ( ent->maxs * 0.5f );
	
	// 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.0f;
	
	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, 0 ) != 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.5f;
	start[ 1 ] = stop[ 1 ] = ( mins[ 1 ] + maxs[ 1 ] ) * 0.5f;
	stop[ 2 ] = start[ 2 ] - 3.0f * STEPSIZE;//2 * STEPSIZE;
	
	trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, false, "M_CheckBottom 1" );
	
	if ( trace.fraction == 1.0f )
	{
		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, false, "M_CheckBottom 2" );
		  
			if ( ( trace.fraction != 1.0f ) && ( trace.endpos[ 2 ] > bottom ) )
			{
				bottom = trace.endpos[ 2 ];
			}
			
			if ( ( trace.fraction == 1.0f ) || ( ( mid - trace.endpos[ 2 ] ) > STEPSIZE ) )
			{
				return false;
			}
		}
	}
	*/
	//c_yes++;
	return true;
}

Entity * G_FindClass( const Entity * ent, const char *classname )
{
	int         entnum;
	gentity_t   *from;
	
	if ( ent )
	{
		entnum = ent->entnum;
	}
	else
	{
		entnum = -1;
	}
	
	for ( from = &g_entities[ entnum + 1 ]; from < &g_entities[ globals.num_entities ] ; from++ )
	{
		if ( !from->inuse )
		{
			continue;
		}
		if ( !Q_stricmp ( from->entity->getClassID(), classname ) )
		{
			return from->entity;
		}
	}
	
	return NULL;
}

Entity * G_FindTarget( Entity * ent, const char *name )
{
	Entity      *next;
	
	if ( name && name[ 0 ] )
	{
		if ( name[ 0 ] == '$' )
			name++;
		
		next = world->GetNextEntity( str( name ), ent );
		if ( next )
		{
			return next;
		}
	}
	
	return NULL;
}

Entity *G_NextEntity( const Entity *ent )
{
	gentity_t *from;
	
	if ( !g_entities )
	{
		return NULL;
	}
	
	if ( !ent )
	{
		from = g_entities;
	}
	else
	{
		from = ent->edict + 1;
	}
	
	if ( !from )
	{
		return NULL;
	}
	
	for ( ; from < &g_entities[ globals.num_entities ] ; 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( float angle )
{
	if ( angle == -1.0f )
	{
		return Vector( 0.0f, 0.0f, 1.0f );
	}
	else if ( angle == -2.0f )
	{
		return Vector( 0.0f, 0.0f, -1.0f );
	}
	
	angle *= ( M_PI * 2.0f / 360.0f );
	return Vector( cos( angle ), sin( angle ), 0.0f );
}

/*
=================
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;
	int         touch[ MAX_GENTITIES ];
	gentity_t   *hit;
	Vector      min;
	Vector      max;
	int         fail;
	
	fail = 0;
	
	min = ent->origin + ent->mins;
	max = ent->origin + ent->maxs;
	
	num = gi.AreaEntities( min, max, touch, MAX_GENTITIES, qfalse );
	
	for( i = 0; i < num; i++ )
	{
		hit = &g_entities[ touch[ i ] ];
		
		if ( !hit->inuse || ( hit->entity == ent ) || !hit->entity || ( hit->entity == world ) || ( !hit->entity->edict->solid ) )
		{
			continue;
		}
		
		hit->entity->Damage( ent, ent, hit->entity->health + 100000.0f, ent->origin, vec_zero, vec_zero,
			0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
		
		//
		// if we didn't kill it, fail
		//
		if ( ( hit->entity->getSolidType() != SOLID_NOT ) && ( hit->entity->health > 0.0f ) )
		{
			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;
}

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

Returns entities that have origins within a spherical area

findradius (org, radius)
=================
*/
Entity *findradius( const Entity *startent, const Vector &org, float rad )
{
	Vector		eorg;
	gentity_t	*from;
	float			r2, distance;
	
	if ( !startent )
	{
		from = active_edicts.next;
	}
	else
	{
		from = startent->edict->next;
	}
	
	assert( from );
	if ( !from )
	   {
		return NULL;
	}
	
	assert( ( from == &active_edicts ) || ( from->inuse ) );
	
	// square the radius so that we don't have to do a square root
	r2 = rad * rad;
	
	for( ; from != &active_edicts; from = from->next )
	   {
		assert( from->inuse );
		assert( from->entity );
		
		eorg = org - from->entity->centroid;
		
		// dot product returns length squared
		distance = eorg * eorg;
		
		if ( distance <= r2 )
		{
			return from->entity;
		}
		else
		{
			// subtract the object's own radius from this distance
			distance -= from->radius2;
			if ( distance <= r2 )
			{
				return from->entity;
			}
		}
	}
	
	return NULL;
}

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

Returns clients that have origins within a spherical area

findclientinradius (org, radius)
=================
*/
Entity *findclientsinradius( const Entity *startent, const Vector &org, float rad )
{
	Vector	 eorg;
	gentity_t *ed;
	float		 r2;
	int       i;
	
	// square the radius so that we don't have to do a square root
	r2 = rad * rad;
	
	if ( !startent )
	{
		i = 0;
	}
	else
	{
		i = startent->entnum + 1;
	}
	
	for( ; i < game.maxclients; i++ )
	{
		ed = &g_entities[ 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;
}

Vector G_CalculateImpulse( const Vector &start, const 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_currentGravity->value * traveltime );
	xydir.normalize();
	
	velocity = speed * xydir;
	velocity.z = vertical_speed;
	return velocity;
}

Vector G_PredictPosition( const Vector &start, const Vector &target, const 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;
}

char *ClientTeam( const gentity_t *ent )
{
	static char	value[512];
	
	value[0] = 0;
	
	if (!ent->client)
		return value;
	
	if ( multiplayerManager.checkFlag( MP_FLAG_MODELTEAMS ) )
		COM_StripExtension( Info_ValueForKey( ent->client->pers.userinfo, "model" ), value );
	else if ( multiplayerManager.checkFlag( MP_FLAG_SKINTEAMS ) )
		COM_StripExtension( Info_ValueForKey( ent->client->pers.userinfo, "skin" ), value );
	
	return( value );
}

qboolean OnSameTeam( const Entity *ent1, const Entity *ent2 )
{
	char	ent1Team [512];
	char	ent2Team [512];
	
	if ( !multiplayerManager.checkFlag( MP_FLAG_MODELTEAMS | MP_FLAG_SKINTEAMS ) )
		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...
==============
*/
qboolean G_LoadAndExecScript( const char *filename, const char *label, qboolean quiet )
{
	CThread *pThread;
	
	if ( gi.FS_ReadFile( filename, NULL, quiet ) != -1 )
	{
		pThread = Director.CreateThread( label );
		if ( pThread )
		{
			// start right away
			pThread->Start();
			return true;
		}
		else
		{
			gi.WDPrintf( "G_LoadAndExecScript : %s could not create thread.\n", filename );
			return false;
		}
	}
	
	return false;
}

CThread *ExecuteThread( const str &thread_name, qboolean start, Entity *currentEnt )
{
	if ( thread_name.length() )
	{
		CThread *pThread;
		
		pThread = Director.CreateThread( thread_name.c_str() );
		if ( pThread )
		{
			if ( currentEnt )
				pThread->SetCurrentEntity(currentEnt);
			
			if ( start )
            {
				// start right away
				pThread->Start();
            }
		}
		else
		{
			gi.WDPrintf( "StartThread::unable to go to %s\n", thread_name.c_str() );
			return NULL;
		}
		
		return pThread;
	}
	
	return NULL;
}

/*
==============
G_ArchiveEdict
==============
*/
void G_ArchiveEdict( Archiver &arc, gentity_t *edict )
{
	int    i;
	str    tempStr;
	
	assert( edict );
	
	//
	// this is written funny because it is used for both saving and loading
	//
	
	if ( edict->client )
	{
		arc.ArchiveRaw( edict->client, sizeof( *edict->client ) );
	}

	// Don't archive int number;

	arc.ArchiveInteger( &edict->s.instanceNumber );
	
	arc.ArchiveInteger( &edict->s.eType );
	arc.ArchiveInteger( &edict->s.eFlags );

	// Don't archive
	//trajectory_t	pos;
	//trajectory_t	apos;
	
	arc.ArchiveVec3( edict->s.netorigin );
	arc.ArchiveVec3( edict->s.origin );
	arc.ArchiveVec3( edict->s.origin2 );
	arc.ArchiveVec3( edict->s.netangles );
	arc.ArchiveVec3( edict->s.angles );
	
	arc.ArchiveVec3( edict->s.viewangles );
	
	arc.ArchiveUnsigned( &edict->s.constantLight );
	
	if ( arc.Saving() )
	{
		if ( edict->s.loopSound )
			tempStr = gi.getConfigstring( CS_SOUNDS + edict->s.loopSound );
		else
			tempStr = "";
		
		arc.ArchiveString( &tempStr );
	}
	else
	{
		arc.ArchiveString( &tempStr );
		
		if ( tempStr.length() )
			edict->s.loopSound = gi.soundindex( tempStr.c_str() );
		else
			edict->s.loopSound = 0;
	}
	
	arc.ArchiveFloat( &edict->s.loopSoundVolume );
	arc.ArchiveFloat( &edict->s.loopSoundMinDist );
	
	arc.ArchiveInteger( &edict->s.parent );
	arc.ArchiveInteger( &edict->s.tag_num );
	arc.ArchiveBoolean( &edict->s.attach_use_angles );
	arc.ArchiveVec3( edict->s.attach_offset );
	arc.ArchiveVec3( edict->s.attach_angles_offset );
	
	arc.ArchiveInteger( &edict->s.modelindex );
	arc.ArchiveInteger( &edict->s.viewmodelindex );
	//arc.ArchiveInteger( &edict->s.worldmodelindex );
	arc.ArchiveInteger( &edict->s.skinNum );
	
	if ( arc.Saving() )
	{
		if ( edict->s.customShader )
			tempStr = gi.getConfigstring( CS_IMAGES + edict->s.customShader );
		else
			tempStr = "";
		
		arc.ArchiveString( &tempStr );
	}
	else
	{
		arc.ArchiveString( &tempStr );
		
		if ( tempStr.length() )
			edict->s.customShader = gi.imageindex( tempStr.c_str() );
		else
			edict->s.customShader = 0;
	}

	if ( arc.Saving() )
	{
		if ( edict->s.customEmitter )
			tempStr = gi.getConfigstring( CS_IMAGES + edict->s.customEmitter );
		else
			tempStr = "";
		
		arc.ArchiveString( &tempStr );
	}
	else
	{
		arc.ArchiveString( &tempStr );
		
		if ( tempStr.length() )
			edict->s.customEmitter = gi.imageindex( tempStr.c_str() );
		else
			edict->s.customEmitter = 0;
	}
	
	arc.ArchiveFloat( &edict->s.animationRate );
	arc.ArchiveInteger( &edict->s.anim );
	arc.ArchiveInteger( &edict->s.frame );
	
	arc.ArchiveInteger( &edict->s.crossblend_time );
	
	arc.ArchiveInteger( &edict->s.torso_anim );
	arc.ArchiveInteger( &edict->s.torso_frame );
	arc.ArchiveInteger( &edict->s.torso_crossblend_time );
	
	for( i = 0; i < NUM_BONE_CONTROLLERS; i++ )
	{
		arc.ArchiveInteger( &edict->s.bone_tag[ i ] );
		arc.ArchiveVec3( edict->s.bone_angles[ i ] );
		arc.ArchiveVec4( edict->s.bone_quat[ i ] );
	}
	
	arc.ArchiveRaw( &edict->s.morph_controllers, sizeof( edict->s.morph_controllers ) );
	
	arc.ArchiveRaw( &edict->s.surfaces, sizeof( edict->s.surfaces ) );
	
	arc.ArchiveInteger( &edict->s.clientNum );
	arc.ArchiveInteger( &edict->s.groundEntityNum );
	arc.ArchiveInteger( &edict->s.solid );
	
	arc.ArchiveFloat( &edict->s.scale );
	arc.ArchiveFloat( &edict->s.alpha );
	arc.ArchiveInteger( &edict->s.renderfx );
	
	arc.ArchiveUnsigned( &edict->s.affectingViewModes );

	arc.ArchiveInteger( &edict->s.archeTypeIndex );
	arc.ArchiveInteger( &edict->s.missionObjective );

	arc.ArchiveInteger( &edict->s.infoIcon );
	
	arc.ArchiveRaw( &edict->s.effectsAnims, sizeof( edict->s.effectsAnims ) );
	
	arc.ArchiveInteger( &edict->s.bindparent );
	
	arc.ArchiveVec4( edict->s.quat );
	arc.ArchiveRaw( &edict->s.mat, sizeof( edict->s.mat ) );
	
	arc.ArchiveInteger( &edict->svflags );
	
	arc.ArchiveVec3( edict->mins );
	arc.ArchiveVec3( edict->maxs );
	arc.ArchiveInteger( &edict->contents );
	arc.ArchiveVec3( edict->absmin );
	arc.ArchiveVec3( edict->absmax );
	arc.ArchiveFloat( &edict->radius );
	
	if ( arc.Loading() ) 
	{
		edict->radius2 = edict->radius * edict->radius;
	}
	
	arc.ArchiveVec3( edict->centroid );
	
	arc.ArchiveVec3( edict->currentOrigin );
	arc.ArchiveVec3( edict->currentAngles );
	
	arc.ArchiveInteger( &edict->ownerNum );
	ArchiveEnum( edict->solid, solid_t );
	arc.ArchiveFloat( &edict->freetime );
	arc.ArchiveFloat( &edict->spawntime );
	
	tempStr = str( edict->entname );
	arc.ArchiveString( &tempStr );
	strncpy( edict->entname, tempStr.c_str(), sizeof( edict->entname ) - 1 );
	
	arc.ArchiveInteger( &edict->clipmask );
	
	if ( arc.Loading() )
	{
		gi.linkentity( edict );
	}
}

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

model / sound configstring indexes

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

/*
=======================
G_FindConfigstringIndex
=======================
*/
int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create )
{
	int		i;
	char	   *s;
	
	if ( !name || !name[0] )
	{
		return 0;
	   }
	
	for ( i=1 ; i<max ; i++ )
	{
		s = gi.getConfigstring( start + i );
		if ( !s || !s[0] )
		{
			break;
		}
		if ( !strcmp( s, name ) )
		{
			return i;
		}
	   }
	
	if ( !create )
	{
		return 0;
	   }
	
	if ( i == max )
	{
		gi.Error( ERR_DROP, "G_FindConfigstringIndex: overflow" );
	   }
	
	gi.setConfigstring( start + i, name );
	return i;
}

int G_ModelIndex( const char *name )
{
	return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, true);
}

int G_SoundIndex( const char *name )
{
	return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, true);
}


/*
===============
G_SetTrajectory

Sets the pos trajectory for a fixed position
===============
*/
void G_SetTrajectory( gentity_t *ent, const vec3_t org )
{
	VectorCopy( org, ent->s.pos.trBase );
	ent->s.pos.trType = TR_STATIONARY;
	ent->s.pos.trTime = 0;
	ent->s.pos.trDuration = 0;
	VectorClear( ent->s.pos.trDelta );
	
	VectorCopy( org, ent->currentOrigin );
	VectorCopy( org, ent->s.origin );
}

/*
===============
G_SetConstantLight

Sets the encoded constant light parameter for entities
===============
*/
void G_SetConstantLight( unsigned int * constantlight, const float * red, const float * green, const float * blue,
		const float * radius, const int * lightStyle )
{
	int ir, ig, ib, iradius;
	
	if ( !constantlight )
		return;
	
	ir = (*constantlight) & 255;
	ig = ( (*constantlight) >> 8 ) & 255;
	ib = ( (*constantlight) >> 16 ) & 255;
	iradius = ( (*constantlight) >> 24 ) & 255;
	
	if ( red )
	{
		ir = *red * 255;
		if ( ir > 255 )
			ir = 255;
	}
	
	if ( green )
	{
		ig = *green * 255;
		if ( ig > 255 )
			ig = 255;
	}
	
	if ( blue )
	{
		ib = *blue * 255;
		if ( ib > 255 )
			ib = 255;
	}
	
	if ( radius )
	{
		iradius = *radius / CONSTANTLIGHT_RADIUS_SCALE;
		if ( iradius > 255 )
			iradius = 255;
	}
	
	if ( lightStyle )
	{
		ir = *lightStyle;
		if ( ir > 255 )
			ir = 255;
	}
	*constantlight = ( ir ) + ( ig << 8 ) + ( ib << 16 ) + ( iradius << 24 );
}

//
// caching commands
//
int modelIndex( const char *mdl )
{
	str name;
	
	assert( mdl );
	
	if ( !mdl )
	{
		return 0;
	}
	
	// Prepend 'models/' to make things easier
	//if ( !strchr( mdl, '*' ) && !strchr( mdl, '\\' ) && !strchr( mdl, '/' ) )
	if ( ( strlen( mdl ) > 0 ) && !strchr( mdl, '*' ) && strnicmp( "models/", mdl, 7 ) && strstr( mdl, ".tik" ) )
	{
		name = "models/";
	}
	
	name += mdl;
	
	return gi.modelindex( name.c_str() );
}

void CacheResource( const char * stuff, Entity * ent )
{
	str real_stuff;

	if ( !stuff || !ent )
		return;

	if ( !strchr( stuff, '.' ) )
	{
		// must be a global alias
		stuff = gi.GlobalAlias_FindRandom( stuff );
		if ( !stuff )
		{
			return;
		}
	}

	real_stuff = stuff;
	real_stuff.tolower();

	if ( strstr( real_stuff.c_str(), ".wav" ) )
	{
		gi.soundindex( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".mp3" ) )
	{
		gi.soundindex( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".tik" ) )
	{
		int index;

		index = modelIndex( real_stuff.c_str() );

		if ( index > 0 )
		{
			 if ( !ent )
			 {
				 ent = new Entity;

				 ent->ProcessInitCommands( index, true );

				 delete ent;
				 ent = NULL;
			 }
			 else
			 {
				 ent->ProcessInitCommands( index, true );
			 }
		}
	}
	else if ( strstr( real_stuff.c_str(), ".spr" ) )
	{
		gi.modelindex( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".ska" ) )
	{
		gi.TIKI_CacheAnim( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".st" ) )
	{
		if ( strncmp( real_stuff.c_str(), "ai/", 3 ) == 0 )
		{
			CacheStatemap( stuff, ( Condition<Class> * )Actor::Conditions );
		}
	}
	else if ( strstr( real_stuff.c_str(), ".img" ) )
	{
		real_stuff.CapLength( real_stuff.length() - 4 );
		gi.imageindex( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".itm" ) )
	{
		real_stuff.CapLength( real_stuff.length() - 4 );
		gi.itemindex( real_stuff.c_str() );
	}
	else if ( strstr( real_stuff.c_str(), ".arc" ) )
	{
		real_stuff.CapLength( real_stuff.length() - 4 );
		gi.archetypeindex( real_stuff.c_str() );
	}
}

void G_CacheStateMachineAnims( Entity *ent, const char *stateMachineName )
{
	Container<const char *> animNames;
	const char *animName;
	StateMap *stateMap;
	int i;

	// Cache the statemap

	if ( ent->isSubclassOf( Player ) )
		stateMap = GetStatemap( stateMachineName, ( Condition<Class> * )Player::Conditions, NULL, false, true );
	else if ( ent->isSubclassOf( Actor ) )
		stateMap = GetStatemap( stateMachineName, ( Condition<Class> * )Actor::Conditions, NULL, false, true );
	else
		return;

	// Get all of the anims in the statemachine

	stateMap->GetAllAnims( &animNames );

	// Loop through all of the anims in this statemachine and cache them all

	for ( i = 1 ; i <= animNames.NumObjects() ; i++ )
	{
		animName = animNames.ObjectAt( i );

		// This forces the caching of every anim that matches this anim name

		gi.Anim_Random( ent->edict->s.modelindex, animName );
	}
}

void ChangeMusic( const char *current, const char *fallback, qboolean force )
{
	int      j;
	gentity_t  *other;
	
	if ( current || fallback )
	{
		for( j = 0; j < game.maxclients; j++ )
		{
			other = &g_entities[ j ];
			if ( other->inuse && other->client )
			{
				Player *client;
				
				client = ( Player * )other->entity;
				client->ChangeMusic( current, fallback, force );
			}
		}
		if ( current && fallback )
		{
			gi.DPrintf( "music set to %s with fallback %s\n", current, fallback );
		}
	}
}

void ChangeMusicVolume( float volume, float fade_time )
{
	int      j;
	gentity_t  *other;
	
	for( j = 0; j < game.maxclients; j++ )
	{
		other = &g_entities[ j ];
		if ( other->inuse && other->client )
		{
			Player *client;
			
			client = ( Player * )other->entity;
			client->ChangeMusicVolume( volume, fade_time );
		}
	}
	gi.DPrintf( "music volume set to %.2f, fade time %.2f\n", volume, fade_time );
}

void RestoreMusicVolume( float fade_time )
{
	int      j;
	gentity_t  *other;
	
	for( j = 0; j < game.maxclients; j++ )
	{
		other = &g_entities[ j ];
		if ( other->inuse && other->client )
		{
			Player *client;
			
			client = ( Player * )other->entity;
			client->RestoreMusicVolume( fade_time );
		}
	}
}

//================================================================
// Name:			G_AllowMusicDucking
// Class:			
//
// Description:		Tells each player whether or not music ducking is allowed
//
// Parameters:		bool allowMusicDucking				- whether or not music ducking is allowed
//
// Returns:			none
//================================================================

void G_AllowMusicDucking( bool allowMusicDucking )
{
	int      j;
	gentity_t  *other;

	for( j = 0; j < game.maxclients; j++ )
	{
		other = &g_entities[ j ];

		if ( other->inuse && other->client )
		{
			Player *player;

			player = ( Player * )other->entity;
			player->allowMusicDucking( allowMusicDucking );
		}
	}
}

//================================================================
// Name:			G_AllowActionMusic
// Class:			
//
// Description:		Tells each player whether or not action music is allowed
//
// Parameters:		bool allowActionMusic				- whether or not action music is allowed
//
// Returns:			none
//================================================================

void G_AllowActionMusic( bool allowActionMusic )
{
	int      j;
	gentity_t  *other;

	for( j = 0; j < game.maxclients; j++ )
	{
		other = &g_entities[ j ];

		if ( other->inuse && other->client )
		{
			Player *player;

			player = ( Player * )other->entity;
			player->allowActionMusic( allowActionMusic );
		}
	}
}


void ChangeSoundtrack( const char * soundtrack )
{
	level.saved_soundtrack = level.current_soundtrack;
	level.current_soundtrack = soundtrack;
	
	gi.setConfigstring( CS_SOUNDTRACK, soundtrack );
	gi.DPrintf( "soundtrack switched to %s.\n", soundtrack );
}

void RestoreSoundtrack( void )
{
	if ( level.saved_soundtrack.length() )
	{
		level.current_soundtrack = level.saved_soundtrack;
		level.saved_soundtrack = "";
		gi.setConfigstring( CS_SOUNDTRACK, level.current_soundtrack.c_str() );
		gi.DPrintf( "soundtrack restored %s.\n", level.current_soundtrack.c_str() );
	}
}

void G_BroadcastSound( Entity *soundent, const Vector &origin, float radius, int soundType )
{
	Sentient *ent;
	Vector	delta;
	Event		*ev;
	str		name;
	float    r2;
	float    dist2;
	int		i;
	int		n;

/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment
	
#if 0
	int		count;
	  
	count = 0;
#endif
		
****************************************************************************/
	
	
	//Putting in the mechanisims for a SoundType within Broadcast sound
	//However it won't actually do anything yet.
	assert( soundent );
	if ( soundent && !( soundent->flags & FL_NOTARGET ) )
	{
		r2 = radius * radius;
		n = SentientList.NumObjects();
		for( i = 1; i <= n; i++ )
		{
			ent = SentientList.ObjectAt( i );
			if ( ( ent == soundent ) || ent->deadflag )
            {
				continue;
            }
			
			if ( ent->isSubclassOf( Actor ) )
			{
				delta = origin - ent->centroid;
				
				// dot product returns length squared
				dist2 = delta * delta;
				if (
					( dist2 <= r2 ) &&
					(
					( soundent->edict->areanum == ent->edict->areanum ) ||
					( gi.AreasConnected( soundent->edict->areanum, ent->edict->areanum ) )
					)
					)
					
				{
					ev = new Event( EV_HeardSound );
					ev->AddEntity( soundent );
					ev->AddVector( origin );
					ev->AddInteger( soundType );
					ent->PostEvent( ev, 0.0f );

/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment
					
#if 0
					count++;
#endif
					  
****************************************************************************/
					
				}
				
			}
		}
		
/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment
		
#if 0
		gi.DPrintf( "Broadcast event %s to %d entities\n", ev->getName(), count );
#endif
		  
****************************************************************************/
		
	}
}

void G_BroadcastAlert( Entity *soundent, const Vector &origin, const Vector &enemy,	float radius )
{
	Sentient *ent;
	Vector	delta;
	Event		*ev;
	str		name;
	float    r2;
	float    dist2;
	int		i;
	int		n;
/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment

#if 0
	int		count;

	count = 0;
#endif

****************************************************************************/

	
	//int	   soundType = SOUNDTYPE_ALERT;
	
	assert( soundent );
	if ( soundent && !( soundent->flags & FL_NOTARGET ) )
	{
		r2 = radius * radius;
		n = SentientList.NumObjects();
		for( i = 1; i <= n; i++ )
		{
			ent = SentientList.ObjectAt( i );
			if ( ( ent == soundent ) || ent->deadflag )
            {
				continue;
            }
			
			if ( ent->isSubclassOf( Actor ) )
			{
				delta = origin - ent->centroid;
				
				// dot product returns length squared
				dist2 = delta * delta;
				if (
					( dist2 <= r2 ) &&
					(
					( soundent->edict->areanum == ent->edict->areanum ) ||
					( gi.AreasConnected( soundent->edict->areanum, ent->edict->areanum ) )
					)
					)
					
				{
					ev = new Event( EV_HeardSound );
					ev->AddEntity( soundent );
					ev->AddVector( enemy );
					ent->PostEvent( ev, 0.0f );

/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment

#if 0
						count++;
#endif

****************************************************************************/

				}		
			}
		}

/****************************************************************************
Squirrel : #if 0 / 1 block demoted to comment

#if 0
      gi.DPrintf( "Broadcast event %s to %d entities\n", ev->getName(), count );
#endif

****************************************************************************/

	}
}

void CloneEntity( Entity * dest, const Entity * src )
{
	int     i, num;
	
	dest->setModel( src->model );
	// don't process our init commands
	//dest->CancelEventsOfType( EV_ProcessInitCommands );
	dest->setOrigin( src->origin );
	dest->setAngles( src->angles );
	dest->setScale( src->edict->s.scale );
	dest->setAlpha( src->edict->s.alpha );
	dest->health = src->health;
	// copy the surfaces
	memcpy( dest->edict->s.surfaces, src->edict->s.surfaces, sizeof( src->edict->s.surfaces ) );
	dest->edict->s.constantLight = src->edict->s.constantLight;
	//dest->edict->s.eFlags = src->edict->s.eFlags;
	dest->edict->s.renderfx = src->edict->s.renderfx;
	dest->edict->s.anim = src->edict->s.anim;
	dest->edict->s.frame = src->edict->s.frame;
	
	if ( src->bind_info )
	{
		num = src->bind_info->numchildren;
		for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ )
		{
			Entity * clone;
			Entity * child;
			
			// duplicate the children
			if ( !src->bind_info->children[ i ] )
			{
				continue;
			}
			child = G_GetEntity( src->bind_info->children[ i ] );
			if ( child )
			{
				clone = new Entity( ENTITY_CREATE_FLAG_ANIMATE );
				CloneEntity( clone, child );
				clone->attach( dest->entnum, child->edict->s.tag_num );
			}
			num--;
		}
	}
	dest->ProcessPendingEvents();
}

weaponhand_t WeaponHandNameToNum( const str &side )
{
	if ( !side.length() )
	{
		gi.DPrintf( "WeaponHandNameToNum : Weapon hand not specified\n" );
		return WEAPON_ERROR;
	}
	
	if ( !side.icmp( "righthand" ) || !side.icmp( "right" ) )
	{
		return WEAPON_RIGHT;
	}
	else if ( !side.icmp( "lefthand" ) || !side.icmp( "left" ) )
	{
		return WEAPON_LEFT;
	}
	else if ( !side.icmp( "dualhand" ) || !side.icmp( "dual" ) )
	{
		return WEAPON_DUAL;
	}
	else
	{
		return (weaponhand_t)atoi( side );
	}
}

const char *WeaponHandNumToName( weaponhand_t hand )
{
	switch( hand )
	{
	case WEAPON_RIGHT:
		return "righthand";
	case WEAPON_LEFT:
		return "lefthand";
	case WEAPON_DUAL:
		return "dualhand";
	case WEAPON_ANY:
		return "anyhand";
	default:
		return "Invalid Hand";
	}
}

firemode_t WeaponModeNameToNum( const str &mode )
{
	if ( !mode.length() )
	{
		gi.DPrintf( "WeaponModeNameToNum : Weapon mode not specified\n" );
		return FIRE_ERROR;
	}
	
	if ( !mode.icmp( "primary" ) )
	{
		return FIRE_MODE1;
	}
	
	if ( !mode.icmp( "alternate" ) )
	{
		return FIRE_MODE2;
	}
	
	if ( !mode.icmp( "MODE1" ) || !mode.icmp( "FIRE_MODE1" ) )
	{
		return FIRE_MODE1;
	}
	
	if ( !mode.icmp( "MODE2" ) || !mode.icmp( "FIRE_MODE2" ) )
	{ 
		return FIRE_MODE2;
	}
	
	if ( !mode.icmp( "MODE3" ) || !mode.icmp( "FIRE_MODE3" ) )
	{
		return FIRE_MODE3;
	}
	
	return (firemode_t)atoi( mode );
}

void G_DebugTargets( Entity *e, const str &from )
{
	gi.DPrintf( "DEBUGTARGETS:%s ", from.c_str() );
	
	if ( e->TargetName() && strlen( e->TargetName() ) )
	{
		gi.DPrintf( "Targetname=\"%s\"\n", e->TargetName() );
	}
	else
	{
		gi.DPrintf( "Targetname=\"None\"\n" );
	}
	
	if ( e->Target() && strlen( e->Target() ) )
	{
		gi.DPrintf( "Target=\"%s\"\n", e->Target() );
	}
	else
	{
		gi.DPrintf( "Target=\"None\"\n" );
	}
}

void G_DebugDamage( float damage, Entity *victim, Entity *attacker, Entity *inflictor )
{
	gi.DPrintf( "Victim:%s Attacker:%s Inflictor:%s Damage:%f\n", victim->getClassname(), attacker->getClassname(), inflictor->getClassname(), damage );
}

void G_FadeOut( float delaytime )
{
	// Make sure we are not already faded or fading out

	if ( ( level.m_fade_type == fadeout ) && ( level.m_fade_time_start > 0.0f ) )
	{
		float alpha;

		alpha = 1.0f - ( level.m_fade_time / level.m_fade_time_start );

		if ( alpha > 0.0f )
			return;
	}

	// Fade the screen out
	level.m_fade_color      = Vector( 0.0f, 0.0f, 0.0f );
	level.m_fade_alpha      = 1.0f;
	level.m_fade_time       = delaytime;
	level.m_fade_time_start = delaytime;
	level.m_fade_type       = fadeout;
	level.m_fade_style      = alphablend;
}

void G_AutoFadeIn( void )
{
	level.m_fade_time_start = 1;
	level.m_fade_time       = 1;
	level.m_fade_color[0]   = 0;
	level.m_fade_color[1]   = 0;
	level.m_fade_color[2]   = 0;
	level.m_fade_alpha      = 1;
	level.m_fade_type       = fadein;
	level.m_fade_style      = alphablend;
}

void G_ClearFade( void )
{
	level.m_fade_time = -1;
	level.m_fade_type = fadein;
}

void G_FadeSound( float delaytime )
{
	float time;
	
	// Fade the screen out
	time = delaytime * 1000.0f;
	gi.SendServerCommand( NULL, va( "fadesound %0.2f", time ) );
}

//
// restarts the game after delaytime
//
void G_RestartLevelWithDelay( float delaytime )
{
	int i;
	
	if ( multiplayerManager.inMultiplayer() )
		return;
	
	if ( level.died_already )
		return;
	
	level.died_already = true;
	
	// Restart the level soon
	for( i = 0; i < game.maxclients; i++ )
	   {
		if ( g_entities[ i ].inuse )
		{
			if ( g_entities[ i ].entity )
            {
				g_entities[ i ].entity->PostEvent( EV_Player_Respawn, delaytime );
            }
		}
	}	
}

void G_MissionFailed( const str& reason )
{
	str playerDeathThread;


	// Make sure we haven't already failed the mission

	if ( level.mission_failed )
		return;

	// Make the music system play the failure music for this level

	ChangeMusic( "failure", "normal", true );

	// Set our failure reason in the config string
	gi.failedcondition( reason.c_str() );		

	playerDeathThread = level.getPlayerDeathThread();

	if ( ( strnicmp( reason.c_str(), "PlayerKilled", strlen( "PlayerKilled" ) ) == 0 ) && ( playerDeathThread.length() ) )
	{
		ExecuteThread( playerDeathThread, true, NULL );
	}
	else
	{
		G_FinishMissionFailed();
	}
}

void G_FinishMissionFailed( void )
{
	Entity* entity;
	Player* player;

	// Get the player
	
	entity = g_entities[ 0 ].entity;

	if ( !entity->isSubclassOf( Player ) )
		return;

	player = (Player *)entity;

	// Fade everything out
	G_FadeOut( 1.0f );
	G_FadeSound( 1.0f );
	
	// Fail the mission

	player->setMissionFailed();
	
	level.mission_failed = true;
	level.playerfrozen = true;

	// Get out of letterbox mode if we are in it

	if ( level.m_letterbox_dir == letterbox_in )
	{
		level.m_letterbox_time_start = 0.5f;
		level.m_letterbox_dir = letterbox_out;
		
		level.m_letterbox_time = 0.5f;
		level.m_letterbox_fraction = 1.0f/8.0f;
	}
}

void G_StartCinematic( void )
{
	Entity *entity;

	level.cinematic = true;
	gi.cvar_set( "sv_cinematic", "1" );

	entity = g_entities[ 0 ].entity;

	if ( entity->isSubclassOf( Player ) )
	{
		Player *player = (Player *)entity;

		player->cinematicStarted();
	}
}

void G_StopCinematic( void )
{
	Entity *entity;

	// clear out the skip thread
	world->skipthread = "";
	level.cinematic = false;
	gi.cvar_set( "sv_cinematic", "0" );

	entity = g_entities[ 0 ].entity;

	if ( entity->isSubclassOf( Player ) )
	{
		Player *player = (Player *)entity;

		player->cinematicStopped();
	}
}

int G_NumClients( void )
{
	int i,count=0;
	for( i = 0; i < maxclients->integer; i++ )
	{
		gentity_t *ent = g_entities + i;
		if ( !ent->inuse || !ent->client || !ent->entity )
		{
			continue;
		}
		count++;
	}
	return count;
}

//----------------------------------------------------------------
// Name:			G_DrawXYBox
// Class:			None
//
// Description:		draws a box in the xy plane 
//
// Parameters:		
//					Vector center	-- the point about which the box is centered
//					float width		-- the width of the box
//					float r, g, b	-- the color values for the box
//					float alpha		-- the amount of alpha applied to the box
//
// Returns:			None
//----------------------------------------------------------------
void G_DrawXYBox(const Vector &center, float width, float r, float g, float b, float alpha )
{
	float squareSize=width/2.0f;
	Vector topLeftPoint		= center + Vector(-squareSize, -squareSize, 0);
	Vector topRightPoint	= center + Vector(+squareSize, -squareSize, 0);
	Vector bottomRightPoint	= center + Vector(+squareSize, +squareSize, 0);
	Vector bottomLeftPoint	= center + Vector(-squareSize, +squareSize, 0);
	G_DebugLine( topLeftPoint,		topRightPoint, r, g, b, alpha );
	G_DebugLine( topRightPoint,		bottomRightPoint, r, g, b, alpha );
	G_DebugLine( bottomRightPoint,	bottomLeftPoint, r, g, b, alpha );
	G_DebugLine( bottomLeftPoint,	topLeftPoint, r, g, b, alpha );
}

float G_GetDatabaseFloat( const str &prefix, const str &objectName, const str &varName )
{
	GameplayManager *gpm;
	str gameplayObjectName;

	// Get the gameplay manager

	gpm = GameplayManager::getTheGameplayManager();

	if ( !gpm )
		return 0.0f;

	// Build the object name

	gameplayObjectName = prefix + objectName;

	return gpm->getFloatValue( gameplayObjectName, varName );
}

str G_GetDatabaseString( const str &prefix, const str &objectName, const str &varName )
{
	GameplayManager *gpm;
	str gameplayObjectName;

	// Get the gameplay manager

	gpm = GameplayManager::getTheGameplayManager();

	if ( !gpm )
		return 0.0f;

	// Build the object name

	gameplayObjectName = prefix + objectName;

	return gpm->getStringValue( gameplayObjectName, varName );
}
