#include "../idlib/precompiled.h"
#pragma hdrstop

#include "Game_local.h"

#include "../sys/AutoVersion.h"

struct game_name_s {
	game_name_s( void ) {
		sprintf( string, "Q4MP %s", VERSION_STRING_DOTTED );
	}
	char string[256];
} game_name;

idCVar gamename( "gamename", game_name.string, CVAR_GAME | CVAR_SERVERINFO | CVAR_ROM, "" );

// RAVEN BEGIN
#include "../bse/BSEInterface.h"
#include "Projectile.h"
#include "client/ClientEffect.h"
#include "ai/AI.h"
#include "ai/AI_Manager.h"
#include "ai/AAS_tactical.h"
#include "Game_Log.h"
// RAVEN END

//#define UI_DEBUG	1

#ifdef GAME_DLL

idSys *						sys = NULL;
idCommon *					common = NULL;
idCmdSystem *				cmdSystem = NULL;
idCVarSystem *				cvarSystem = NULL;
idFileSystem *				fileSystem = NULL;
idNetworkSystem *			networkSystem = NULL;
idRenderSystem *			renderSystem = NULL;
idSoundSystem *				soundSystem = NULL;
idRenderModelManager *		renderModelManager = NULL;
idUserInterfaceManager *	uiManager = NULL;
idDeclManager *				declManager = NULL;
idAASFileManager *			AASFileManager = NULL;
idCollisionModelManager *	collisionModelManager = NULL;

// RAVEN BEGIN
// jscott: game interface to the fx system
rvBSEManager *				bse = NULL;
// RAVEN END

idCVar *					idCVar::staticVars = NULL;

// RAVEN BEGIN
// rjohnson: new help system for cvar ui
//idCVarHelp *				idCVarHelp::staticCVarHelps = NULL;
//idCVarHelp *				idCVarHelp::staticCVarHelpsTail = NULL;
// RAVEN END

idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL|CVAR_SYSTEM, "force generic platform independent SIMD" );

int Sys_Milliseconds( void ) {
	return sys ? sys->Milliseconds() : 0;
}

#endif

idRenderWorld *				gameRenderWorld = NULL;		// all drawing is done to this world

static gameExport_t			gameExport;

static bool rvProjectedLightFloatValid( const float value ) {
	return !FLOAT_IS_NAN( value ) && !FLOAT_IS_INF( value );
}

static bool rvProjectedLightVecFinite( const idVec3 &value ) {
	return rvProjectedLightFloatValid( value[0] ) && rvProjectedLightFloatValid( value[1] ) && rvProjectedLightFloatValid( value[2] );
}

static bool rvProjectedLightVecUsable( const idVec3 &value ) {
	static const float VECTOR_EPSILON_SQR = 1.0e-6f;
	return rvProjectedLightVecFinite( value ) && value.LengthSqr() > VECTOR_EPSILON_SQR;
}

void rvNormalizeProjectedRenderLight( renderLight_t &light, const char *ownerName, const char *lightName ) {
	if ( light.pointLight ) {
		return;
	}

	const bool targetValid = rvProjectedLightVecUsable( light.target );
	const bool rightValid = rvProjectedLightVecUsable( light.right );
	const bool upValid = rvProjectedLightVecUsable( light.up );

	if ( targetValid ) {
		if ( !rvProjectedLightVecFinite( light.start ) ) {
			light.start.Zero();
		}
		if ( !rvProjectedLightVecUsable( light.end ) || !rvProjectedLightVecUsable( light.end - light.start ) ) {
			light.end = light.target;
		}
	}

	const bool depthValid = rvProjectedLightVecFinite( light.start ) && rvProjectedLightVecUsable( light.end - light.start );
	const bool basisValid = rightValid && upValid && rvProjectedLightVecUsable( light.up.Cross( light.right ) );

	if ( targetValid && basisValid && depthValid ) {
		return;
	}

	gameLocal.Warning(
		"Projected render light '%s' on '%s' has invalid projection vectors target=%s right=%s up=%s start=%s end=%s; demoting to point light",
		( lightName && lightName[0] ) ? lightName : "<unnamed>",
		( ownerName && ownerName[0] ) ? ownerName : "<unknown>",
		light.target.ToString( 2 ),
		light.right.ToString( 2 ),
		light.up.ToString( 2 ),
		light.start.ToString( 2 ),
		light.end.ToString( 2 )
	);

	light.pointLight = true;
	light.target.Zero();
	light.right.Zero();
	light.up.Zero();
	light.start.Zero();
	light.end.Zero();
}

// global animation lib
// RAVEN BEGIN
// jsinger: changed to a pointer to prevent its constructor from allocating
//          memory before the unified allocator could be initialized
idAnimManager				*animationLib = NULL;
// RAVEN END

// the rest of the engine will only reference the "game" variable, while all local aspects stay hidden
idGameLocal					gameLocal;
idGame *					game = &gameLocal;	// statically pointed at an idGameLocal

const char *idGameLocal::sufaceTypeNames[ MAX_SURFACE_TYPES ] = {
	"none",	"metal", "stone", "flesh", "wood", "cardboard", "liquid", "glass", "plastic",
	"ricochet", "surftype10", "surftype11", "surftype12", "surftype13", "surftype14", "surftype15"
};

/*
===========
GetGameAPI
============
*/

#if __GNUC__ >= 4
#pragma GCC visibility push(default)
#endif
extern "C" gameExport_t *GetGameAPI( gameImport_t *import ) {

	if ( import->version == GAME_API_VERSION ) {

		// set interface pointers used by the game
		sys							= import->sys;
		common						= import->common;
		cmdSystem					= import->cmdSystem;
		cvarSystem					= import->cvarSystem;
		fileSystem					= import->fileSystem;
		networkSystem				= import->networkSystem;
		renderSystem				= import->renderSystem;
		soundSystem					= import->soundSystem;
		renderModelManager			= import->renderModelManager;
		uiManager					= import->uiManager;
		declManager					= import->declManager;
		AASFileManager				= import->AASFileManager;
		collisionModelManager		= import->collisionModelManager;
// RAVEN BEGIN
// jscott: import the fx system
		bse							= import->bse;
// RAVEN END

// RAVEN BEGIN
// dluetscher: import the memory system variables
#ifdef _RV_MEM_SYS_SUPPORT
		::currentHeapArena			= import->heapArena;
		rvSetAllSysHeaps( import->systemHeapArray );
#endif
// RAVEN END
	}

	// set interface pointers used by idLib
	idLib::sys					= sys;
	idLib::common				= common;
	idLib::cvarSystem			= cvarSystem;
	idLib::fileSystem			= fileSystem;

	// setup export interface
	gameExport.version = GAME_API_VERSION;
	gameExport.game = game;
	gameExport.gameEdit = gameEdit;
// RAVEN BEGIN
// bdube: game log
	gameExport.gameLog = gameLog;
// RAVEN END	

	return &gameExport;
}
#if __GNUC__ >= 4
#pragma GCC visibility pop
#endif

/*
===========
TestGameAPI
============
*/
void TestGameAPI( void ) {
	gameImport_t testImport;
	gameExport_t testExport;

	testImport.sys						= ::sys;
	testImport.common					= ::common;
	testImport.cmdSystem				= ::cmdSystem;
	testImport.cvarSystem				= ::cvarSystem;
	testImport.fileSystem				= ::fileSystem;
	testImport.networkSystem			= ::networkSystem;
	testImport.renderSystem				= ::renderSystem;
	testImport.soundSystem				= ::soundSystem;
	testImport.renderModelManager		= ::renderModelManager;
	testImport.uiManager				= ::uiManager;
	testImport.declManager				= ::declManager;
	testImport.AASFileManager			= ::AASFileManager;
	testImport.collisionModelManager	= ::collisionModelManager;

// RAVEN BEGIN
// jscott: import the fx system
	testImport.bse						= ::bse;
// RAVEN END

	testExport = *GetGameAPI( &testImport );
}

/*
================
idGameLocal::BuildModList
================
*/
void idGameLocal::BuildModList( ) {
	int i;
	idStr currentMod;

	int numServers = networkSystem->GetNumScannedServers();

	if ( filterMod >= 0 && filterMod < modList.Num() ) {
		currentMod = modList[ filterMod ];
	} else {
		currentMod = "";
	}

	modList.Clear();
	for (i = 0; i < numServers; i++) {
		const scannedServer_t *server;
		idStr modname;

		server = networkSystem->GetScannedServerInfo( i );

		server->serverInfo.GetString( "fs_game", "", modname );
		modname.ToLower();
		modList.AddUnique( modname );
	}

	modList.Sort();

	if ( modList.Num() > 0 && (modList[ 0 ].Cmp( "" ) == 0) ) {
		modList.RemoveIndex( 0 );
	}

	filterMod = modList.Num();
	for (i = 0; i < modList.Num(); i++) {
		if ( modList[ i ].Icmp( currentMod ) == 0 ) {
			filterMod = i;
		}
	}
}

/*
================
FilterByMod
================
*/
static int FilterByMod( const int* serverIndex ) {
	const scannedServer_t *server;

	if ( gameLocal.filterMod < 0 || gameLocal.filterMod >= gameLocal.modList.Num() ) {
		return (int)false;
	}

	server = networkSystem->GetScannedServerInfo( *serverIndex );
	const idStr& modFilter = gameLocal.modList[ gameLocal.filterMod ];
	return static_cast<int>( modFilter.Icmp( server->serverInfo.GetString( "fs_game" ) ) != 0 );
}

static sortInfo_t filterByMod = {
	SC_ALL,
	NULL,
	FilterByMod,
	"#str_123006"
};

/*
===========
idGameLocal::idGameLocal
============
*/
idGameLocal::idGameLocal() {
	Clear();
}

/*
===========
idGameLocal::Clear
============
*/
void idGameLocal::Clear( void ) {
	int i;

	serverInfo.Clear();
	repeaterInfo.Clear();
	numClients = 0;
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		userInfo[i].Clear();
		persistentPlayerInfo[i].Clear();
	}
	usercmds = NULL;
	memset( entities, 0, sizeof( entities ) );
	memset( spawnIds, -1, sizeof( spawnIds ) );
	firstFreeIndex = 0;
	num_entities = 0;
	spawnedEntities.Clear();
	activeEntities.Clear();
	numEntitiesToDeactivate = 0;
	sortPushers = false;
	sortTeamMasters = false;
	persistentLevelInfo.Clear();
	memset( globalShaderParms, 0, sizeof( globalShaderParms ) );
	random.SetSeed( 0 );
	world = NULL;
	frameCommandThread = NULL;
	testmodel = NULL;
	ShutdownInstances();
	clip.Clear();
	
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		clientsPVS[ i ].i = -1;
		clientsPVS[ i ].h = -1;
	}
	freePlayerPVS = false;
	pvs.Shutdown();
	sessionCommand.Clear();
	locationEntities = NULL;
	editEntities = NULL;
	entityHash.Clear( 1024, MAX_GENTITIES );
	inCinematic = false;
	cinematicSkipTime = 0;
	cinematicStopTime = 0;
	cinematicMaxSkipTime = 0;
	framenum = 0;
	previousTime = 0;
	time = 0;
	autoExecAfterMapLoadStartTime = 0;
	autoExecAfterMapLoadPending = false;
	autoExecAfterMapLoadWaitingLogged = false;
	vacuumAreaNum = 0;

// RAVEN BEGIN
// abahr
	gravityInfo.Clear();
	scriptObjectProxies.Clear();
// RAVEN END

	mapFileName.Clear();
// RAVEN BEGIN
// rjohnson: entity usage stats
	mapFileNameStripped.Clear();
// RAVEN END
	mapFile = NULL;
	spawnCount = INITIAL_SPAWN_COUNT;
	memset( isMapEntity, 0, sizeof( bool ) * MAX_GENTITIES );

	camera = NULL;

// RAVEN BEGIN
// jscott: for portal skies
	portalSky = NULL;
// RAVEN END

	aasList.Clear();
	aasNames.Clear();
// RAVEN BEGIN
// bdube: added
	lastAIAlertEntity = NULL;
	lastAIAlertEntityTime = 0;
	lastAIAlertActor = NULL;
	lastAIAlertActorTime = 0;
// RAVEN END
	spawnArgs.Clear();
	gravity.Set( 0, 0, -1 );
	playerPVS.i = -1;
	playerPVS.h = -1;
	playerConnectedAreas.i = -1;
	playerConnectedAreas.h = -1;
	mapMediaPrecacheActive = false;
	mapMediaPrecacheSeen.Clear();
	mapMediaPrecacheRequests = 0;
	mapMediaPrecacheSkips = 0;
	gamestate = GAMESTATE_UNINITIALIZED;
	skipCinematic = false;
	influenceActive = false;

	localClientNum = 0;
	isMultiplayer = false;
	isServer = false;
	isClient = false;
	isTVClient = false;
	realClientTime = 0;
	isNewFrame = true;
	entityDefBits = 0;

	nextGibTime = 0;
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	unFreezeTime = 0;
	isFrozen = false;
// RITUAL END
	globalMaterial = NULL;
	newInfo.Clear();
	lastGUIEnt = NULL;
	lastGUI = 0;
	
	memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
	memset( clientPVS, 0, sizeof( clientPVS ) );
	memset( clientSnapshots, 0, sizeof( clientSnapshots ) );

	ReallocViewers( 0 );

	maxViewers = 0;
	maxViewer = 0;
	viewers = NULL;
	viewerEntityStates = NULL;
	viewerPVS = NULL;
	viewerSnapshots = NULL;
	viewerUnreliableMessages = NULL;

	eventQueue.Init();

// RAVEN BEGIN
// bdube: client entities
	clientSpawnCount = INITIAL_SPAWN_COUNT;
	clientSpawnedEntities.Clear();
	memset( clientEntities, 0, sizeof( clientEntities ) );
	memset( clientSpawnIds, -1, sizeof( clientSpawnIds ) );

	gameDebug.Shutdown ( );
	gameLogLocal.Shutdown ( );
	currentThinkingEntity = NULL;
// RAVEN END

	demoState = DEMO_NONE;
	serverDemo = false;
	timeDemo = false;

	memset( &usercmd, 0, sizeof( usercmd ) );
	memset( &oldUsercmd, 0, sizeof( oldUsercmd ) );
	followPlayer = -1;	// start free flying

	demo_protocol = 0;

	instancesEntityIndexWatermarks.Clear();
	clientInstanceFirstFreeIndex = MAX_CLIENTS;
	minSpawnIndex = MAX_CLIENTS;

	modList.Clear();
	filterMod = -1;
	if ( networkSystem ) {
		networkSystem->UseSortFunction( filterByMod, false );
	}
	clientAckSequence = -1;
}

/*
===========
idGameLocal::Init

  initialize the game object, only happens once at startup, not each level load
============
*/
// RAVEN BEGIN
// jsinger: attempt to eliminate cross-DLL allocation issues
extern idHashTable<rvViseme> *visemeTable100;
extern idHashTable<rvViseme> *visemeTable66;
extern idHashTable<rvViseme> *visemeTable33;
#ifdef RV_UNIFIED_ALLOCATOR
void idGameLocal::Init( void *(*allocator)(size_t size), void (*deallocator)( void *ptr ), size_t (*msize)(void *ptr) ) {
#else
void idGameLocal::Init( void ) {
#endif
// RAVEN END
	const idDict *dict;
	idAAS *aas;

#ifndef GAME_DLL

	TestGameAPI();

#else

	mHz = common->GetUserCmdHz();
	msec = common->GetUserCmdMSec();

// RAVEN BEGIN
// jsinger: attempt to eliminate cross-DLL allocation issues
#ifdef RV_UNIFIED_ALLOCATOR
	Memory::InitAllocator(allocator, deallocator, msize);
#endif
// RAVEN END
	// initialize idLib
	idLib::Init();

	// register static cvars declared in the game
	idCVar::RegisterStaticVars();

	// initialize processor specific SIMD
	idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );

#endif
// RAVEN BEGIN
// jsinger: these need to be initialized after the InitAllocator call above in order
//          to avoid crashes when the allocator is used.
	animationLib = new idAnimManager();
	visemeTable100 = new idHashTable<rvViseme>;
	visemeTable66 = new idHashTable<rvViseme>;
	visemeTable33 = new idHashTable<rvViseme>;
// RAVEN END

	Printf( "------------- Initializing Game -------------\n" );
	Printf( "gamename: %s\n", game_name.string );
	Printf( "gamedate: %s\n", __DATE__ );

// RAVEN BEGIN
// rjohnson: new help system for cvar ui
	//idCVarHelp::RegisterStatics();

// jsinger: added to support serialization/deserialization of binary decls
#ifdef RV_BINARYDECLS
	idStr prefix="";
	if(cvarSystem->GetCVarBool("com_binaryDeclRead"))
	{
		prefix = "binary/";
	}
	declManager->RegisterDeclType( "model",				DECL_MODELDEF,		idDeclAllocator<idDeclModelDef>, idDeclStreamAllocator<idDeclModelDef> );
	//declManager->RegisterDeclType( "export",			DECL_MODELEXPORT,	idDeclAllocator<idDecl>, idDeclStreamAllocator<idDecl> );
	declManager->RegisterDeclType( "camera",			DECL_CAMERADEF,		idDeclAllocator<idDeclCameraDef>, idDeclStreamAllocator<idDeclCameraDef> );
	declManager->RegisterDeclFolderWrapper( prefix + "def",			".def",			DECL_ENTITYDEF );
	declManager->RegisterDeclFolderWrapper( prefix + "af",			".af",			DECL_AF );
#else
// RAVEN END
	// register game specific decl types
	declManager->RegisterDeclType( "model",				DECL_MODELDEF,		idDeclAllocator<idDeclModelDef> );
	declManager->RegisterDeclType( "export",			DECL_MODELEXPORT,	idDeclAllocator<idDecl> );

// RAVEN BEGIN
// rjohnson: camera is now contained in a def for frame commands
	declManager->RegisterDeclType( "camera",			DECL_CAMERADEF,		idDeclAllocator<idDeclCameraDef> );
// RAVEN END
	// register game specific decl folders
// RAVEN BEGIN
#ifndef RV_SINGLE_DECL_FILE
	declManager->RegisterDeclFolderWrapper( "def",			".def",			DECL_ENTITYDEF );
// bdube: not used in quake 4
//	declManager->RegisterDeclFolder( "fx",					".fx",			DECL_FX );
//	declManager->RegisterDeclFolder( "particles",			".prt",			DECL_PARTICLE );
	declManager->RegisterDeclFolderWrapper( "af",			".af",			DECL_AF );
//	declManager->RegisterDeclFolderWrapper( "newpdas",		".pda",			DECL_PDA );
#else
	if(!cvarSystem->GetCVarBool("com_SingleDeclFile"))
	{
		declManager->RegisterDeclFolderWrapper( "def",			".def",			DECL_ENTITYDEF );
		declManager->RegisterDeclFolderWrapper( "af",			".af",			DECL_AF );
	}
	else
	{
		// loads the second set of decls from the file which will contain
		// modles, cameras
		declManager->LoadDeclsFromFile();
		declManager->FinishLoadingDecls();
	}
#endif
#endif  // RV_BINARYDECLS
// RAVEN END

	cmdSystem->AddCommand( "listModelDefs", idListDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "lists model defs" );
	cmdSystem->AddCommand( "printModelDefs", idPrintDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "prints a model def", idCmdSystem::ArgCompletion_Decl<DECL_MODELDEF> );

	Clear();

	idEvent::Init();
// RAVEN BEGIN
// jnewquist: Register subclasses explicitly so they aren't dead-stripped
	idClass::RegisterClasses();
// RAVEN END
	idClass::Init();

	InitConsoleCommands();
	// load default scripts
	program.Startup( SCRIPT_DEFAULT );
	
	// set up the aas
// RAVEN BEGIN
// ddynerman: added false as 2nd parameter, otherwise def will be created
	dict = FindEntityDefDict( "aas_types", false );
// RAVEN END
	if ( !dict ) {
		if ( !g_allowAssetlessStartup.GetBool() ) {
			Error( "Unable to find entityDef for 'aas_types'" );
		}
		Printf( "aas_types decl not found; continuing without AAS because g_allowAssetlessStartup is enabled.\n" );
	}

	// allocate space for the aas
	const idKeyValue *kv = dict ? dict->MatchPrefix( "type" ) : NULL;
	while( kv != NULL ) {
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
		MEM_SCOPED_TAG(tag,MA_AAS);
// RAVEN END
		aas = idAAS::Alloc();
		aasList.Append( aas );
		aasNames.Append( kv->GetValue() );
		kv = dict->MatchPrefix( "type", kv );
	}

	gamestate = GAMESTATE_NOMAP;

	Printf( "...%d aas types\n", aasList.Num() );
	Printf( "game initialized.\n" );
	Printf( "---------------------------------------------\n" );
	
// RAVEN BEGIN
// bdube: debug stuff
	gameDebug.Init();
	gameLogLocal.Init();

// jscott: facial animation init
	if( !FAS_Init( "annosoft" ) ) {
		common->Warning( "Failed to load viseme file" );
	}

// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG(tag,MA_RENDER);
// shouchard:  make sure ban list starts out in a known state
	banListLoaded = false;
	banListChanged = false;
	memset( clientGuids, 0, sizeof( clientGuids ) );
// RAVEN END

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
	for(int i=0;i<MAX_CLIENTS;i++)
	{
		persistentPlayerInfo[i].SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_PERMANENT));
	}
	entityHash.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	gravityInfo.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	scriptObjectProxies.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	entityUsageList.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	ambientLights.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	instances.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
	clip.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_LEVEL));
#endif
// RAVEN END

	networkSystem->AddSortFunction( filterByMod );

	incompatibleMaps = 0;
}

/*
===========
idGameLocal::Shutdown

  shut down the entire game
============
*/
void idGameLocal::Shutdown( void ) {

	int		i;

	if ( !common ) {
		return;
	}

// RAVEN BEGIN
// jscott: FAS
	FAS_Shutdown();
// shouchard:  clean up ban list stuff
	SaveBanList();
	FlushBanList();
// RAVEN END

	Printf( "--------------- Game Shutdown ---------------\n" );

	networkSystem->RemoveSortFunction( filterByMod );

	mpGame.Shutdown();

	MapShutdown();

	aasList.DeleteContents( true );
	aasNames.Clear();

	idAI::FreeObstacleAvoidanceNodes();

	// shutdown the model exporter
	idModelExport::Shutdown();

	idEvent::Shutdown();

	program.Shutdown();

	idClass::Shutdown();

	// clear list with forces
	idForce::ClearForceList();

	// free the program data
	program.FreeData();

	// delete the .map file
	delete mapFile;
	mapFile = NULL;

// RAVEN BEGIN
// jscott: free up static objects
	for( i = 0; i < MAX_CLIENTS; i++ ) {
		userInfo[i].Clear();
		persistentPlayerInfo[i].Clear();
	}

	for( i = 0; i < entityUsageList.Num(); i++ ) {
		entityUsageList[i].Clear();
	}

	serverInfo.Clear();
	repeaterInfo.Clear();
	persistentLevelInfo.Clear();
	sessionCommand.Clear();
	mapFileName.Clear();
	mapFileNameStripped.Clear();
	entityUsageList.Clear();

	newInfo.Clear();
	spawnArgs.Clear();
	shakeSounds.Clear();
	aiManager.Clear();
// RAVEN END

	ShutdownConsoleCommands();

	// free memory allocated by class objects
	Clear();

	// shut down the animation manager
// RAVEN BEGIN
// jsinger: animationLib changed to a pointer
	animationLib->Shutdown();
// RAVEN END

// RAVEN BEGIN
// rjohnson: entity usage stats
	entityUsageList.Clear();
// RAVEN END

	Printf( "---------------------------------------------\n" );

	instances.DeleteContents( true );

#ifdef GAME_DLL

	// remove auto-completion function pointers pointing into this DLL
	cvarSystem->RemoveFlaggedAutoCompletion( CVAR_GAME );

	// enable leak test
	Mem_EnableLeakTest( "game" );

	// shutdown idLib
	idLib::ShutDown();

#endif
}

/*
===========
idGameLocal::SaveGame

save the current player state, level name, and level state
the session may have written some data to the file already
============
*/
// RAVEN BEGIN
// mekberg: added saveTypes
void idGameLocal::SaveGame( idFile *f, saveType_t saveType ) {
// RAVEN END
	int i;
	idEntity *ent;
	idEntity *link;

	//remove weapon effect entites from the world
	if(	GetLocalPlayer() && 
		!GetLocalPlayer()->IsInVehicle() &&	
		GetLocalPlayer()->weapon )	{
		
		GetLocalPlayer()->weapon->PreSave();
	}

	idSaveGame savegame( f );

	if (g_flushSave.GetBool( ) == true ) { 
		// force flushing with each write... for tracking down
		// save game bugs.
		f->ForceFlush();
	}

	savegame.WriteBuildNumber( BUILD_NUMBER );

	// go through all entities and threads and add them to the object list
	for( i = 0; i < MAX_GENTITIES; i++ ) {
		ent = entities[i];

		if ( ent ) {
			if ( ent->GetTeamMaster() && ent->GetTeamMaster() != ent ) {
				continue;
			}
			for ( link = ent; link != NULL; link = link->GetNextTeamEntity() ) {
				savegame.AddObject( link );
			}
		}
	}

	idList<idThread *> threads;
	threads = idThread::GetThreads();

	for( i = 0; i < threads.Num(); i++ ) {
		savegame.AddObject( threads[i] );
	}

// RAVEN BEGIN
// abahr: saving clientEntities
	rvClientEntity* clientEnt = NULL;
	for( i = 0; i < MAX_CENTITIES; ++i ) {
		clientEnt = clientEntities[ i ];
		if( !clientEnt ) {
			continue;
		}
//		if( clientEnt->IsType( rvClientEffect::GetClassType() )){
//			continue;
//		}
		savegame.AddObject( clientEnt );
	}
// RAVEN END

	// write out complete object list
	savegame.WriteObjectList();

	program.Save( &savegame );

	savegame.WriteInt( g_skill.GetInteger() );

	savegame.WriteDict( &serverInfo );

	savegame.WriteInt( numClients );
	for( i = 0; i < numClients; i++ ) {
// RAVEN BEGIN
// mekberg: don't write out userinfo. Grab from cvars
//		savegame.WriteDict( &userInfo[ i ] );
// RAVEN END
//		savegame.WriteUsercmd( usercmds[ i ] );
		savegame.WriteDict( &persistentPlayerInfo[ i ] );
	}

	for( i = 0; i < MAX_GENTITIES; i++ ) {
		savegame.WriteObject( entities[ i ] );
		savegame.WriteInt( spawnIds[ i ] );
	}

// RAVEN BEGIN
// abahr: more clientEntities saving
	for( i = 0; i < MAX_CENTITIES; i++ ) {
//		if( clientEntities[ i ] && clientEntities[ i ]->IsType( rvClientEffect::GetClassType() )){
//			continue;
//		}
		savegame.WriteObject( clientEntities[ i ] );
		savegame.WriteInt( clientSpawnIds[ i ] );
	}
// RAVEN END

	savegame.WriteInt( firstFreeIndex );
	savegame.WriteInt( num_entities );

	// enityHash is restored by idEntity::Restore setting the entity name.

	savegame.WriteObject( world );

	savegame.WriteInt( spawnedEntities.Num() );
	for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
		savegame.WriteObject( ent );
	}

// RAVEN BEGIN
// abahr: saving scriptObject proxies
	savegame.WriteInt( scriptObjectProxies.Num() );
	for( i = 0; i < scriptObjectProxies.Num(); ++i ) {
		scriptObjectProxies[i].Save( &savegame );
	}
// abahr: save out client stuff
	savegame.WriteInt( clientSpawnedEntities.Num() );
	for( clientEnt = clientSpawnedEntities.Next(); clientEnt != NULL; clientEnt = clientEnt->spawnNode.Next() ) {
		savegame.WriteObject( clientEnt );
	}
// RAVEN END

	savegame.WriteInt( activeEntities.Num() );
	for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
		savegame.WriteObject( ent );
	}

	savegame.WriteInt( numEntitiesToDeactivate );
	savegame.WriteBool( sortPushers );
	savegame.WriteBool( sortTeamMasters );
	savegame.WriteDict( &persistentLevelInfo );

	for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
		savegame.WriteFloat( globalShaderParms[ i ] );
	}

	savegame.WriteInt( random.GetSeed() );
	savegame.WriteObject( frameCommandThread );

	// clip
	// push
	// pvs

	testmodel = NULL;
// RAVEN BEGIN
// bdube: no test fx
//	testFx = NULL;
// RAVEN END

	savegame.WriteString( sessionCommand );
// RAVEN BEGIN
// nmckenzie: Let the AI system save itself too.
	aiManager.Save( &savegame );
// RAVEN END

	// FIXME: save smoke particles

	savegame.WriteInt( cinematicSkipTime );
	savegame.WriteInt( cinematicStopTime );
	savegame.WriteInt( cinematicMaxSkipTime );
	savegame.WriteBool( inCinematic );
	savegame.WriteBool( skipCinematic );

	savegame.WriteBool( isMultiplayer );
	savegame.WriteInt( gameType );

	savegame.WriteInt( framenum );
	savegame.WriteInt( previousTime );
	savegame.WriteInt( time );

	savegame.WriteInt( vacuumAreaNum );

	savegame.WriteInt( entityDefBits );
	savegame.WriteBool( isServer );
	savegame.WriteBool( isClient );
// RAVEN BEGIN
	savegame.WriteBool( isListenServer );
// RAVEN END

	savegame.WriteInt( localClientNum );

	// snapshotEntities is used for multiplayer only

	savegame.WriteInt( realClientTime );
	savegame.WriteBool( isNewFrame );

	savegame.WriteBool( mapCycleLoaded );
	savegame.WriteInt( spawnCount );

	if ( !locationEntities ) {
		savegame.WriteInt( 0 );
	} else {
		savegame.WriteInt( gameRenderWorld->NumAreas() );
		for( i = 0; i < gameRenderWorld->NumAreas(); i++ ) {
			savegame.WriteObject( locationEntities[ i ] );
		}
	}

	savegame.WriteObject( camera );

	savegame.WriteMaterial( globalMaterial );

// RAVEN BEGIN
// bdube: added
	lastAIAlertActor.Save( &savegame );
	lastAIAlertEntity.Save( &savegame );
	savegame.WriteInt( lastAIAlertEntityTime );
	savegame.WriteInt( lastAIAlertActorTime );
// RAVEN END

	savegame.WriteDict( &spawnArgs );

	savegame.WriteInt( playerPVS.i );
	savegame.WriteInt( playerPVS.h );
	savegame.WriteInt( playerConnectedAreas.i );
	savegame.WriteInt( playerConnectedAreas.h );

	savegame.WriteVec3( gravity );

	// gamestate

	savegame.WriteBool( influenceActive );
	savegame.WriteInt( nextGibTime );

	// spawnSpots
	// initialSpots
	// currentInitialSpot
	// newInfo
	// makingBuild
	// shakeSounds

	// write out pending events
	idEvent::Save( &savegame );

	savegame.Close();

// RAVEN BEGIN
// mekberg: added saveTypes and wrapped saveMessage
	if ( saveType != ST_AUTO && GetLocalPlayer() && GetLocalPlayer()->GetHud() ) {
		GetLocalPlayer()->SaveMessage();
	}

// jshepard: resume weapon operation
	if(	GetLocalPlayer() && 
		!GetLocalPlayer()->IsInVehicle() &&	
		GetLocalPlayer()->weapon )	{
		GetLocalPlayer()->weapon->PostSave();
	}
// RAVEN END
}

/*
===========
idGameLocal::GetPersistentPlayerInfo
============
*/
const idDict &idGameLocal::GetPersistentPlayerInfo( int clientNum ) {
	idEntity	*ent;

	persistentPlayerInfo[ clientNum ].Clear();
	ent = entities[ clientNum ];
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
	if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
		static_cast<idPlayer *>(ent)->SavePersistantInfo();
	}

	return persistentPlayerInfo[ clientNum ];
}

/*
===========
idGameLocal::SetPersistentPlayerInfo
============
*/
void idGameLocal::SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ) {
	TIME_THIS_SCOPE( __FUNCLINE__);
	persistentPlayerInfo[ clientNum ] = playerInfo;
}

/*
============
idGameLocal::Printf
============
*/
void idGameLocal::Printf( const char *fmt, ... ) const {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

	common->Printf( "%s", text );
}

/*
============
idGameLocal::DPrintf
============
*/
void idGameLocal::DPrintf( const char *fmt, ... ) const {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];

	if ( !developer.GetBool() ) {
		return;
	}

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

	common->Printf( "%s", text );
}

/*
============
idGameLocal::Warning
============
*/
void idGameLocal::Warning( const char *fmt, ... ) const {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

	common->Warning( "%s", text );
}

/*
============
idGameLocal::DWarning
============
*/
void idGameLocal::DWarning( const char *fmt, ... ) const {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	idThread *	thread;

	if ( !developer.GetBool() ) {
		return;
	}

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

	thread = idThread::CurrentThread();
	if ( thread ) {
		thread->Warning( "%s", text );
	} else {
		common->DWarning( "%s", text );
	}
}

/*
============
idGameLocal::Error
============
*/
void idGameLocal::Error( const char *fmt, ... ) const {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];
	idThread *	thread;

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

// RAVEN BEGIN
// scork: some model errors arrive here during validation which kills the whole process, so let's just warn about them instead...
	if ( common->DoingDeclValidation() ) {
		this->Warning( "%s", text );
		return;
	}
// RAVEN END

	thread = idThread::CurrentThread();
	if ( thread ) {
		thread->Error( "%s", text );
	} else {
		common->Error( "%s", text );
	}
}

/*
===============
gameError
===============
*/
void gameError( const char *fmt, ... ) {
	va_list		argptr;
	char		text[MAX_STRING_CHARS];

	va_start( argptr, fmt );
	idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
	va_end( argptr );

	gameLocal.Error( "%s", text );
}

/*
===========
idGameLocal::SetLocalClient
============
*/
void idGameLocal::SetLocalClient( int clientNum ) {
	localClientNum = clientNum;
}

/*
===========
idGameLocal::SetUserInfo
============
*/
const idDict* idGameLocal::SetUserInfo( int clientNum, const idDict &userInfo, bool isClient ) {
	
	TIME_THIS_SCOPE( __FUNCLINE__);
	
	int i;
	bool modifiedInfo = false;

	this->isClient = isClient;

	if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
		idGameLocal::userInfo[ clientNum ] = userInfo;

		// server sanity
		if ( !isClient ) {

			// don't let numeric nicknames, it can be exploited to go around kick and ban commands from the server
			if ( idStr::IsNumeric( this->userInfo[ clientNum ].GetString( "ui_name" ) ) ) {
#if UI_DEBUG
				common->Printf( "SetUserInfo: client %d changed name from %s to %s_\n", 
					clientNum, idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) );
#endif
				idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
				modifiedInfo = true;
			}
		
			// don't allow dupe nicknames
			for ( i = 0; i < numClients; i++ ) {
				if ( i == clientNum ) {
					continue;
				}
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
				if ( entities[ i ] && entities[ i ]->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
					if ( !idStr::Icmp( idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), idGameLocal::userInfo[ i ].GetString( "ui_name" ) ) ) {
#if UI_DEBUG
						common->Printf( "SetUserInfo: client %d changed name from %s to %s_ because of client %d\n", 
							clientNum, idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), i );
#endif
						idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
						modifiedInfo = true;
						i = -1;	// rescan
						continue;
					}
				}
			}
		}

// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
			modifiedInfo |= static_cast<idPlayer *>( entities[ clientNum ] )->UserInfoChanged();
		}

		if ( !isClient ) {
			// now mark this client in game
			mpGame.EnterGame( clientNum );
		}
	}

	if ( modifiedInfo ) {
		newInfo = idGameLocal::userInfo[ clientNum ];
		return &newInfo;
	}
	return NULL;
}

/*
===========
idGameLocal::GetUserInfo
============
*/
const idDict* idGameLocal::GetUserInfo( int clientNum ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
	if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
		return &userInfo[ clientNum ];
	}
	return NULL;
}

/*
===========
idGameLocal::IsClientActive
============
*/
bool idGameLocal::IsClientActive( int clientNum ) {
	if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::GetClassType() ) ) {
		return true;
	}

	return false;
}

/*
===========
idGameLocal::RepeaterSetUserInfo
============
*/
const idDict* idGameLocal::RepeaterSetUserInfo( int clientNum, const idDict &userInfo ) {

	TIME_THIS_SCOPE( __FUNCLINE__);

	assert( cvarSystem->GetCVarInteger( "ri_maxViewers" ) > clientNum );
	ReallocViewers( cvarSystem->GetCVarInteger( "ri_maxViewers" ) );

	idDict *info;
	
	int i;
	bool modifiedInfo = false;

	info = &viewers[ clientNum ].info;
	*info = userInfo;

	// don't let numeric nicknames, it can be exploited to go around kick and ban commands from the server
	if ( idStr::IsNumeric( info->GetString( "ui_name" ) ) ) {
		info->Set( "ui_name", va( "%s_", info->GetString( "ui_name" ) ) );
		modifiedInfo = true;
	}

	// don't allow dupe nicknames
	for ( i = 0; i < maxViewer; i++ ) {
		if ( i == clientNum ) {
			continue;
		}

		if ( !viewers[ i ].active ) {
			continue;
		}

		if ( !idStr::Icmp( info->GetString( "ui_name" ), viewers[ i ].info.GetString( "ui_name" ) ) ) {
			info->Set( "ui_name", va( "%s_", info->GetString( "ui_name" ) ) );
			modifiedInfo = true;
			i = -1;	// rescan
			continue;
		}
	}

	if ( modifiedInfo ) {
		newInfo = *info;
		return &newInfo;
	}

	return NULL;
}

/*
===========
idGameLocal::UpdateRepeaterInfo
============
*/

void idGameLocal::UpdateRepeaterInfo( bool transmit ) {
	if ( !isRepeater ) {
		return;
	}

	repeaterInfo = serverInfo;
	repeaterInfo.Set( "si_tv", "1" );

	// fudge a few cvars so old browsers display correctly
	repeaterInfo.Set( "si_usePass", ri_useViewerPass.GetBool() ? "1" : "0" );

	const char *ri_name = cvarSystem->GetCVarString( "ri_name" );
	if ( ri_name[0] ) {
		if ( !serverInfo.GetString( "ri_origName" ) [0] ) {
			repeaterInfo.Set( "ri_origName", serverInfo.GetString( "si_name" ) );
		}
		repeaterInfo.Set( "si_name", ri_name );
	}

	const char *si_serverURL = cvarSystem->GetCVarString( "si_serverURL" );
	if ( si_serverURL[0] ) {
		if ( !serverInfo.GetString( "ri_origServerURL" ) [0] ) {
			repeaterInfo.Set( "ri_origServerURL", serverInfo.GetString( "si_serverURL" ) );
		}
		repeaterInfo.Set( "si_serverURL", si_serverURL );
	}

	repeaterInfo.Copy( *cvarSystem->MoveCVarsToDict( CVAR_REPEATERINFO ) );

	networkSystem->RepeaterSetInfo( repeaterInfo );

	if ( transmit ) {
		// Let our clients know the server info changed
		idBitMsg	outMsg;
		byte		msgBuf[MAX_GAME_MESSAGE_SIZE];

		outMsg.Init( msgBuf, sizeof( msgBuf ) );
		outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
		outMsg.WriteDeltaDict( gameLocal.repeaterInfo, NULL );
// jmarshall - engine has no repeater reliable channel
		//networkSystem->RepeaterSendReliableMessage( -1, outMsg );
// jmarshall end
	}
}

/*
===========
idGameLocal::SetServerInfo
============
*/
void idGameLocal::SetServerInfo( const idDict &_serverInfo ) {
	TIME_THIS_SCOPE( __FUNCLINE__);
	
	idBitMsg	outMsg;
	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
	bool timeLimitChanged = false;

// RAVEN BEGIN
// mekberg: clear announcer and reschedule time announcements
	if ( ( isClient || isListenServer ) && mpGame.GetGameState( ) && mpGame.GetGameState( )->GetMPGameState( ) == GAMEON &&
		serverInfo.GetInt( "si_timelimit" ) != _serverInfo.GetInt( "si_timelimit" ) ) {
		timeLimitChanged = true;
	}

	serverInfo = _serverInfo;

	if ( timeLimitChanged ) {
		mpGame.ClearAnnouncerSounds( );
		mpGame.ScheduleTimeAnnouncements( );
	}
// RAVEN END

	UpdateRepeaterInfo( true );

	if ( isServer ) {
		// Let our clients know the server info changed
		outMsg.Init( msgBuf, sizeof( msgBuf ) );
		outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
		outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
		networkSystem->ServerSendReliableMessage( -1, outMsg );
	} else if ( isClient ) {
		networkSystem->ClientSetServerInfo( gameLocal.serverInfo );
		isTVClient = ( gameLocal.serverInfo.GetInt( "si_tv" ) == 1 ) ? true : false;
	}
}

/*
===================
idGameLocal::LoadMap

Initializes all map variables common to both save games and spawned games.
===================
*/
void idGameLocal::LoadMap( const char *mapName, int randseed ) {
	int i;
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG(tag,MA_PARSER);
// RAVEN END
	bool sameMap = (mapFile && idStr::Icmp(mapFile->GetName(), mapName) == 0);
	const int loadStartMsec = Sys_Milliseconds();
	int phaseStartMsec = loadStartMsec;
	int mapParseMsec = 0;
	int mapResolveMsec = 0;
	int collisionMsec = 0;

	networkSystem->SetLoadingText( mapName );

	InitAsyncNetwork();

	// these can changed based upon sp / mp
	mHz = common->GetUserCmdHz();
	msec = common->GetUserCmdMSec();

	if ( !sameMap || ( mapFile && mapFile->NeedsReload() ) ) {
		// load the .map file
		if ( mapFile ) {
			delete mapFile;
		}
		mapFile = new idMapFile;
		if ( !mapFile->Parse( idStr( mapName ) + ".map" ) ) {
			delete mapFile;
			mapFile = NULL;
			Error( "Couldn't load %s", mapName );
		}
		mapParseMsec = Sys_Milliseconds() - phaseStartMsec;
		phaseStartMsec = Sys_Milliseconds();
// RAVEN BEGIN
// rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
		mapFile->Resolve();
// RAVEN END
		mapResolveMsec = Sys_Milliseconds() - phaseStartMsec;
	}
	mapFileName = mapFile->GetName();
	
	assert(!idStr::Cmp(mapFileName, mapFile->GetName()));
	
// RAVEN BEGIN
// rjohnson: entity usage stats
	mapFileNameStripped = mapFileName;
	mapFileNameStripped.StripFileExtension();
	mapFileNameStripped.StripPath();

	const char*	entityFilter;

	gameLocal.serverInfo.GetString( "si_entityFilter", "", &entityFilter );
	if ( entityFilter && *entityFilter ) {
		mapFileNameStripped += " ";
		mapFileNameStripped += entityFilter;
	}
// RAVEN END

	// load the collision map
	networkSystem->SetLoadingText( common->GetLocalizedString( "#str_107668" ) );
	phaseStartMsec = Sys_Milliseconds();
	collisionModelManager->LoadMap( mapFile, false );
	collisionMsec = Sys_Milliseconds() - phaseStartMsec;
	if ( cvarSystem->GetCVarBool( "com_showLevelLoadTimes" ) ) {
		Printf(
			"Game LoadMap phases: mapParse=%d mapResolve=%d collision=%d total=%d msec\n",
			mapParseMsec,
			mapResolveMsec,
			collisionMsec,
			Sys_Milliseconds() - loadStartMsec );
	}

	numClients = 0;

	// initialize all entities for this game
	memset( entities, 0, sizeof( entities ) );
	usercmds = NULL;
	memset( spawnIds, -1, sizeof( spawnIds ) );
	spawnCount = INITIAL_SPAWN_COUNT;
	
	spawnedEntities.Clear();
	activeEntities.Clear();
	numEntitiesToDeactivate = 0;
	sortTeamMasters = false;
	sortPushers = false;
	lastGUIEnt = NULL;
	lastGUI = 0;

// RAVEN BEGIN
// bdube: client entities
	clientSpawnCount = INITIAL_SPAWN_COUNT;
	clientSpawnedEntities.Clear ( );
	memset ( clientSpawnIds, -1, sizeof(clientSpawnIds ) );
	memset ( clientEntities, 0, sizeof(clientEntities) );
	firstFreeClientIndex = 0;
// RAVEN END

	globalMaterial = NULL;

	memset( globalShaderParms, 0, sizeof( globalShaderParms ) );

	// always leave room for the max number of clients,
	// even if they aren't all used, so numbers inside that
	// range are NEVER anything but clients
	num_entities	= MAX_CLIENTS;
	firstFreeIndex	= MAX_CLIENTS;

	// reset the random number generator.
	random.SetSeed( isMultiplayer ? randseed : 0 );

	camera			= NULL;
	world			= NULL;
	testmodel		= NULL;
// RAVEN BEGIN
// bdube: not using id effects
//	testFx			= NULL;
// RAVEN END

// RAVEN BEGIN
// bdube: merged
	lastAIAlertEntity = NULL;
	lastAIAlertEntityTime = 0;
	lastAIAlertActor = NULL;
	lastAIAlertActorTime = 0;
// RAVEN END
	
	previousTime	= 0;
	time			= 0;
	framenum		= 0;
	sessionCommand = "";
	nextGibTime		= 0;
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	unFreezeTime	= 0;
	isFrozen		= 0;
// RITUAL END

	vacuumAreaNum = -1;		// if an info_vacuum is spawned, it will set this

// RAVEN BEGIN
// abahr
	gravityInfo.Clear();
	scriptObjectProxies.Clear();
// RAVEN END

	if ( !editEntities ) {
		editEntities = new idEditEntities;
	}

	if( gameLocal.isMultiplayer ) {
		gravity.Set( 0, 0, -g_mp_gravity.GetFloat() );
	} else {
		gravity.Set( 0, 0, -g_gravity.GetFloat() );
	}

	spawnArgs.Clear();

// RAVEN BEGIN
// nmckenzie:
	//make sure we clear all reachabilities we marked as blocked!
	aiManager.UnMarkAllReachBlocked();
	aiManager.Clear();
// RAVEN END

	skipCinematic = false;
	inCinematic = false;
	cinematicSkipTime = 0;
	cinematicStopTime = 0;
	cinematicMaxSkipTime = 0;

// RAVEN BEGIN
// ddynerman: main world instance
	PACIFIER_UPDATE;
	AddInstance( 0, true );
	assert( instances.Num() == 1 && instances[ 0 ]->GetInstanceID() == 0 );
// RAVEN END
	pvs.Init();
// RAVEN BEGIN
// mwhitlock: Xenon texture streaming
#if defined(_XENON)
	// Experimental use of game's PVS for reducing amount of stuff streamed. Will
	// do this a bit more cleanly if I decide to keep this.
	extern idPVS* pvsForStreaming;
	pvsForStreaming=&pvs;
#endif
// RAVEN END

	playerPVS.i = -1;
	playerConnectedAreas.i = -1;

	// load navigation system for all the different monster sizes
	for( i = 0; i < aasNames.Num(); i++ ) {
		aasList[ i ]->Init( idStr( mapFileName ).SetFileExtension( aasNames[ i ] ).c_str(), mapFile->GetGeometryCRC() );
	}

// RAVEN BEGIN
// cdr: Obstacle Avoidance
	AI_MoveInitialize();
// RAVEN END

	FindEntityDef( "preCacheExtras", false );

	if ( !sameMap ) {
		mapFile->RemovePrimitiveData();
	}

// RAVEN BEGIN
// ddynerman: ambient light list
	ambientLights.Clear();
// RAVEN END
}

/*
===================
idGameLocal::LocalMapRestart
===================
*/
void idGameLocal::LocalMapRestart( int instance ) {
	int i, latchSpawnCount;

	Printf( "----------- Game Map Restart (%s) ------------\n", instance == -1 ? "all instances" : va( "instance %d", instance ) );

	// client always respawns everything, so make sure it picks up the right map entities
	if( instance == -1 || isClient ) {
		memset( isMapEntity, 0, sizeof(bool) * MAX_GENTITIES );
	} else {
		assert( instance >= 0 && instance < instances.Num() );

		for( int i = 0; i < instances[ instance ]->GetNumMapEntities(); i++ ) {
			if ( instances[ instance ]->GetMapEntityNumber( i ) >= 0 && instances[ instance ]->GetMapEntityNumber( i ) < MAX_GENTITIES ) {
				isMapEntity[ instances[ instance ]->GetMapEntityNumber( i ) ] = false;
			}
		}
	}

	gamestate = GAMESTATE_SHUTDOWN;

	for ( i = 0; i < MAX_CLIENTS; i++ ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( entities[ i ] && entities[ i ]->IsType( idPlayer::GetClassType() ) && (isClient || instance == -1 || entities[ i ]->GetInstance() == instance ) ) {
// RAVEN END
			static_cast< idPlayer * >( entities[ i ] )->PrepareForRestart();
		}
	}

	eventQueue.Shutdown();

	MapClear( false, instance );

	// clear the sound system
	soundSystem->StopAllSounds( SOUNDWORLD_GAME );

	// clear icons
	iconManager->Shutdown();

	// the spawnCount is reset to zero temporarily to spawn the map entities with the same spawnId
	// if we don't do that, network clients are confused and don't show any map entities
	latchSpawnCount = spawnCount;
	spawnCount = INITIAL_SPAWN_COUNT;

	gamestate = GAMESTATE_STARTUP;

	program.Restart();

	InitScriptForMap();

	MapPopulate( instance );

	// once the map is populated, set the spawnCount back to where it was so we don't risk any collision
	// (note that if there are no players in the game, we could just leave it at it's current value)
	spawnCount = latchSpawnCount;

	// setup the client entities again
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( entities[ i ] && entities[ i ]->IsType( idPlayer::GetClassType() ) && (isClient || instance == -1 || entities[ i ]->GetInstance() == instance ) ) {
// RAVEN END
			static_cast< idPlayer * >( entities[ i ] )->Restart();
		}
	}

	gamestate = GAMESTATE_ACTIVE;

	Printf( "--------------------------------------\n" );
}

/*
===================
idGameLocal::MapRestart
===================
*/
void idGameLocal::MapRestart( int instance ) {
	idBitMsg	outMsg;
	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
	idDict		newInfo;
	int			i;
	const idKeyValue *keyval, *keyval2;

	if ( isClient ) {
		SetGameType();
		LocalMapRestart( instance );
	} else {

		if ( mpGame.PickMap( "", true ) ) {
			common->Warning( "map %s and gametype %s are incompatible, aborting map change", si_map.GetString(), si_gameType.GetString() );
			if ( (++incompatibleMaps) >= 50 ) {
				common->Warning( "too many incompatible maps for gametype %s; setting gametype to DM.", si_gameType.GetString() );
				si_gameType.SetString( "DM" );
			}
			return;
		}

		incompatibleMaps = 0;

		newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
		// this has to be after the cvars are moved to the dict
		for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {

			keyval = newInfo.GetKeyVal( i );
			keyval2 = serverInfo.FindKey( keyval->GetKey() );
			if ( !keyval2 ) {
				break;
			}
			// a select set of si_ changes will cause a full restart of the server
			if ( keyval->GetValue().Icmp( keyval2->GetValue() ) &&
				( !keyval->GetKey().Icmp( "si_pure" ) || !keyval->GetKey().Icmp( "si_map" ) ) ) {
				break;
			}
//RAVEN BEGIN
			//asalmon: need to restart if the game type has changed but the map has not cause someone could be connecting
			if(  keyval->GetValue().Icmp( keyval2->GetValue() ) && (!keyval->GetKey().Icmp("si_gametype")))
			{
				break;
			}
//RAVEN END

			// some calculations about time are done from the frame, and
			// that changes when si_fps changes.  full restart resets to 0.
			if ( keyval->GetValue().Icmp( keyval2->GetValue() ) &&
				!keyval->GetKey().Icmp( "si_fps" ) ) {
				break;
			}
		}
		cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" " " __FILE__ " " __LINESTR__ );

		SetGameType();

		mpGame.isBuyingAllowedRightNow = false;

		if ( i != newInfo.GetNumKeyVals() ) {
			gameLocal.sessionCommand = "nextMap";
		} else {
			outMsg.Init( msgBuf, sizeof( msgBuf ) );
			outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
			outMsg.WriteBits( 1, 1 );
			outMsg.WriteDeltaDict( serverInfo, NULL );
			networkSystem->ServerSendReliableMessage( -1, outMsg );

			if ( isRepeater ) {
				outMsg.Init( msgBuf, sizeof( msgBuf ) );
				outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
				outMsg.WriteBits( 1, 1 );
				outMsg.WriteDeltaDict( repeaterInfo, NULL );
// jmarshall - engine has no repeater reliable channel
				//networkSystem->RepeaterSendReliableMessage( -1, outMsg );
// jmarshall end
			}

			LocalMapRestart( instance );
			mpGame.MapRestart();
		}
	}
}

/*
===================
idGameLocal::VerifyServerSettings_f
===================
*/
void idGameLocal::VerifyServerSettings_f( const idCmdArgs &args ) {
	gameLocal.mpGame.PickMap( si_gameType.GetString() );
}

/*
===================
idGameLocal::MapRestart_f
===================
*/
void idGameLocal::MapRestart_f( const idCmdArgs &args ) {
	if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
		common->Printf( "server is not running - use spawnServer\n" );
		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
		return;
	}
	
	int instance = -1;
	if( args.Argc() > 1 ) {
		instance = atoi( args.Args( 1 ) );
		if( instance < 0 || instance >= gameLocal.GetNumInstances() ) {
			common->Warning( "idGameLocal::MapRestart_f() - Invalid instance '%d' specified\n", instance );
			return;
		}
		gameLocal.LocalMapRestart( instance );
		return;
	}

	gameLocal.MapRestart( instance );
}

/*
===================
idGameLocal::NextMap
===================
*/
bool idGameLocal::NextMap( void ) {
	const function_t	*func;
	idThread			*thread;
	idDict				newInfo;
	const idKeyValue	*keyval, *keyval2;
	int					i;
	const char			*mapCycleList, *currentMap;

//RAVEN BEGIN
//asalmon: pick another map on Xenon
#ifdef _XENON
	//Live()->PickMap();
	return true;
#endif
//RAVEN END

// RAVEN BEGIN
// rjohnson: traditional map cycle
//		si_mapCycle "mp/q4dm4;mp/q4dm5;mp/q4dm6"
	mapCycleList = si_mapCycle.GetString();
	if ( mapCycleList && strlen( mapCycleList ) ) {
		idLexer src;
		idToken token, firstFound;
		int		numMaps = 0;
		bool	foundMap = false;

		src.FreeSource();
		src.SetFlags( LEXFL_NOFATALERRORS | LEXFL_ALLOWPATHNAMES | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
		src.LoadMemory( mapCycleList, strlen( mapCycleList ), "idGameLocal::NextMap" );
		if ( src.IsLoaded() ) {

			currentMap = si_map.GetString();
			while( src.ReadToken( &token ) ) {
				if ( token == ";" ) {
					continue;
				}
				numMaps++;

				if ( numMaps == 1 ) {
					// guarantee that we use a map no matter what ( or when we wrap )
					firstFound = token;
				}
				if ( foundMap ) {
					firstFound = token;
					break;
				}
				
				if ( idStr::Icmp( token, currentMap ) == 0 ) {
					foundMap = true;
				}
			}

			if ( firstFound != "" ) {
				si_map.SetString( firstFound );
				return true;
			}
		}

		return false;
	}
// RAVEN END

	if ( !g_mapCycle.GetString()[0] ) {
		Printf( common->GetLocalizedString( "#str_104294" ) );
		return false;
	}
	if ( fileSystem->ReadFile( g_mapCycle.GetString(), NULL, NULL ) < 0 ) {
		if ( fileSystem->ReadFile( va( "%s.scriptcfg", g_mapCycle.GetString() ), NULL, NULL ) < 0 ) {
			Printf( "map cycle script '%s': not found\n", g_mapCycle.GetString() );
			return false;
		} else {
			g_mapCycle.SetString( va( "%s.scriptcfg", g_mapCycle.GetString() ) );
		}
	}

	Printf( "map cycle script: '%s'\n", g_mapCycle.GetString() );
	func = program.FindFunction( "mapcycle::cycle" );
	if ( !func ) {
		program.CompileFile( g_mapCycle.GetString(), false );
		func = program.FindFunction( "mapcycle::cycle" );
	}
	if ( !func ) {
		Printf( "Couldn't find mapcycle::cycle\n" );
		return false;
	}
	thread = new idThread( func );
	thread->Start();
	delete thread;

	newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
	for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
		keyval = newInfo.GetKeyVal( i );
		keyval2 = serverInfo.FindKey( keyval->GetKey() );
		if ( !keyval2 || keyval->GetValue().Icmp( keyval2->GetValue() ) ) {
			break;
		}
	}
	return ( i != newInfo.GetNumKeyVals() );
}

/*
===================
idGameLocal::NextMap_f
===================
*/
void idGameLocal::NextMap_f( const idCmdArgs &args ) {
	if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
		common->Printf( "server is not running\n" );
		return;
	}

	gameLocal.NextMap( );
	// next map was either voted for or triggered by a server command - always restart
	gameLocal.MapRestart( );
}

/*
===============
idGameLocal::GetStartingIndexForInstance
===============
*/
int idGameLocal::GetStartingIndexForInstance( int instanceID ) {
	if ( isServer ) {
		assert( instancesEntityIndexWatermarks.Num() >= instanceID );
		if ( instanceID == 0 ) {
			return MAX_CLIENTS;
		} else {
			// the high watermark of the previous instance is the starting index of the next one
			return instancesEntityIndexWatermarks[ instanceID - 1 ];
		}
	} else {
		assert( instanceID == 0 );
		return clientInstanceFirstFreeIndex;
	}
}

/*
===============
idGameLocal::ServerSetEntityIndexWatermark
keep track of the entity layout at the server - specially when there are multiple instances ( tourney )
===============
*/
void idGameLocal::ServerSetEntityIndexWatermark( int instanceID ) {
	if ( isClient ) {
		return;
	}
	instancesEntityIndexWatermarks.AssureSize( instanceID + 1, MAX_CLIENTS );
	// make sure there is no drift. if a value was already set it has to match
	// otherwise that means the server is repopulating with different indexes, and that would likely lead to net corruption
	assert( instancesEntityIndexWatermarks[ instanceID ] == MAX_CLIENTS || instancesEntityIndexWatermarks[ instanceID ] == firstFreeIndex );
	instancesEntityIndexWatermarks[ instanceID ] = firstFreeIndex;
}

/*
===============
idGameLocal::ServerSetMinSpawnIndex
===============
*/
void idGameLocal::ServerSetMinSpawnIndex( void ) {
	if ( isClient ) {
		return;
	}
	// setup minSpawnIndex with enough headroom so gameplay entities don't cause bad offsets
	// only needed on server, clients are completely slaved up to server entity layout
	if ( !idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Tourney" ) ) {
		minSpawnIndex = MAX_CLIENTS + GetNumMapEntities() * MAX_INSTANCES;
	}
}

/*
===================
idGameLocal::MapPopulate
===================
*/
void idGameLocal::MapPopulate( int instance ) {

// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG(tag,MA_ENTITY);
// RAVEN END

	if ( isMultiplayer ) {
		cvarSystem->SetCVarBool( "r_skipSpecular", false );
	}

	minSpawnIndex = MAX_CLIENTS;

	// parse the key/value pairs and spawn entities
// RAVEN BEGIN
// ddynerman: instance code
	// reload the instances
	if ( instance == -1 ) {
		int i;
		firstFreeIndex = MAX_CLIENTS;
		for ( i = 0; i < instances.Num(); i++ ) {
			if ( instances[ i ] ) {
				instances[ i ]->Restart();

				ServerSetEntityIndexWatermark( i );
			}
		}
	} else {
		assert( instance >= 0 && instance < instances.Num() );
		instances[ instance ]->Restart();
	}
// RAVEN END

	ServerSetMinSpawnIndex();

	// mark location entities in all connected areas
	SpreadLocations();

	// RAVEN BEGIN
	// ddynerman: prepare the list of spawn spots
	InitializeSpawns();
	// RAVEN END

	// execute pending events before the very first game frame
	// this makes sure the map script main() function is called
	// before the physics are run so entities can bind correctly
	Printf( "------------ Processing events --------------\n" );
	idEvent::ServiceEvents();
}

/*
===================
idGameLocal::InitFromNewMap
===================
*/
void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, bool isServer, bool isClient, int randseed ) {

	TIME_THIS_SCOPE( __FUNCLINE__);
	const int initStartMsec = Sys_Milliseconds();
	int phaseStartMsec = initStartMsec;
	int startupMsec = 0;
	int loadMapMsec = 0;
	int scriptMsec = 0;
	int populateMsec = 0;
	int mpPrecacheMsec = 0;
	int animationFlushMsec = 0;
	
	this->isServer = isServer;
	this->isClient = isClient;
// RAVEN BEGIN
// ddynerman: listen server
	this->isListenServer = isServer && !cvarSystem->GetCVarBool( "net_serverDedicated" );
// RAVEN END
	this->isMultiplayer = isServer || isClient;
	
	if ( this->isClient && gameLocal.serverInfo.GetInt( "si_tv" ) == 1 ) {
		this->isTVClient = true;
	}

	if ( cvarSystem->GetCVarInteger( "net_serverDownload" ) == 3 ) {
		networkSystem->HTTPEnable( this->isServer || this->isRepeater );
	}

	if ( !this->isMultiplayer )
		gameLocal.Error( "This mod is for multiplayer only" );

	PACIFIER_UPDATE;

//RAVEN BEGIN
//asalmon: stats for single player
	if (!this->isMultiplayer) {
#ifdef _MPBETA
		return;
#else
		statManager->EndGame();
#ifdef _XENON
		Live()->DeleteSPSession(true);
#endif
		statManager->Shutdown();
		statManager->Init();
		statManager->BeginGame();
		statManager->ClientConnect(0);
#ifdef _XENON
		Live()->CreateSPSession();
#endif
#endif // _MPBETA
	}
//RAVEN END

	if ( mapFileName.Length() ) {
		MapShutdown();
	}

	Printf( "-------------- Game Map Init ----------------\n" );

	gamestate = GAMESTATE_STARTUP;

	gameRenderWorld = renderWorld;

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
	animationLib->BeginLevelLoad();
#endif
	
	SetGameType();

	startupMsec = Sys_Milliseconds() - phaseStartMsec;
	phaseStartMsec = Sys_Milliseconds();
	BeginMapMediaPrecache();
	LoadMap( mapName, randseed );
	loadMapMsec = Sys_Milliseconds() - phaseStartMsec;
	phaseStartMsec = Sys_Milliseconds();

	InitScriptForMap();
	scriptMsec = Sys_Milliseconds() - phaseStartMsec;
	phaseStartMsec = Sys_Milliseconds();

	MapPopulate();
	populateMsec = Sys_Milliseconds() - phaseStartMsec;
	phaseStartMsec = Sys_Milliseconds();

	mpGame.Reset();

	mpGame.Precache();
	EndMapMediaPrecache();
	mpPrecacheMsec = Sys_Milliseconds() - phaseStartMsec;
	phaseStartMsec = Sys_Milliseconds();

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
	animationLib->EndLevelLoad();
#endif
// RAVEN END

	// free up any unused animations
// RAVEN BEGIN
// jsinger: animationLib changed to a pointer
	animationLib->FlushUnusedAnims();
// RAVEN END
	animationFlushMsec = Sys_Milliseconds() - phaseStartMsec;
	if ( cvarSystem->GetCVarBool( "com_showLevelLoadTimes" ) ) {
		Printf(
			"Game init phases: startup=%d loadMap=%d script=%d populate=%d mpPrecache=%d animationFlush=%d total=%d msec\n",
			startupMsec,
			loadMapMsec,
			scriptMsec,
			populateMsec,
			mpPrecacheMsec,
			animationFlushMsec,
			Sys_Milliseconds() - initStartMsec );
	}

	gamestate = GAMESTATE_ACTIVE;
	autoExecAfterMapLoadStartTime = 0;
	const char *autoExecAfterMapLoad = g_autoExecAfterMapLoad.GetString();
	const char *autoExecAfterMapLoadCvar = cvarSystem->GetCVarString( "g_autoExecAfterMapLoad" );
	const bool autoExecAfterMapLoadArmed =
		( autoExecAfterMapLoad && autoExecAfterMapLoad[ 0 ] != '\0' ) ||
		( autoExecAfterMapLoadCvar && autoExecAfterMapLoadCvar[ 0 ] != '\0' );
	autoExecAfterMapLoadPending = false;
	autoExecAfterMapLoadWaitingLogged = false;
	if ( autoExecAfterMapLoadArmed ) {
		Printf( "AutoExecAfterMapLoad: armed for first active draw (delay %d ms)\n", g_autoExecAfterMapLoadDelayMs.GetInteger() );
		Printf( "AutoExecAfterMapLoad: waiting for first active draw\n" );
		autoExecAfterMapLoadWaitingLogged = true;
	}

	Printf( "---------------------------------------------\n" );
}

/*
=================
idGameLocal::InitFromSaveGame
=================
*/
static void GameLocal_ValidateSaveGameCount( idRestoreGame &savegame, int count, int maxCount, const char *detail ) {
	if ( count < 0 || count > maxCount ) {
		savegame.Error( "idGameLocal::InitFromSaveGame: invalid %s %d (max %d)", detail, count, maxCount );
	}
}

bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idFile *saveGameFile ) {
	TIME_THIS_SCOPE( __FUNCLINE__);
	
	int i;
	int num;
	idEntity *ent;
	idDict si;

	if ( mapFileName.Length() ) {
		MapShutdown();
	}

	Printf( "---------- Game Map Init SaveGame -----------\n" );

	gamestate = GAMESTATE_STARTUP;

	gameRenderWorld = renderWorld;

	idRestoreGame savegame( saveGameFile );

	savegame.ReadBuildNumber();
	if ( !savegame.IsOpenQ4SaveGameCompatible() ) {
		Warning(
			"Savegame payload is not compatible with this openQ4 build: %s",
			savegame.GetOpenQ4SaveGameCompatibilityError() );
		program.Restart();
		return false;
	}

	// Create the list of all objects in the game
	savegame.CreateObjects();

	// Load the idProgram, also checking to make sure scripting hasn't changed since the savegame
	if ( program.Restore( &savegame ) == false ) {

		// Abort the load process, and let the session know so that it can restart the level
		// with the player persistent data.
		savegame.DeleteObjects();
		program.Restart();

		return false;
	}

	// load the map needed for this savegame
	LoadMap( mapName, 0 );

	savegame.ReadInt( i );
	g_skill.SetInteger( i );

	// precache any media specified in the map
	BeginMapMediaPrecache();
	for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
		idMapEntity *mapEnt = mapFile->GetEntity( i );

		if ( !InhibitEntitySpawn( mapEnt->epairs ) ) {
			CacheDictionaryMedia( &mapEnt->epairs );
			const char *classname = mapEnt->epairs.GetString( "classname" );
			if ( classname[0] != '\0' ) {
				FindEntityDef( classname, false );
			}
		}
	}
	EndMapMediaPrecache();

	savegame.ReadDict( &si );
	SetServerInfo( si );

	savegame.ReadInt( numClients );
	GameLocal_ValidateSaveGameCount( savegame, numClients, MAX_CLIENTS, "client count" );
	for( i = 0; i < numClients; i++ ) {
// RAVEN BEGIN
// mekberg: don't read in userinfo. Grab from cvars
//		savegame.ReadDict( &userInfo[ i ] );
// RAVEN END
//		savegame.ReadUsercmd( usercmds[ i ] );
		savegame.ReadDict( &persistentPlayerInfo[ i ] );
	}

	for( i = 0; i < MAX_GENTITIES; i++ ) {
		savegame.ReadObject( reinterpret_cast<idClass *&>( entities[ i ] ) );
		savegame.ReadInt( spawnIds[ i ] );

		// restore the entityNumber
		if ( entities[ i ] != NULL ) {
			entities[ i ]->entityNumber = i;
		}
	}

	// Precache the player
// RAVEN BEGIN
// bdube: changed so we actually cache stuff
	FindEntityDef( idPlayer::GetSpawnClassname( ) );

// abahr: saving clientEntities
	for( i = 0; i < MAX_CENTITIES; i++ ) {
		savegame.ReadObject( reinterpret_cast<idClass *&>( clientEntities[ i ] ) );
		savegame.ReadInt( clientSpawnIds[ i ] );

		// restore the entityNumber
		if ( clientEntities[ i ] != NULL ) {
			clientEntities[ i ]->entityNumber = i;
		}
	}
// RAVEN END

	savegame.ReadInt( firstFreeIndex );
	savegame.ReadInt( num_entities );
	if ( firstFreeIndex < 0 || firstFreeIndex >= MAX_GENTITIES ) {
		savegame.Error( "idGameLocal::InitFromSaveGame: invalid first free entity index %d", firstFreeIndex );
	}
	GameLocal_ValidateSaveGameCount( savegame, num_entities, MAX_GENTITIES, "entity count" );

	// enityHash is restored by idEntity::Restore setting the entity name.

	savegame.ReadObject( reinterpret_cast<idClass *&>( world ) );

	savegame.ReadInt( num );
	GameLocal_ValidateSaveGameCount( savegame, num, MAX_GENTITIES, "spawned entity count" );
	for( i = 0; i < num; i++ ) {
		savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
		assert( ent );
		if ( ent ) {
			ent->spawnNode.AddToEnd( spawnedEntities );
		}
	}

// RAVEN BEGIN
// abahr: save scriptObject proxies
	savegame.ReadInt( num );
	GameLocal_ValidateSaveGameCount( savegame, num, MAX_GENTITIES + MAX_CENTITIES, "script object proxy count" );
	scriptObjectProxies.SetNum( num );
	for( i = 0; i < num; ++i ) {
		scriptObjectProxies[i].Restore( &savegame );
	}
// abahr: save client entity stuff
	rvClientEntity* clientEnt = NULL;
	savegame.ReadInt( num );
	GameLocal_ValidateSaveGameCount( savegame, num, MAX_CENTITIES, "client spawned entity count" );
	for( i = 0; i < num; ++i ) {
		savegame.ReadObject( reinterpret_cast<idClass *&>( clientEnt ) );
		assert( clientEnt );
		if ( clientEnt  ) {
			clientEnt->spawnNode.AddToEnd( clientSpawnedEntities );
		}
	}
// RAVEN END

	savegame.ReadInt( num );
	GameLocal_ValidateSaveGameCount( savegame, num, MAX_GENTITIES, "active entity count" );
	for( i = 0; i < num; i++ ) {
		savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
		assert( ent );
		if ( ent ) {
			ent->activeNode.AddToEnd( activeEntities );
		}
	}

	savegame.ReadInt( numEntitiesToDeactivate );
	GameLocal_ValidateSaveGameCount( savegame, numEntitiesToDeactivate, MAX_GENTITIES, "deactivation count" );
	savegame.ReadBool( sortPushers );
	savegame.ReadBool( sortTeamMasters );
	savegame.ReadDict( &persistentLevelInfo );

	for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
		savegame.ReadFloat( globalShaderParms[ i ] );
	}

	savegame.ReadInt( i );
	random.SetSeed( i );

	savegame.ReadObject( reinterpret_cast<idClass *&>( frameCommandThread ) );

	// clip
	// push
	// pvs

	// testmodel = "<NULL>"
	// testFx = "<NULL>"

	savegame.ReadString( sessionCommand );
// RAVEN BEGIN
// nmckenzie: Let the AI system load itself too.
	aiManager.Restore( &savegame );
// RAVEN END

	// FIXME: save smoke particles

	savegame.ReadInt( cinematicSkipTime );
	savegame.ReadInt( cinematicStopTime );
	savegame.ReadInt( cinematicMaxSkipTime );
	savegame.ReadBool( inCinematic );
	savegame.ReadBool( skipCinematic );

	savegame.ReadBool( isMultiplayer );
	savegame.ReadInt( (int &)gameType );

	savegame.ReadInt( framenum );
	savegame.ReadInt( previousTime );
	savegame.ReadInt( time );

	savegame.ReadInt( vacuumAreaNum );

	savegame.ReadInt( entityDefBits );
	savegame.ReadBool( isServer );
	savegame.ReadBool( isClient );
// RAVEN BEGIN
	savegame.ReadBool( isListenServer );
// RAVEN END

	savegame.ReadInt( localClientNum );

	// snapshotEntities is used for multiplayer only

	savegame.ReadInt( realClientTime );
	savegame.ReadBool( isNewFrame );

	savegame.ReadBool( mapCycleLoaded );
	savegame.ReadInt( spawnCount );

	savegame.ReadInt( num );
	if ( num < 0 ) {
		savegame.Error( "idGameLocal::InitFromSaveGame: invalid location entity area count %d", num );
	}
	if ( num ) {
		if ( num != gameRenderWorld->NumAreas() ) {
			savegame.Error( "idGameLocal::InitFromSaveGame: number of areas in map differs from save game." );
		}

		locationEntities = new idLocationEntity *[ num ];
		for( i = 0; i < num; i++ ) {
			savegame.ReadObject( reinterpret_cast<idClass *&>( locationEntities[ i ] ) );
		}
	}

	savegame.ReadObject( reinterpret_cast<idClass *&>( camera ) );

	savegame.ReadMaterial( globalMaterial );

// RAVEN BEGIN
// bdube: added
	lastAIAlertEntity.Restore( &savegame );
	savegame.ReadInt( lastAIAlertEntityTime );
	lastAIAlertActor.Restore( &savegame );
	savegame.ReadInt( lastAIAlertActorTime );
// RAVEN END

	savegame.ReadDict( &spawnArgs );

	savegame.ReadInt( playerPVS.i );
	savegame.ReadInt( (int &)playerPVS.h );
	savegame.ReadInt( playerConnectedAreas.i );
	savegame.ReadInt( (int &)playerConnectedAreas.h );

	savegame.ReadVec3( gravity );

	// gamestate is restored after restoring everything else

	savegame.ReadBool( influenceActive );
	savegame.ReadInt( nextGibTime );

	// spawnSpots
	// initialSpots
	// currentInitialSpot
	// newInfo
	// makingBuild
	// shakeSounds

	// Read out pending events
	idEvent::Restore( &savegame );

	savegame.RestoreObjects();

	mpGame.Reset();

	mpGame.Precache();

	// free up any unused animations
// RAVEN BEGIN
// jsinger: animationLib changed to a pointer
	animationLib->FlushUnusedAnims();
// RAVEN END

	gamestate = GAMESTATE_ACTIVE;

	Printf( "--------------------------------------\n" );

	return true;
}

/*
===========
idGameLocal::MapClear
===========
*/
// RAVEN BEGIN
// ddynerman: multiple instances 
void idGameLocal::MapClear( bool clearClients, int instance ) {
// RAVEN END
	int i;

// RAVEN BEGIN
// bdube: delete client entities first since they reference real entities
	for( i = 0; i < MAX_CENTITIES; i++ ) {
		// on the server we need to keep around client entities bound to entities in our instance2
		if( gameLocal.isServer && gameLocal.GetLocalPlayer() && instance != -1 &&
			instance != gameLocal.GetLocalPlayer()->GetInstance() && 
			clientEntities[ i ] && clientEntities[ i ]->GetBindMaster() && 
			clientEntities[ i ]->GetBindMaster()->GetInstance() == gameLocal.GetLocalPlayer()->GetInstance() ) {
			continue;
		}
		delete clientEntities[ i ];
		clientEntities[ i ] = NULL;
		clientSpawnIds[ i ] = -1;
	}
// RAVEN END

	for( i = ( clearClients ? 0 : MAX_CLIENTS ); i < MAX_GENTITIES; i++ ) {
		if( instance >= 0 && entities[ i ] && entities[ i ]->GetInstance() != instance ) {
			continue;
		}

		delete entities[ i ];
		// ~idEntity is in charge of setting the pointer to NULL
		// it will also clear pending events for this entity
		assert( !entities[ i ] );
// RAVEN BEGIN
// see FIXME in idRestoreGame::Error
		entities[ i ] = NULL;
// RAVEN END
		spawnIds[ i ] = -1;
	}

	entityHash.Clear( 1024, MAX_GENTITIES );
// RAVEN BEGIN
// rjohnson: reset spawnedEntities during clear to ensure no left over pieces that get remapped to a new id ( causing bad snapshot reading )
	if ( instance == -1 ) {
		spawnedEntities.Clear();
	}
// RAVEN END

	if ( !clearClients ) {
		// add back the hashes of the clients/stuff in other instances
		for ( i = 0; i < MAX_GENTITIES; i++ ) {
			if ( !entities[ i ] ) {
				continue;
			}
			entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i );
// RAVEN BEGIN
// rjohnson: reset spawnedEntities during clear to ensure no left over pieces that get remapped to a new id ( causing bad snapshot reading )
			if ( instance == -1 ) {
				entities[ i ]->spawnNode.AddToEnd( spawnedEntities );
			}
// RAVEN END
		}
	}

// RAVEN BEGIN
// jscott: clear out portal skies
	portalSky = NULL;
// abahr:
	gravityInfo.Clear();
	scriptObjectProxies.Clear();
// RAVEN END

	delete frameCommandThread;
	frameCommandThread = NULL;

	if ( editEntities ) {
		delete editEntities;
		editEntities = NULL;
	}

	delete[] locationEntities;
	locationEntities = NULL;
	
// RAVEN BEGIN
// ddynerman: mp clear
	if( gameLocal.isMultiplayer ) {
		ClearForwardSpawns();
		for( i = 0; i < TEAM_MAX; i++ ) {
			teamSpawnSpots[ i ].Clear();
		}
		mpGame.ClearMap();
	}
	ambientLights.Clear();
// RAVEN END

	// set the free index back at MAX_CLIENTS for registering entities again
	// the deletion of map entities causes the index to go down, but it may not have been left exactly at 32
	// under such conditions, the map populate that will follow may be offset
	firstFreeIndex = MAX_CLIENTS;
}

// RAVEN BEGIN
// ddynerman: instance-specific clear
void idGameLocal::InstanceClear( void ) {
	// note: clears all ents EXCEPT those in the instance
	int i;

	for( i = 0; i < MAX_CENTITIES; i++ ) {
		delete clientEntities[ i ];
		assert( !clientEntities[ i ] );
		clientSpawnIds[ i ] = -1;
	}

	for( i = MAX_CLIENTS; i < MAX_GENTITIES; i++ ) {
		if( i == ENTITYNUM_CLIENT || i == ENTITYNUM_WORLD || i == ENTITYNUM_NONE ) {
			continue;
		}

		if( entities[ i ] && entities[ i ]->fl.persistAcrossInstances ) {
			//common->DPrintf( "Instance %d: persistant: excluding ent from clear: %s (%s)\n", instance, entities[ i ]->name.c_str(), entities[ i ]->GetClassname() );
			continue;
		}

		//if( entities[ i ] ) {
			//Printf( "Instance %d: CLEARING ent from clear: %s (%s)\n", instance, entities[ i ]->name.c_str(), entities[ i ]->GetClassname() );
		//}
		
		delete entities[ i ];
		// ~idEntity is in charge of setting the pointer to NULL
		// it will also clear pending events for this entity
		assert( !entities[ i ] );
		// RAVEN BEGIN
		// see FIXME in idRestoreGame::Error
		entities[ i ] = NULL;
		// RAVEN END
		spawnIds[ i ] = -1;
	}

	entityHash.Clear( 1024, MAX_GENTITIES );

	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		if ( !entities[ i ] ) {
			continue;
		}
		entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i );
	}

// RAVEN BEGIN
// jscott: clear out portal skies
	portalSky = NULL;
	// abahr:
	gravityInfo.Clear();
	scriptObjectProxies.Clear();
// RAVEN END

	delete frameCommandThread;
	frameCommandThread = NULL;

	if ( editEntities ) {
		delete editEntities;
		editEntities = NULL;
	}

	delete[] locationEntities;
	locationEntities = NULL;

	// RAVEN BEGIN
	// ddynerman: mp clear
	if( gameLocal.isMultiplayer ) {
		ClearForwardSpawns();
		for( i = 0; i < TEAM_MAX; i++ ) {
			teamSpawnSpots[ i ].Clear();
		}
		mpGame.ClearMap();
	}
	ambientLights.Clear();

	nextLagoCheck = 0;
}
// RAVEN END

/*
===========
idGameLocal::MapShutdown
============
*/
void idGameLocal::MapShutdown( void ) {
	Printf( "------------ Game Map Shutdown --------------\n" );
	
	gamestate = GAMESTATE_SHUTDOWN;

	if ( soundSystem ) {
		//soundSystem->ResetListener();
	}

// RAVEN BEGIN
// rjohnson: new blur special effect
	renderSystem->ShutdownSpecialEffects();
// RAVEN END

	// clear out camera if we're in a cinematic
	if ( inCinematic ) {
		camera = NULL;
		inCinematic = false;
	}

// RAVEN BEGIN
// jscott: cleanup playbacks
	gameEdit->ShutdownPlaybacks();
// RAVEN END

	MapClear( true );

	instancesEntityIndexWatermarks.Clear();
	clientInstanceFirstFreeIndex = MAX_CLIENTS;	

// RAVEN BEGIN
// jscott: make sure any spurious events are killed
	idEvent::ClearEventList();

	// reset the script to the state it was before the map was started
	program.Restart();

// bdube: game debug
	gameDebug.Shutdown( );
	gameLogLocal.Shutdown( );
// RAVEN END

	iconManager->Shutdown();

	pvs.Shutdown();

// RAVEN BEGIN
// ddynerman: MP multiple instances
	ShutdownInstances();
// mwhitlock: Dynamic memory consolidation
	clip.Clear();
// RAVEN END
	idClipModel::ClearTraceModelCache();

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
	idForce::ClearForceList();
#endif
// RAVEN END

	ShutdownAsyncNetwork();

	// Drop this map's collision-model reference before clearing the name.  Level
	// transitions reuse the same game instance, so waiting for full game shutdown
	// leaves previous maps pinned in the fixed collision-model table.
	collisionModelManager->FreeMap( GetMapName() );
	collisionModelManager->PurgeModels();

	mapFileName.Clear();

	gameRenderWorld = NULL;

	gamestate = GAMESTATE_NOMAP;

	camera = NULL;
	portalSky = NULL;
	
	Printf( "---------------------------------------------\n" );
}

/*
===================
idGameLocal::DumpOggSounds
===================
*/
void idGameLocal::DumpOggSounds( void ) {
	int i, j, k, size, totalSize;
	idFile *file;
	idStrList oggSounds, weaponSounds;
	const idSoundShader *soundShader;
	const soundShaderParms_t *parms;
	idStr soundName;

	for ( i = 0; i < declManager->GetNumDecls( DECL_SOUND ); i++ ) {
		soundShader = static_cast<const idSoundShader *>(declManager->DeclByIndex( DECL_SOUND, i, false ));
		parms = soundShader->GetParms();

		if ( soundShader->EverReferenced() && soundShader->GetState() != DS_DEFAULTED ) {

			const_cast<idSoundShader *>(soundShader)->EnsureNotPurged();

			for ( j = 0; j < soundShader->GetNumSounds(); j++ ) {
				soundName = soundShader->GetSound( j );
				soundName.BackSlashesToSlashes();

				// don't OGG sounds that cause a shake because that would
				// cause continuous seeking on the OGG file which is expensive
				if ( parms->shakes != 0.0f ) {
					shakeSounds.AddUnique( soundName );
					continue;
				}

				// if not voice over or combat chatter
				if (	soundName.Find( "/vo/", false ) == -1 &&
						soundName.Find( "/combat_chatter/", false ) == -1 &&
						soundName.Find( "/bfgcarnage/", false ) == -1 &&
						soundName.Find( "/enpro/", false ) == - 1 &&
						soundName.Find( "/soulcube/energize_01.wav", false ) == -1 ) {
					// don't OGG weapon sounds
					if (	soundName.Find( "weapon", false ) != -1 ||
							soundName.Find( "gun", false ) != -1 ||
							soundName.Find( "bullet", false ) != -1 ||
							soundName.Find( "bfg", false ) != -1 ||
							soundName.Find( "plasma", false ) != -1 ) {
						weaponSounds.AddUnique( soundName );
						continue;
					}
				}

				for ( k = 0; k < shakeSounds.Num(); k++ ) {
					if ( shakeSounds[k].IcmpPath( soundName ) == 0 ) {
						break;
					}
				}
				if ( k < shakeSounds.Num() ) {
					continue;
				}

				oggSounds.AddUnique( soundName );
			}
		}
	}

	file = fileSystem->OpenFileWrite( "makeogg.bat", "fs_savepath" );
	if ( file == NULL ) {
		common->Warning( "Couldn't open makeogg.bat" );
		return;
	}

	// list all the shake sounds
	totalSize = 0;
	for ( i = 0; i < shakeSounds.Num(); i++ ) {
		size = fileSystem->ReadFile( shakeSounds[i], NULL, NULL );
		totalSize += size;
		shakeSounds[i].Replace( "/", "\\" );
		file->Printf( "echo \"%s\" (%d kB)\n", shakeSounds[i].c_str(), size >> 10 );
	}
	file->Printf( "echo %d kB in shake sounds\n\n\n", totalSize >> 10 );

	// list all the weapon sounds
	totalSize = 0;
	for ( i = 0; i < weaponSounds.Num(); i++ ) {
		size = fileSystem->ReadFile( weaponSounds[i], NULL, NULL );
		totalSize += size;
		weaponSounds[i].Replace( "/", "\\" );
		file->Printf( "echo \"%s\" (%d kB)\n", weaponSounds[i].c_str(), size >> 10 );
	}
	file->Printf( "echo %d kB in weapon sounds\n\n\n", totalSize >> 10 );

	// list commands to convert all other sounds to ogg
	totalSize = 0;
	for ( i = 0; i < oggSounds.Num(); i++ ) {
// RAVEN BEGIN
// rjohnson: changed path to raven's directories
// jnewquist: Use filesystem search path to get absolute file path
		idStr	tempFile;
		idFile *f = fileSystem->OpenFileRead( oggSounds[i] );
		if ( !f ) {
			continue;
		}
		size = f->Length();
		totalSize += size;
		tempFile = f->GetFullPath();
		fileSystem->CloseFile(f);
		f = NULL;
		tempFile.Replace( "/", "\\" );

// jnewquist: prevent alterations to files relative to cdpath
		const char *cdPath = cvarSystem->GetCVarString("fs_cdpath");
		const int cdPathLen = idStr::Length(cdPath);
		if ( cdPathLen > 0 && idStr::Icmpn(cdPath, tempFile, cdPathLen) == 0 ) {
			file->Printf( "rem Ignored file from CD path: %s\n", tempFile.c_str() );
			continue;
		}

		file->Printf( "echo %d / %d\n", i, oggSounds.Num() );
		file->Printf( "k:\\utility\\oggenc -q 0 \"%s\"\n", tempFile.c_str() );
		file->Printf( "del \"%s\"\n", tempFile.c_str() );
// RAVEN END
	}
	file->Printf( "\n\necho %d kB in OGG sounds\n\n\n", totalSize >> 10 );

	fileSystem->CloseFile( file );

	shakeSounds.Clear();
}

/*
===================
idGameLocal::GetShakeSounds
===================
*/
void idGameLocal::GetShakeSounds( const idDict *dict ) {
	const idSoundShader *soundShader;
	const char *soundShaderName;
	idStr soundName;

	soundShaderName = dict->GetString( "s_shader" );
	if ( soundShaderName != '\0' && dict->GetFloat( "s_shakes" ) != 0.0f ) {
		soundShader = declManager->FindSound( soundShaderName );

		for ( int i = 0; i < soundShader->GetNumSounds(); i++ ) {
			soundName = soundShader->GetSound( i );
			soundName.BackSlashesToSlashes();

			shakeSounds.AddUnique( soundName );
		}
	}
}

/*
===================
idGameLocal::BeginMapMediaPrecache
===================
*/
void idGameLocal::BeginMapMediaPrecache( void ) {
	mapMediaPrecacheSeen.Clear();
	mapMediaPrecacheRequests = 0;
	mapMediaPrecacheSkips = 0;
	mapMediaPrecacheActive = !cvarSystem->GetCVarBool( "com_makingBuild" );
}

/*
===================
idGameLocal::EndMapMediaPrecache
===================
*/
void idGameLocal::EndMapMediaPrecache( void ) {
	if ( mapMediaPrecacheActive && cvarSystem->GetCVarBool( "com_showLevelLoadTimes" ) && mapMediaPrecacheRequests > 0 ) {
		Printf( "Game media precache dedup: skipped=%d unique=%d requests=%d\n",
			mapMediaPrecacheSkips,
			mapMediaPrecacheSeen.Num(),
			mapMediaPrecacheRequests );
	}
	mapMediaPrecacheActive = false;
}

/*
===================
idGameLocal::RememberMapMediaPrecache
===================
*/
bool idGameLocal::RememberMapMediaPrecache( const char *kind, const char *value ) {
	if ( !mapMediaPrecacheActive || value == NULL || value[0] == '\0' ) {
		return false;
	}

	mapMediaPrecacheRequests++;

	idStr key = kind != NULL ? kind : "";
	key += ":";
	key += value;
	key.BackSlashesToSlashes();
	key.ToLower();

	if ( mapMediaPrecacheSeen.Get( key.c_str() ) ) {
		mapMediaPrecacheSkips++;
		return true;
	}

	int remembered = 1;
	mapMediaPrecacheSeen.Set( key.c_str(), remembered );
	return false;
}

/*
===================
idGameLocal::CacheDictionaryMedia

This is called after parsing an EntityDef and for each entity spawnArgs before
merging the entitydef.  It could be done post-merge, but that would
avoid the fast pre-cache check associated with each entityDef
===================
*/
void idGameLocal::CacheDictionaryMedia( const idDict *dict ) {
	idDict spawnerArgs;
	
	TIME_THIS_SCOPE( __FUNCLINE__);

	if ( dict == NULL ) {
#ifndef _CONSOLE
		if ( cvarSystem->GetCVarBool( "com_makingBuild") ) {
			DumpOggSounds();
		}
#endif
		return;
	}

#ifndef _CONSOLE
	if ( cvarSystem->GetCVarBool( "com_makingBuild" ) ) {
		GetShakeSounds( dict );
	}
#endif

	int numVals = dict->GetNumKeyVals();
	
	for ( int i = 0; i < numVals; ++i ) {
		const idKeyValue *kv = dict->GetKeyVal( i );
		
		#define MATCH(s) \
			(!kv->GetKey().Icmpn( s, strlen(s) ))
		/**/
		
		if ( !kv || !kv->GetValue().Length() ) {
			continue;
		}
		
		if ( MATCH( "model" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MODEL);
			if ( RememberMapMediaPrecache( "model", kv->GetValue() ) ) {
				continue;
			}
			declManager->MediaPrint( "Precaching model %s\n", kv->GetValue().c_str() );
			// precache model/animations
			if ( declManager->FindType( DECL_MODELDEF, kv->GetValue(), false ) == NULL ) {
				// precache the render model
				renderModelManager->FindModel( kv->GetValue() );
				// precache .cm files only
				collisionModelManager->PreCacheModel( GetMapName(), kv->GetValue() );
			}
		} else if ( MATCH( "s_shader" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_SOUND);
			if ( RememberMapMediaPrecache( "sound", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_SOUND, kv->GetValue() );
		} else if ( MATCH( "snd_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_SOUND);
			if ( RememberMapMediaPrecache( "sound", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_SOUND, kv->GetValue() );
		} else if ( MATCH( "gui_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_GUI);
			if ( !idStr::Icmp( kv->GetKey(), "gui_noninteractive" )
				|| !idStr::Icmpn( kv->GetKey(), "gui_parm", 8 )	
				|| !idStr::Icmp( kv->GetKey(), "gui_inventory" ) ) {
				// unfortunate flag names, they aren't actually a gui
			} else {
				if ( RememberMapMediaPrecache( "gui", kv->GetValue() ) ) {
					continue;
				}
				declManager->MediaPrint( "Precaching gui %s\n", kv->GetValue().c_str() );
				uiManager->FindGui( kv->GetValue().c_str(), true );
			}
		} else if ( MATCH( "texture" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			if ( RememberMapMediaPrecache( "material", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_MATERIAL, kv->GetValue() );
		} else if ( MATCH( "mtr_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			if ( RememberMapMediaPrecache( "material", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_MATERIAL, kv->GetValue() );
		} else if ( MATCH( "screenShot" ) ) {
		
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			if ( RememberMapMediaPrecache( "material", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_MATERIAL, kv->GetValue() );
		} else if ( MATCH( "inv_icon" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			if ( RememberMapMediaPrecache( "material", kv->GetValue() ) ) {
				continue;
			}
			declManager->FindType( DECL_MATERIAL, kv->GetValue() );
		} else if ( MATCH( "teleport" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_EFFECT);
			int teleportType = atoi( kv->GetValue() );
			const char *p = ( teleportType ) ? va( "effects/teleporter%i.fx", teleportType ) : "effects/teleporter.fx";
			if ( RememberMapMediaPrecache( "effect", p ) ) {
				continue;
			}
			declManager->FindType( DECL_EFFECT, p );
		} else if ( MATCH( "fx_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_EFFECT);
			if ( RememberMapMediaPrecache( "effect", kv->GetValue() ) ) {
				continue;
			}
			declManager->MediaPrint( "Precaching fx %s\n", kv->GetValue().c_str() );
			declManager->FindEffect( kv->GetValue() );
		} else if ( MATCH( "skin" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			if ( RememberMapMediaPrecache( "skin", kv->GetValue() ) ) {
				continue;
			}
			declManager->MediaPrint( "Precaching skin %s\n", kv->GetValue().c_str() );
			declManager->FindType( DECL_SKIN, kv->GetValue() );
		} else if ( MATCH( "def_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_DECL);
			if ( RememberMapMediaPrecache( "def", kv->GetValue() ) ) {
				continue;
			}
			FindEntityDef( kv->GetValue().c_str(), false );
		} else if ( MATCH( "playback_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_ANIM);
			if ( RememberMapMediaPrecache( "playback", kv->GetValue() ) ) {
				continue;
			}
			declManager->MediaPrint( "Precaching playback %s\n", kv->GetValue().c_str() );
			declManager->FindPlayback( kv->GetValue() );
		} else if ( MATCH( "lipsync_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_ANIM);
			if ( RememberMapMediaPrecache( "lipsync", kv->GetValue() ) ) {
				continue;
			}
			declManager->MediaPrint( "Precaching lipsync %s\n", kv->GetValue().c_str() );
			declManager->FindLipSync( kv->GetValue() );
			declManager->FindSound ( kv->GetValue() );
		} else if ( MATCH( "icon " ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_MATERIAL);
			idStr iconPrecacheKey = kv->GetKey();
			iconPrecacheKey += "=";
			iconPrecacheKey += kv->GetValue();
			if ( RememberMapMediaPrecache( "icon", iconPrecacheKey.c_str() ) ) {
				continue;
			}
			idLexer  src ( LEXFL_ALLOWPATHNAMES );
			idToken  token;
			idToken	 token2;
			src.LoadMemory( kv->GetValue(), kv->GetValue().Length(), "icon" );
			
			src.ReadToken ( &token );
			if ( src.CheckTokenString ( "," ) ) {
				int x, y, w, h;
				src.ReadToken ( &token2 ) ;
				x = token2.GetIntValue ( );
				src.ExpectTokenString ( "," );
				
				src.ReadToken ( &token2 ) ;
				y = token2.GetIntValue ( );
				src.ExpectTokenString ( "," );

				src.ReadToken ( &token2 ) ;
				w = token2.GetIntValue ( );
				src.ExpectTokenString ( "," );

				src.ReadToken ( &token2 ) ;
				h = token2.GetIntValue ( );
				
				uiManager->RegisterIcon ( kv->GetKey ( ).c_str() + 5, token, x, y, w, h );
			} else { 
				uiManager->RegisterIcon ( kv->GetKey ( ).c_str() + 5, token );
			}
		} else if ( MATCH( "spawn_" ) ) {
			
			TIME_THIS_SCOPE( __FUNCLINE__);
			
			MEM_SCOPED_TAG(tag,MA_DECL);
			spawnerArgs.Set ( kv->GetKey ( ).c_str() + 6, kv->GetValue ( ) );
		}
		
		#undef MATCH
	}

	if ( spawnerArgs.GetNumKeyVals() ) {
		CacheDictionaryMedia ( &spawnerArgs );
	}
// RAVEN END
}

/*
===========
idGameLocal::InitScriptForMap
============
*/
void idGameLocal::InitScriptForMap( void ) {

// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG(tag,MA_DEFAULT);
// RAVEN END
	// create a thread to run frame commands on
	frameCommandThread = new idThread();
	frameCommandThread->ManualDelete();
	frameCommandThread->SetThreadName( "frameCommands" );


// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG_SET(tag,MA_SCRIPT);

	// run the main game script function (not the level specific main)
	const function_t *func = program.FindFunction( SCRIPT_DEFAULTFUNC );


// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG_SET(tag,MA_DEFAULT);

	if ( func != NULL ) {
		idThread *thread = new idThread( func );
		if ( thread->Start() ) {
			// thread has finished executing, so delete it
			delete thread;
		}
	}

// RAVEN END

}

/*
===========
idGameLocal::SpawnPlayer
============
*/
void idGameLocal::SpawnPlayer( int clientNum, bool isBot, const char* botName ) {
	(void)isBot;
	(void)botName;

	TIME_THIS_SCOPE( __FUNCLINE__);

	idEntity	*ent;
	idDict		args;
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
	MEM_SCOPED_TAG(tag,MA_ENTITY);
// RAVEN END

	// they can connect
	common->DPrintf( "SpawnPlayer: %i\n", clientNum );

	args.SetInt( "spawn_entnum", clientNum );
	args.Set( "name", va( "player%d", clientNum + 1 ) );
// RAVEN BEGIN
// bdube: changed marine class
	args.Set( "classname", idPlayer::GetSpawnClassname() );
// RAVEN END
	
	// This takes a really long time.
	PACIFIER_UPDATE;
	if ( !SpawnEntityDef( args, &ent ) || !entities[ clientNum ] ) {
		Error( "Failed to spawn player as '%s'", args.GetString( "classname" ) );
	}

	// make sure it's a compatible class
	if ( !ent->IsType( idPlayer::GetClassType() ) ) {
		Error( "'%s' spawned the player as a '%s'.  Player spawnclass must be a subclass of idPlayer.", args.GetString( "classname" ), ent->GetClassname() );
	}

	PACIFIER_UPDATE;

	if ( clientNum != ENTITYNUM_NONE ) {
		if ( clientNum >= numClients ) {
			numClients = clientNum + 1;
		}
	}

	mpGame.SpawnPlayer( clientNum );
}

/*
================
idGameLocal::GetClientByNum
================
*/
idPlayer *idGameLocal::GetClientByNum( int current ) const {
	if ( current == MAX_CLIENTS ) {
		return NULL;
	}
	if ( current == ENTITYNUM_NONE ) {
		return static_cast<idPlayer *>( entities[ current ] );
	}
	if ( current < 0 || current >= numClients ) {
		// that's a bit nasty but I suppose it has it's use
		current = 0;
	}
	if ( entities[current] ) {
		return static_cast<idPlayer *>( entities[ current ] );
	}
	return NULL;
}

/*
================
idGameLocal::GetClientByName
================
*/
idPlayer *idGameLocal::GetClientByName( const char *name ) const {
	int i;
	idEntity *ent;
	for ( i = 0 ; i < numClients ; i++ ) {
		ent = entities[ i ];
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
// RAVEN BEGIN
// bdube: escape codes
			if ( idStr::IcmpNoEscape( name, userInfo[ i ].GetString( "ui_name" ) ) == 0 ) {
// RAVEN END			
				return static_cast<idPlayer *>( ent );
			}
		}
	}
	return NULL;
}

/*
================
idGameLocal::GetClientByCmdArgs
================
*/
idPlayer *idGameLocal::GetClientByCmdArgs( const idCmdArgs &args ) const {
	idPlayer *player;
	idStr client = args.Argv( 1 );
	if ( !client.Length() ) {
		return NULL;
	}
	// we don't allow numeric ui_name so this can't go wrong
	if ( client.IsNumeric() ) {
		player = GetClientByNum( atoi( client.c_str() ) );
	} else {
		player = GetClientByName( client.c_str() );
	}
	if ( !player ) {
		common->Printf( "Player '%s' not found\n", client.c_str() );
	}
	return player;
}

/*
================
idGameLocal::GetClientNumByName
================
*/
int idGameLocal::GetClientNumByName( const char *name ) const {
	int i;
	idEntity *ent;
	for ( i = 0 ; i < numClients ; i++ ) {
		ent = entities[ i ];

		if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
			if ( idStr::IcmpNoEscape( name, userInfo[ i ].GetString( "ui_name" ) ) == 0 ) {
				return i;
			}
		}
	}
	return -1;
}

/*
================
idGameLocal::GetNextClientNum
================
*/
int idGameLocal::GetNextClientNum( int _current ) const {
	int i, current;

	current = 0;
	for ( i = 0; i < numClients; i++) {
		current = ( _current + i + 1 ) % numClients;
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( entities[ current ] && entities[ current ]->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
			return current;
		}
	}

	return current;
}

/*
================
idGameLocal::GetLocalPlayer

Nothing in the game tic should EVER make a decision based on what the
local client number is, it shouldn't even be aware that there is a
draw phase even happening.

localClientNum == MAX_CLIENTS when playing server demos
don't send back the wrong entity obviously
================
*/
idPlayer *idGameLocal::GetLocalPlayer() const {
	if ( localClientNum == MAX_CLIENTS ) {
		return static_cast<idPlayer *>( entities[ ENTITYNUM_NONE ] );
	}

	if ( localClientNum < 0 || localClientNum >= MAX_CLIENTS ) {
		return NULL;
	}

	if ( !entities[ localClientNum ] || !entities[ localClientNum ]->IsType( idPlayer::GetClassType() ) ) {
		// not fully in game yet
		return NULL;
	}
	
	return static_cast<idPlayer *>( entities[ localClientNum ] );
}

/*
================
idGameLocal::SetupClientPVS
for client spectating others, get the pvs of spectated
================
*/
pvsHandle_t idGameLocal::GetClientPVS( idPlayer *player, pvsType_t type ) {
	if ( player->GetPrivateCameraView() ) {
		return pvs.SetupCurrentPVS( player->GetPrivateCameraView()->GetPVSAreas(), player->GetPrivateCameraView()->GetNumPVSAreas() );
	} else if ( camera ) {
		return pvs.SetupCurrentPVS( camera->GetPVSAreas(), camera->GetNumPVSAreas() );
	} else {
		if ( player->spectating && player->spectator != player->entityNumber && entities[ player->spectator ] ) {
			player = static_cast<idPlayer*>( entities[ player->spectator ] );
		}
		return pvs.SetupCurrentPVS( player->GetPVSAreas(), player->GetNumPVSAreas() );
	}
}

// RAVEN BEGIN
// jscott: for portal skies
/*
================
idGameLocal::GetSpawnCount
================
*/
int idGameLocal::GetSpawnCount ( void ) const {
	return spawnCount;
}

/*
================
idGameLocal::SetupPortalSkyPVS
================
*/
bool idGameLocal::SetupPortalSkyPVS( idPlayer *player ) {

	int			i, count, numAreas;
	const int	*areaNums;
	bool		*visibleAreas;

	if( !portalSky ) {

		return( false );
	}

	// Allocate room for the area flags
	numAreas = gameRenderWorld->NumAreas();
	visibleAreas = ( bool * )_alloca( numAreas );
	memset( visibleAreas, 0, numAreas );

	// Grab the areas the player can see....
	count = player->GetNumPVSAreas();
	areaNums = player->GetPVSAreas();
	for( i = 0; i < count; i++ ) {

		// Work out the referenced areas
		gameRenderWorld->FindVisibleAreas( player->GetPhysics()->GetOrigin(), areaNums[i], visibleAreas );
	}

	// Do any of the visible areas have a skybox?
	for( i = 0; i < numAreas; i++ ) {

		if( !visibleAreas[i] ) {

			continue;
		}

		if( gameRenderWorld->HasSkybox( i ) ) {

			break;
		}
	}

	// .. if any one has a skybox component, then merge in the portal sky
	return ( i != numAreas );
}
// RAVEN END

/*
===============
idGameLocal::UpdateClientsPVS
===============
*/
void idGameLocal::UpdateClientsPVS( void ) {
	int i;
	for ( i = 0; i < numClients; i++ ) {
		if ( !entities[ i ] ) {
			continue;
		}
		assert( clientsPVS[ i ].i == -1 );
		clientsPVS[ i ] = GetClientPVS( static_cast< idPlayer * >( entities[ i ] ), PVS_NORMAL );
	}
}

/*
================
idGameLocal::SetupPlayerPVS
================
*/
void idGameLocal::SetupPlayerPVS( void ) {
	int			i = 0;
	idPlayer *	player = NULL;
	pvsHandle_t	otherPVS, newPVS;

	UpdateClientsPVS( );

	playerPVS.i = -1;
	for ( i = 0; i < numClients; i++ ) {
		if ( !entities[i] ) {
			return;
		}
		assert( entities[i]->IsType( idPlayer::GetClassType() ) );

		player = static_cast<idPlayer *>( entities[ i ] );

		if ( playerPVS.i == -1 ) {
			playerPVS = clientsPVS[ i ];
			freePlayerPVS = false;	// don't try to free it as long as we stick to the client PVS
		} else {
			otherPVS = clientsPVS[ i ];
			newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS );
			if ( freePlayerPVS ) {
				pvs.FreeCurrentPVS( playerPVS );
				freePlayerPVS = false;
			}
			playerPVS = newPVS;
			freePlayerPVS = true;	// that merged one will need to be freed
		}

// RAVEN BEGIN
// jscott: for portal skies
		portalSkyVisible = SetupPortalSkyPVS( player );
		if ( portalSkyVisible ) {

			otherPVS = pvs.SetupCurrentPVS( portalSky->GetPVSAreas(), portalSky->GetNumPVSAreas() );
			newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS );

			if ( freePlayerPVS ) {
				pvs.FreeCurrentPVS( playerPVS );
				freePlayerPVS = false;
			}
			pvs.FreeCurrentPVS( otherPVS );
			playerPVS = newPVS;
			freePlayerPVS = true;
		}
// RAVEN END

		if ( playerConnectedAreas.i == -1 ) {
			playerConnectedAreas = GetClientPVS( player, PVS_CONNECTED_AREAS );
		} else {
			otherPVS = GetClientPVS( player, PVS_CONNECTED_AREAS );
			newPVS = pvs.MergeCurrentPVS( playerConnectedAreas, otherPVS );
			pvs.FreeCurrentPVS( playerConnectedAreas );
			pvs.FreeCurrentPVS( otherPVS );
			playerConnectedAreas = newPVS;
		}
	}
}

/*
================
idGameLocal::FreePlayerPVS
================
*/
void idGameLocal::FreePlayerPVS( void ) {
	int i;

	// only clear playerPVS if it's a different handle than the one in clientsPVS
	if ( freePlayerPVS && playerPVS.i != -1 ) {
		pvs.FreeCurrentPVS( playerPVS );
		freePlayerPVS = false;
	}
	playerPVS.i = -1;
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		if ( clientsPVS[ i ].i >= 0 ) {
			pvs.FreeCurrentPVS( clientsPVS[ i ] );
			clientsPVS[i].i = -1;
		}
	}
	if ( playerConnectedAreas.i != -1 ) {
		pvs.FreeCurrentPVS( playerConnectedAreas );
		playerConnectedAreas.i = -1;
	}
}

/*
================
idGameLocal::InPlayerPVS

  should only be called during entity thinking and event handling
================
*/
bool idGameLocal::InPlayerPVS( idEntity *ent ) const {
	if ( playerPVS.i == -1 ) {
		return false;
	}
    return pvs.InCurrentPVS( playerPVS, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
}

/*
================
idGameLocal::InPlayerConnectedArea

  should only be called during entity thinking and event handling
================
*/
bool idGameLocal::InPlayerConnectedArea( idEntity *ent ) const {
	if ( playerConnectedAreas.i == -1 ) {
		return false;
	}
    return pvs.InCurrentPVS( playerConnectedAreas, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
}


/*
================
idGameLocal::UpdateGravity
================
*/
void idGameLocal::UpdateGravity( void ) {
	idEntity *ent;
	
	idCVar* gravityCVar = NULL;

	if( gameLocal.isMultiplayer ) {
		gravityCVar = &g_mp_gravity;
	} else {
		gravityCVar = &g_gravity;
	}

	if ( gravityCVar->IsModified() ) {
		if ( gravityCVar->GetFloat() == 0.0f ) {
			gravityCVar->SetFloat( 1.0f );
		}
        gravity.Set( 0, 0, -gravityCVar->GetFloat() );

		// update all physics objects
		for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
			if ( ent->IsType( idAFEntity_Generic::GetClassType() ) ) {
// RAVEN END
				idPhysics *phys = ent->GetPhysics();
				if ( phys ) {
					phys->SetGravity( gravity );
				}
// RAVEN BEGIN
// ddynerman: jump pads
			} else if ( ent->IsType( rvJumpPad::GetClassType() ) ) {
				ent->PostEventMS( &EV_FindTargets, 0 );
			}
// RAVEN END
		}
		gravityCVar->ClearModified();
	}
}

/*
================
idGameLocal::GetGravity
================
*/
const idVec3 &idGameLocal::GetGravity( void ) const {
	return gravity;
}

/*
================
idGameLocal::SortActiveEntityList

  Sorts the active entity list such that pushing entities come first,
  actors come next and physics team slaves appear after their master.
================
*/
void idGameLocal::SortActiveEntityList( void ) {
	idEntity *ent, *next_ent, *master, *part;

	// if the active entity list needs to be reordered to place physics team masters at the front
	if ( sortTeamMasters ) {
		// Sort bind masters first
		for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
			next_ent = ent->activeNode.Next();
			for ( part = ent->GetBindMaster ( ); part; part = part->GetBindMaster ( ) ) {
				// Ensure we dont rerun the whole active entity list if our cached next_ent is one 
				// of the entities we are moving
				if ( next_ent == part ) {
					next_ent = next_ent->activeNode.Next();
					part = ent->GetBindMaster ( );
					continue;
				}
				part->activeNode.Remove();
				part->activeNode.AddToFront( activeEntities );												
			}
		}				
	
		for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
			next_ent = ent->activeNode.Next();
			master = ent->GetTeamMaster();
			if ( master && master == ent ) {
				ent->activeNode.Remove();
				ent->activeNode.AddToFront( activeEntities );												
			}
		}
	}

	// if the active entity list needs to be reordered to place pushers at the front
	if ( sortPushers ) {

		for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
			next_ent = ent->activeNode.Next();
			master = ent->GetTeamMaster();
			if ( !master || master == ent ) {
				// check if there is an actor on the team
				for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
					if ( part->GetPhysics()->IsType( idPhysics_Actor::GetClassType() ) ) {
// RAVEN END
						break;
					}
				}
				// if there is an actor on the team
				if ( part ) {
					ent->activeNode.Remove();
					ent->activeNode.AddToFront( activeEntities );
				}
			}
		}

		for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
			next_ent = ent->activeNode.Next();
			master = ent->GetTeamMaster();
			if ( !master || master == ent ) {
				// check if there is an entity on the team using parametric physics
				for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
					if ( part->GetPhysics()->IsType( idPhysics_Parametric::GetClassType() ) ) {
						break;
					}
					if ( part->GetPhysics()->IsType( rvPhysics_Spline::GetClassType() ) ) {
						break;
					}
				}
				// if there is an entity on the team using parametric physics
				if ( part ) {
					ent->activeNode.Remove();
					ent->activeNode.AddToFront( activeEntities );
				}
			}
		}
	}

	sortTeamMasters = false;
	sortPushers = false;
}

/*
================
idGameLocal::MenuFrame
Called each session frame when a map is not running (e.g. usually in the main menu)
================
*/
void idGameLocal::MenuFrame( void ) { }

/*
================
idGameLocal::RunFrame
================
*/
// RAVEN BEGIN
gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds, int activeEditors, bool lastCatchupFrame, int serverGameFrame ) {
	idEntity *	ent;
	int			num;
	float		ms;
	idTimer		timer_think, timer_events, timer_singlethink;
	idTimer		timer_misc, timer_misc2;
	gameReturn_t ret;
	idPlayer	*player;
	const renderView_t *view;

	editors = activeEditors;
	isLastPredictFrame = lastCatchupFrame;
	mHz = common->GetUserCmdHz();

	assert( !isClient );

	player = GetLocalPlayer();

	do {

		previousTime = time;

		// sanity - game frame is counted on both sides of the DLL fence
		// but I've seen cases where it would be offset and we need to make sure that doesn't happen
		if ( serverGameFrame != framenum ) {
			common->Warning( "core and game frame counters are out of sync: %d/%d", serverGameFrame, framenum );
			if ( serverGameFrame > framenum ) {
				int delta = serverGameFrame - framenum;
				time += delta * GetMSec();
				framenum = serverGameFrame;
			} if ( serverGameFrame < framenum ) {
				// don't do anything, let the core catchup to us
				memset( &ret, 0, sizeof( ret ) );
				return ret;
			}
		}
		
		// update the game time
		framenum++;
		// bdube: use GetMSec access rather than USERCMD_TIME
		time += GetMSec();
		msec = time - previousTime;

		realClientTime = time;
		{
TIME_THIS_SCOPE("idGameLocal::RunFrame - gameDebug.BeginFrame()");
		// bdube: added advanced debug support
		gameDebug.BeginFrame( );
		gameLogLocal.BeginFrame( time );
		}

#ifdef GAME_DLL
		// allow changing SIMD usage on the fly
		if ( com_forceGenericSIMD.IsModified() ) {
			idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
		}
#endif

		// make sure the random number counter is used each frame so random events
		// are influenced by the player's actions
		random.RandomInt();

		if ( player && !( inCinematic && skipCinematic ) ) {
			// update the renderview so that any gui videos play from the right frame
			view = player->GetRenderView();
			if ( view ) {
				gameRenderWorld->SetRenderView( view );
			}
		}

		// If modview is running then let it think
		common->ModViewThink( );	

		// rjohnson: added option for guis to always think
		common->RunAlwaysThinkGUIs( time );

		// nmckenzie: Let AI System stuff update itself.
		if ( !isMultiplayer ) {
#ifndef _MPBETA
			aiManager.RunFrame();
#endif // !_MPBETA
		}

		timer_misc.Start();
		
		// set the user commands for this frame
		usercmds = clientCmds;

		// create a merged pvs for all players
		// do this before we process events, which may rely on PVS info
		SetupPlayerPVS();

		// process events on the server
		ServerProcessEntityNetworkEventQueue();

		// update our gravity vector if needed.
		UpdateGravity();

		if ( isLastPredictFrame ) {
			// jscott: effect system uses gravity and the player PVS
			bse->StartFrame();
		}

		// sort the active entity list
		SortActiveEntityList();

		timer_think.Clear();
		timer_think.Start();

		// jscott: for timing and effect handling
		timer_misc2.Start();
		timer_misc.Stop();

		// let entities think
		if ( g_timeentities.GetFloat() ) {
			// rjohnson: will now draw entity info for long thinkers
			idPlayer *player;
			idVec3 origin;

			player = GetLocalPlayer();
			if ( player ) {
				origin = player->GetPhysics()->GetOrigin();
			}

			idBounds	viewTextBounds( origin );
			viewTextBounds.ExpandSelf( 128.0f );

			num = 0;

			for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
				if ( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) {
					ent->GetPhysics()->UpdateTime( time );
					continue;
				}
				timer_singlethink.Clear();
				timer_singlethink.Start();
				// ddynerman: save the current thinking entity for instance-dependent
				currentThinkingEntity = ent;
				ent->Think();
				currentThinkingEntity = NULL;
				timer_singlethink.Stop();
				ms = timer_singlethink.Milliseconds();
				if ( ms >= g_timeentities.GetFloat() ) {
					// rjohnson: will now draw entity info for long thinkers
					Printf( "%d: entity '%s' [%s]: %.1f ms\n", time, ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString(), ms );

					if ( ms >= g_timeentities.GetFloat() * 3.0f )
					{
						ent->mLastLongThinkColor = colorRed;
					}
					else
					{
						ent->mLastLongThinkColor[0] = 1.0f;
						ent->mLastLongThinkColor[1] = 2.0f - (( ms - g_timeentities.GetFloat()) / g_timeentities.GetFloat() );
						ent->mLastLongThinkColor[2] = 0.0f;
						ent->mLastLongThinkColor[3] = 1.0f;
					}
					ent->DrawDebugEntityInfo( 0, &viewTextBounds, &ent->mLastLongThinkColor );
					ent->mLastLongThinkTime = time + 2000;
				}
				else if ( ent->mLastLongThinkTime > time )
				{
					ent->DrawDebugEntityInfo( 0, &viewTextBounds, &ent->mLastLongThinkColor );
				}
				num++;
			}
		} else {
			if ( inCinematic ) {
				num = 0;
				for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
					if ( g_cinematic.GetBool() && !ent->cinematic ) {
						ent->GetPhysics()->UpdateTime( time );
						continue;
					}
					// ddynerman: save the current thinking entity for instance-dependent
					currentThinkingEntity = ent;
					ent->Think();
					currentThinkingEntity = NULL;
					num++;
				}
			} else {
				num = 0;
				for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
					// ddynerman: save the current thinking entity for instance-dependent
					currentThinkingEntity = ent;
					ent->Think();
					currentThinkingEntity = NULL;
					num++;
				}
			}
		}

		// remove any entities that have stopped thinking
		if ( numEntitiesToDeactivate ) {
			idEntity *next_ent;
			int c = 0;
			for( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
				next_ent = ent->activeNode.Next();
				if ( !ent->thinkFlags ) {
					ent->activeNode.Remove();
					c++;
				}
			}
			//assert( numEntitiesToDeactivate == c );
			numEntitiesToDeactivate = 0;
		}

		timer_think.Stop();
		timer_events.Clear();
		timer_events.Start();

		if ( isLastPredictFrame ) {
			// bdube: client entities
			rvClientEntity* cent;
			for( cent = clientSpawnedEntities.Next(); cent != NULL; cent = cent->spawnNode.Next() ) {
				cent->Think();			
			}	
		}

		// service any pending events
		idEvent::ServiceEvents();

		// nrausch: player could have been deleted in an event
		player = GetLocalPlayer();

		timer_events.Stop();

		if ( isLastPredictFrame ) {
			// jscott: effect system uses gravity and the player PVS
			bse->EndFrame();
		}

		// do multiplayer related stuff
		if ( isMultiplayer ) {
			mpGame.Run();
			CheckAutoExecAfterMapLoad();
		}

		// free the player pvs
		FreePlayerPVS();

		// display how long it took to calculate the current game frame
		if ( g_frametime.GetBool() ) {
			Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n",
				time, timer_think.Milliseconds() + timer_events.Milliseconds(),
				timer_think.Milliseconds(), timer_events.Milliseconds(), num );
		}

		// build the return value
		ret.consistencyHash = 0;
		ret.sessionCommand[0] = 0;

		// see if a target_sessionCommand has forced a changelevel
		if ( sessionCommand.Length() ) {
			strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) );
			sessionCommand = "";
			break;
		}

		// make sure we don't loop forever when skipping a cinematic
		if ( skipCinematic && ( time > cinematicMaxSkipTime ) ) {
			Warning( "Exceeded maximum cinematic skip length.  Cinematic may be looping infinitely." );
			skipCinematic = false;
			break;
		}

		// jscott: additional timings
		timer_misc2.Stop();

		if ( g_frametime.GetInteger() > 1 )  {
			gameLocal.Printf( "misc:%.1f misc2:%.1f\n", timer_misc.Milliseconds(), timer_misc2.Milliseconds() );
		}
		// bdube: let gameDebug know that its not in a game frame anymore
		gameDebug.EndFrame( );
		gameLogLocal.EndFrame( );

	} while( ( inCinematic || ( time < cinematicStopTime ) ) && skipCinematic );

	ret.syncNextGameFrame = skipCinematic;
	if ( skipCinematic ) {
		//soundSystem->EndCinematic();
		soundSystem->SetMute( false );
		skipCinematic = false;		
	}

	// show any debug info for this frame
	RunDebugInfo();
	D_DrawDebugLines();

	g_simpleItems.ClearModified();
	return ret;
}
// RAVEN END

/*
================
idGameLocal::RepeaterFrame
================
*/
void idGameLocal::RepeaterFrame( const userOrigin_t *clientOrigins, bool lastCatchupFrame, int serverGameFrame ) {
	int i;

	// passing serverGameFrame is strictly a sanity check
	// the adjustment in case of problem in RunFrame should cascade to here and never be a long term problem
	static int FrameDelta = 0;
	if ( serverGameFrame != framenum ) {
		// don't want to spam the warning. don't have a good repro case to fix this but still want to track the error (seen it happen)
		int delta = serverGameFrame - framenum;
		if ( delta != FrameDelta ) {
			if ( framenum != 0 ) {	// to skip printing the initial messages
				common->Warning( "repeater frame and game frame out of sync: %d/%d", serverGameFrame, framenum );
			}
			FrameDelta = delta;
		}
	} else {
		if ( FrameDelta != 0 ) {
			common->Warning( "repeater in sync with game frame at %d", serverGameFrame );
		}
		FrameDelta = 0;
	}

	ReallocViewers( cvarSystem->GetCVarInteger( "ri_maxViewers" ) );

	for ( i = 0; i < maxViewer; i++ ) {
		if ( !viewers[ i ].active ) {
			continue;
		}

		if ( viewers[ i ].nopvs ) {
			continue;
		}

		viewers[ i ].origin = clientOrigins[ i ];

		if ( viewers[ i ].origin.followClient >= 0 && entities[ viewers[ i ].origin.followClient ] && entities[ viewers[ i ].origin.followClient ]->IsType( idPlayer::GetClassType() ) ) {
			// if the viewer is following someone, use their origin
			idPlayer *player = static_cast< idPlayer * >( entities[ viewers[ i ].origin.followClient ] );
			viewers[ i ].origin.origin = player->GetEyePosition();
		}

		viewers[ i ].pvsArea = pvs.GetPVSArea( viewers[ i ].origin.origin );
	}
}

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

  Game view drawing

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

/*
====================
idGameLocal::GetScreenAspectRatio
====================
*/
float idGameLocal::GetScreenAspectRatio( void ) const {
	int screenWidth = ( renderSystem != NULL ) ? renderSystem->GetScreenWidth() : 0;
	int screenHeight = ( renderSystem != NULL ) ? renderSystem->GetScreenHeight() : 0;

	if ( screenWidth > 0 && screenHeight > 0 ) {
		return ( float )screenWidth / ( float )screenHeight;
	}

	if ( cvarSystem != NULL ) {
		screenWidth = cvarSystem->GetCVarInteger( "r_customWidth" );
		screenHeight = cvarSystem->GetCVarInteger( "r_customHeight" );
		if ( screenWidth > 0 && screenHeight > 0 ) {
			return ( float )screenWidth / ( float )screenHeight;
		}
	}

	return 4.0f / 3.0f;
}

/*
====================
idGameLocal::CalcFov

Calculates the horizontal and vertical field of view based on a horizontal field of view and custom aspect ratio
====================
*/
void idGameLocal::CalcFov( float base_fov, float &fov_x, float &fov_y ) const {
	float	x;
	float	y;
	const float referenceAspect = 4.0f / 3.0f;
	const float aspectRatio = idMath::ClampFloat( 0.1f, 10.0f, GetScreenAspectRatio() );

// RAVEN BEGIN
// jnewquist: Option to adjust vertical fov instead of horizontal for non 4:3 modes
	if ( g_fixedHorizFOV.GetBool() ) {
		x = aspectRatio / idMath::Tan( base_fov / 360.0f * idMath::PI );
		y = idMath::ATan( 1.0f, x );
		fov_y = y * 360.0f / idMath::PI;
		fov_x = base_fov;
		return;
	}
// RAVEN END

	// first, calculate the vertical fov from the legacy 4:3 baseline.
	x = referenceAspect / idMath::Tan( base_fov / 360.0f * idMath::PI );
	y = idMath::ATan( 1.0f, x );
	fov_y = y * 360.0f / idMath::PI;

	// FIXME: somehow, this is happening occasionally
	assert( fov_y > 0 );
	if ( fov_y <= 0 ) {
		Printf( sys->FPU_GetState() );
		Error( "idGameLocal::CalcFov: bad result" );
	}

	if ( idMath::Fabs( aspectRatio - referenceAspect ) < 0.001f ) {
		fov_x = base_fov;
		return;
	}

	y = 1.0f / idMath::Tan( fov_y / 360.0f * idMath::PI );
	fov_x = idMath::ATan( aspectRatio, y ) * 360.0f / idMath::PI;

	if ( fov_x < base_fov ) {
		fov_x = base_fov;
		x = aspectRatio / idMath::Tan( fov_x / 360.0f * idMath::PI );
		fov_y = idMath::ATan( 1.0f, x ) * 360.0f / idMath::PI;
	}

	// FIXME: somehow, this is happening occasionally
	assert( ( fov_x > 0 ) && ( fov_y > 0 ) );
	if ( ( fov_y <= 0 ) || ( fov_x <= 0 ) ) {
		Printf( sys->FPU_GetState() );
		Error( "idGameLocal::CalcFov: bad result" );
	}
}

void ClearClipProfile( void );
void DisplayClipProfile( void );

/*
================
idGameLocal::Draw

makes rendering and sound system calls
================
*/
bool idGameLocal::Draw( int clientNum ) {
//	DisplayClipProfile( );
//	ClearClipProfile( );

	if ( isMultiplayer ) {
		const bool drawn = mpGame.Draw( clientNum );
		if ( drawn ) {
			CheckAutoExecAfterMapLoad();
		}
		return drawn;
	}

	idPlayer *player = static_cast<idPlayer *>(entities[ clientNum ]);

	if ( !player ) {
		return false;
	}

// RAVEN BEGIN
// mwhitlock: Xenon texture streaming.
#if defined(_XENON)
	renderView_t *view = player->GetRenderView();
	// nrausch: view doesn't necessarily exist yet
	if ( !view ) {
		player->CalculateRenderView();
		view = player->GetRenderView();
	}
#endif
// RAVEN END
	// render the scene
	player->playerView.RenderPlayerView( player->hud );

// RAVEN BEGIN
// bdube: debugging HUD
	gameDebug.DrawHud( );
// RAVEN END

	CheckAutoExecAfterMapLoad();
	return true;
}

/*
================
idGameLocal::CheckAutoExecAfterMapLoad

Execute a one-shot cfg after the first active map frames, for deterministic validation harnesses.
================
*/
void idGameLocal::CheckAutoExecAfterMapLoad( void ) {
	if ( gamestate != GAMESTATE_ACTIVE ) {
		return;
	}

	const char *execPath = g_autoExecAfterMapLoad.GetString();
	if ( !execPath || execPath[ 0 ] == '\0' ) {
		execPath = cvarSystem->GetCVarString( "g_autoExecAfterMapLoad" );
	}
	const bool wantAutoExec = execPath && execPath[ 0 ] != '\0';

	if ( wantAutoExec ) {
		if ( !autoExecAfterMapLoadPending ) {
			autoExecAfterMapLoadPending = true;
			autoExecAfterMapLoadStartTime = Sys_Milliseconds();
			if ( !autoExecAfterMapLoadWaitingLogged ) {
				common->Printf( "AutoExecAfterMapLoad: waiting for first active draw\n" );
				autoExecAfterMapLoadWaitingLogged = true;
			}
			common->Printf( "AutoExecAfterMapLoad: first active draw observed; waiting %d ms before exec\n",
				Max( 0, g_autoExecAfterMapLoadDelayMs.GetInteger() ) );
		}
	} else if ( autoExecAfterMapLoadPending || autoExecAfterMapLoadWaitingLogged ) {
		common->Printf( "AutoExecAfterMapLoad: skipped because g_autoExecAfterMapLoad was cleared before execution\n" );
		autoExecAfterMapLoadPending = false;
		autoExecAfterMapLoadWaitingLogged = false;
	}

	if ( !autoExecAfterMapLoadPending ) {
		return;
	}

	const int nowMs = Sys_Milliseconds();
	int delayMs = g_autoExecAfterMapLoadDelayMs.GetInteger();
	if ( delayMs < 0 ) {
		delayMs = 0;
	}
	if ( delayMs > 0 && ( nowMs - autoExecAfterMapLoadStartTime ) < delayMs ) {
		return;
	}

	bool safePath = true;
	for ( const char *scan = execPath; scan && *scan; scan++ ) {
		if ( *scan == '\n' || *scan == '\r' || *scan == ';' || *scan == '"' ) {
			safePath = false;
			break;
		}
	}

	if ( safePath ) {
		idCmdArgs execArgs;
		execArgs.AppendArg( "exec" );
		execArgs.AppendArg( execPath );
		common->Printf( "AutoExecAfterMapLoad: executed %s at %d ms\n",
			execPath,
			nowMs - autoExecAfterMapLoadStartTime );
		cmdSystem->BufferCommandArgs( CMD_EXEC_NOW, execArgs );
	} else {
		common->Warning( "AutoExecAfterMapLoad: skipped unsafe cfg path '%s'", execPath ? execPath : "" );
	}

	autoExecAfterMapLoadPending = false;
	autoExecAfterMapLoadWaitingLogged = false;
	g_autoExecAfterMapLoad.SetString( "" );
	cvarSystem->SetCVarString( "g_autoExecAfterMapLoad", "" );
}

/*
================
idGameLocal::HandleESC
================
*/
escReply_t idGameLocal::HandleESC( idUserInterface **gui ) {

//RAVEN BEGIN
//asalmon: xbox dedicated server needs to bring up a special menu
#ifdef _XBOX
	if( cvarSystem->GetCVarBool( "net_serverDedicated" ))
	{
		return ESC_MAIN;
	}
#endif
//RAVEN END

	if ( isMultiplayer ) {
		*gui = StartMenu();
		// we may set the gui back to NULL to hide it
		return ESC_GUI;
	}
	idPlayer *player = GetLocalPlayer();
	if ( player ) {
		if ( player->HandleESC() ) {
			return ESC_IGNORE;
		} else {
			return ESC_MAIN;
		}
	}
	return ESC_MAIN;
}

/*
================
idGameLocal::UpdatePlayerPostMainMenu
================
*/
void idGameLocal::UpdatePlayerPostMainMenu()	{
	idPlayer* player = GetLocalPlayer();

	//dedicated server?
	if( !player)	{
		return;
	}	
	
	//crosshair may have changed
	player->UpdateHudWeapon();
}

/*
================
idGameLocal::StartMenu
================
*/
idUserInterface* idGameLocal::StartMenu( void ) {
	if ( !isMultiplayer ) {
		return NULL;
	}
	return mpGame.StartMenu();
}

/*
================
idGameLocal::GetClientStats
================
*/
void idGameLocal::GetClientStats( int clientNum, char *data, const int len ) {
	mpGame.PlayerStats( clientNum, data, len );
}

/*
================
idGameLocal::SwitchTeam
================
*/
void idGameLocal::SwitchTeam( int clientNum, int team ) {

	idPlayer *   player;
	player = clientNum >= 0 ? static_cast<idPlayer *>( gameLocal.entities[ clientNum ] ) : NULL;

	if ( !player ) {
		return;
	}

	int oldTeam = player->team;

	// Put in spectator mode
	if ( team == -1 ) {
		static_cast< idPlayer * >( entities[ clientNum ] )->Spectate( true );
	}
	// Switch to a team
	else {
		mpGame.SwitchToTeam ( clientNum, oldTeam, team );
	}
}

/*
================
idGameLocal::HandleGuiCommands
================
*/
const char* idGameLocal::HandleGuiCommands( const char *menuCommand ) {
	if ( !isMultiplayer ) {
		return NULL;
	}
	return mpGame.HandleGuiCommands( menuCommand );
}

/*
================
idGameLocal::HandleMainMenuCommands
================
*/
void idGameLocal::HandleMainMenuCommands( const char *menuCommand, idUserInterface *gui ) {
	if ( !idStr::Icmp( menuCommand, "initCreateServerSettings" ) ) {
		int	guiValue = 0;

		switch ( mpGame.GameTypeToVote( si_gameType.GetString() ) ) {
			case idMultiplayerGame::VOTE_GAMETYPE_DM:
				guiValue = 0;
				break;
			case idMultiplayerGame::VOTE_GAMETYPE_TOURNEY:
				guiValue = 1;
				break;
			case idMultiplayerGame::VOTE_GAMETYPE_TDM:
				guiValue = 2;
				break;
			case idMultiplayerGame::VOTE_GAMETYPE_CTF:
				guiValue = 3;
				break;
			case idMultiplayerGame::VOTE_GAMETYPE_ARENA_CTF:
				guiValue = 4;
				break;
			case idMultiplayerGame::VOTE_GAMETYPE_DEADZONE:
				guiValue = 5;
				break;
		}

		gui->SetStateInt( "currentGametype", guiValue );
	} else if ( !idStr::Icmp( menuCommand, "filterByNextMod" ) ) {
		BuildModList();

		if ( modList.Num() > 0 && (filterMod < 0 || filterMod >= modList.Num()) ) {
			filterMod = 0;
			networkSystem->UseSortFunction( filterByMod, true );
			gui->SetStateString( "filterMod", modList[ filterMod ].c_str() );
		} else {
			++filterMod;
			if ( filterMod < modList.Num() ) {
				networkSystem->UseSortFunction( filterByMod, true );
				gui->SetStateString( "filterMod", modList[ filterMod ].c_str() );
			} else {
				filterMod = -1;
				networkSystem->UseSortFunction( filterByMod, false );
				gui->SetStateString( "filterMod", common->GetLocalizedString( "#str_123008" ) );
			}
		}
	} else if ( !idStr::Icmp( menuCommand, "filterByPrevMod" ) ) {
		BuildModList();

		if ( modList.Num() > 0 && (filterMod < 0 || filterMod >= modList.Num()) ) {
			filterMod = modList.Num() - 1;
			networkSystem->UseSortFunction( filterByMod, true );
			gui->SetStateString( "filterMod", modList[ filterMod ].c_str() );
		} else {
			--filterMod;
			if ( filterMod >= 0 ) {
				networkSystem->UseSortFunction( filterByMod, true );
				gui->SetStateString( "filterMod", modList[ filterMod ].c_str() );
			} else {
				filterMod = -1;
				networkSystem->UseSortFunction( filterByMod, false );
				gui->SetStateString( "filterMod", common->GetLocalizedString( "#str_123008" ) );
			}
		}
	} else if ( !idStr::Icmp( menuCommand, "updateFilterByMod" ) ) {
		if ( filterMod < 0 || filterMod >= modList.Num() ) {
			gui->SetStateString( "filterMod", common->GetLocalizedString( "#str_123008" ) );
		} else {
			gui->SetStateString( "filterMod", modList[ filterMod ].c_str() );
		}
	} else if ( !idStr::Icmp( menuCommand, "server_clearSort" ) ) {
		filterMod = -1;
		gui->SetStateString( "filterMod", common->GetLocalizedString( "#str_123008" ) );
	}

	return;
}

/*
================
idGameLocal::GetLevelMap

  should only be used for in-game level editing
================
*/
idMapFile *idGameLocal::GetLevelMap( void ) {
// RAVEN BEGIN
// rhummer: Added the HasBeenResolved check, if resolve has been run then it doesn't have func_groups.
//			Which we probably don't want, so force the map to be reloaded.
	if ( mapFile && mapFile->HasPrimitiveData() && !mapFile->HasBeenResloved() ) {
// RAVEN END
		return mapFile;
	}
	if ( !mapFileName.Length() ) {
		return NULL;
	}

	if ( mapFile ) {
		delete mapFile;
	}

	mapFile = new idMapFile;
	if ( !mapFile->Parse( mapFileName ) ) {
		delete mapFile;
		mapFile = NULL;
	}

	return mapFile;
}

/*
================
idGameLocal::GetMapName
================
*/
const char *idGameLocal::GetMapName( void ) const {
	return mapFileName.c_str();
}

/*
================
idGameLocal::CallFrameCommand
================
*/
void idGameLocal::CallFrameCommand( idEntity* ent, const char* frameCommand ) {
	const function_t *func = program.FindFunction( frameCommand );
	if ( !func ) {
		Warning( "Script function '%s' not found.", frameCommand );
		return;
	}
	CallFrameCommand ( ent, func );
}

void idGameLocal::CallFrameCommand( idEntity *ent, const function_t *frameCommand ) {
	frameCommandThread->CallFunction( ent, frameCommand, true );
	frameCommandThread->Execute();
}

/*
================
idGameLocal::CallObjectFrameCommand
================
*/
void idGameLocal::CallObjectFrameCommand( idEntity *ent, const char *frameCommand ) {
	const function_t *func;

	func = ent->scriptObject.GetFunction( frameCommand );
	if ( !func ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( !ent->IsType( idTestModel::GetClassType() ) ) {
// RAVEN END
			Error( "Unknown function '%s' called for frame command on entity '%s'", frameCommand, ent->name.c_str() );
		}
	} else {
		frameCommandThread->CallFunction( ent, func, true );
		frameCommandThread->Execute();
	}
}

/*
================
idGameLocal::ShowTargets
================
*/
void idGameLocal::ShowTargets( void ) {
	idMat3		axis = GetLocalPlayer()->viewAngles.ToMat3();
	idVec3		up = axis[ 2 ] * 5.0f;
	const idVec3 &viewPos = GetLocalPlayer()->GetPhysics()->GetOrigin();
	idBounds	viewTextBounds( viewPos );
	idBounds	viewBounds( viewPos );
	idBounds	box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) );
	idEntity	*ent;
	idEntity	*target;
	int			i;
	idBounds	totalBounds;

	viewTextBounds.ExpandSelf( 128.0f );
	viewBounds.ExpandSelf( 512.0f );
	for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
		totalBounds = ent->GetPhysics()->GetAbsBounds();
		for( i = 0; i < ent->targets.Num(); i++ ) {
			target = ent->targets[ i ].GetEntity();
			if ( target ) {
				totalBounds.AddBounds( target->GetPhysics()->GetAbsBounds() );
			}
		}

		if ( !viewBounds.IntersectsBounds( totalBounds ) ) {
			continue;
		}

		float dist;
		idVec3 dir = totalBounds.GetCenter() - viewPos;
		dir.NormalizeFast();
		totalBounds.RayIntersection( viewPos, dir, dist );
		float frac = ( 512.0f - dist ) / 512.0f;
		if ( frac < 0.0f ) {
			continue;
		}

		gameRenderWorld->DebugBounds( ( ent->IsHidden() ? colorLtGrey : colorOrange ) * frac, ent->GetPhysics()->GetAbsBounds() );
		if ( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
			idVec3 center = ent->GetPhysics()->GetAbsBounds().GetCenter();
			gameRenderWorld->DrawText( ent->name.c_str(), center - up, 0.1f, colorWhite * frac, axis, 1 );
			gameRenderWorld->DrawText( ent->GetEntityDefName(), center, 0.1f, colorWhite * frac, axis, 1 );
			gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), center + up, 0.1f, colorWhite * frac, axis, 1 );
		}

		for( i = 0; i < ent->targets.Num(); i++ ) {
			target = ent->targets[ i ].GetEntity();
			if ( target ) {
				gameRenderWorld->DebugArrow( colorYellow * frac, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 );
				gameRenderWorld->DebugBounds( colorGreen * frac, box, target->GetPhysics()->GetOrigin() );
			}
		}
	}
}

/*
================
idGameLocal::RunDebugInfo
================
*/
void idGameLocal::RunDebugInfo( void ) {
	idEntity *ent;
	idPlayer *player;

	player = GetLocalPlayer();
	if ( !player ) {
		return;
	}

	const idVec3 &origin = player->GetPhysics()->GetOrigin();

	if ( g_showEntityInfo.GetBool() ) {
		idBounds	viewTextBounds( origin );
		idBounds	viewBounds( origin );

		viewTextBounds.ExpandSelf( 128.0f );
		viewBounds.ExpandSelf( 512.0f );
		for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
			// don't draw the worldspawn
			if ( ent == world ) {
				continue;
			}

// RAVEN BEGIN
// rjohnson: moved entity info out of idGameLocal into its own function
			ent->DrawDebugEntityInfo(&viewBounds, &viewTextBounds);
// RAVEN END
		}
	}

	// debug tool to draw bounding boxes around active entities
	if ( g_showActiveEntities.GetBool() ) {
		for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
			idBounds	b = ent->GetPhysics()->GetBounds();
			if ( b.GetVolume() <= 0 ) {
				b[0][0] = b[0][1] = b[0][2] = -8;
				b[1][0] = b[1][1] = b[1][2] = 8;
			}
			if ( ent->fl.isDormant ) {
				gameRenderWorld->DebugBounds( colorYellow, b, ent->GetPhysics()->GetOrigin() );
			} else {
				gameRenderWorld->DebugBounds( colorGreen, b, ent->GetPhysics()->GetOrigin() );
			}
		}
	}

// RAVEN BEGIN
// bdube: client entities
	if ( cl_showEntityInfo.GetBool ( ) ) {
		rvClientEntity* cent;
		for( cent = clientSpawnedEntities.Next(); cent != NULL; cent = cent->spawnNode.Next() ) {
			cent->DrawDebugInfo ( );
		}				
	}
// RAVEN END

	if ( g_showTargets.GetBool() ) {
		ShowTargets();
	}

	if ( g_showTriggers.GetBool() ) {
		idTrigger::DrawDebugInfo();
	}

	if ( ai_showCombatNodes.GetBool() ) {
//		idCombatNode::DrawDebugInfo();
	}

	if ( ai_showPaths.GetBool() ) {
		idPathCorner::DrawDebugInfo();
	}

	if ( g_editEntityMode.GetBool() ) {
		if ( gameLocal.isMultiplayer ) 	{
			g_editEntityMode.SetInteger(0);
			Printf( "Not allowed in multiplayer.\n" );
		} else {
			editEntities->DisplayEntities();
		}
	}

	if ( g_showCollisionWorld.GetBool() ) {
		// use g_maxShowDistance value instead of 128.0f
		collisionModelManager->DrawModel( clip[0]->GetWorldCollisionModel(), vec3_origin, mat3_identity, origin, mat3_identity, g_maxShowDistance.GetFloat() );
	}

	if ( g_showCollisionModels.GetBool() ) {
		if( g_showCollisionModels.GetInteger() == 2 ) {
			clip[ 0 ]->DrawClipModels( player->GetEyePosition(), g_maxShowDistance.GetFloat(), pm_thirdPerson.GetBool() ? NULL : player, &idPlayer::GetClassType() );
		} else {
			clip[ 0 ]->DrawClipModels( player->GetEyePosition(), g_maxShowDistance.GetFloat(), pm_thirdPerson.GetBool() ? NULL : player );
		}
		
	}

	if ( g_showCollisionTraces.GetBool() ) {
		clip[ 0 ]->PrintStatistics();
	}

	if ( g_showPVS.GetInteger() ) {
		pvs.DrawPVS( origin, ( g_showPVS.GetInteger() == 2 ) ? PVS_ALL_PORTALS_OPEN : PVS_NORMAL );
	}

// RAVEN BEGIN
// rjohnson: added debug hud
	if ( gameDebug.IsHudActive ( DBGHUD_PHYSICS ) )	{
		clip[ 0 ]->DebugHudStatistics();
	}

	clip[ 0 ]->ClearStatistics();
// RAVEN END

	if ( aas_test.GetInteger() >= 0 ) {
		idAAS *aas = GetAAS( aas_test.GetInteger() );
		if ( aas ) {
			aas->Test( origin );
			if ( ai_testPredictPath.GetBool() ) {
				idVec3 velocity;
				predictedPath_t path;

				velocity.x = idMath::Cos( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
				velocity.y = idMath::Sin( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
				velocity.z = 0.0f;
				idAI::PredictPath( player, aas, origin, velocity, 1000, 100, SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA, path );
			}
		}
// RAVEN BEGIN 
// rjohnson: added more debug drawing
		if ( aas_showAreas.GetInteger() == 3 ) {
			for( int i=0; i<aasNames.Num(); i++ ) {
				aas = GetAAS( i );
				if ( !aas || !aas->IsValid() ) {
					continue;
				}

				if ( aas->GetSettings()->debugDraw ) {
					aas->ShowAreas( origin );
				}
			}
		}
		if ( aas_showProblemAreas.GetInteger() == 3 ) {
			for( int i=0; i<aasNames.Num(); i++ ) {
				aas = GetAAS( i );
				if ( !aas || !aas->IsValid() ) {
					continue;
				}

				if ( aas->GetSettings()->debugDraw ) {
					aas->ShowAreas( origin, true );
				}
			}
		}
// RAVEN END
	}

	if ( ai_showObstacleAvoidance.GetInteger() == 2 ) {
		idAAS *aas = GetAAS( 0 );
		if ( aas ) {
			idVec3 seekPos;
			obstaclePath_t path;

			seekPos = player->GetPhysics()->GetOrigin() + player->viewAxis[0] * 200.0f;
			idAI::FindPathAroundObstacles( player->GetPhysics(), aas, NULL, player->GetPhysics()->GetOrigin(), seekPos, path );
		}
	}

	// collision map debug output
	idVec3 debugViewOrigin;
	idMat3 debugViewAxis;
	player->GetViewPos( debugViewOrigin, debugViewAxis );
	collisionModelManager->DebugOutput( debugViewOrigin, debugViewAxis );

// RAVEN BEGIN
// jscott: for debugging playbacks
	if( g_showPlayback.GetInteger() ) {
		gameEdit->DrawPlaybackDebugInfo();
	}
// RAVEN END

// RAVEN BEGIN
// ddynerman: SD's clip sector code
	if ( g_showClipSectors.GetBool() ) {
		clip[ 0 ]->DrawClipSectors();
	}

	if ( g_showAreaClipSectors.GetFloat() ) {
		clip[ 0 ]->DrawAreaClipSectors( g_showAreaClipSectors.GetFloat() );
	}
// RAVEN END
}

/*
==================
idGameLocal::NumAAS
==================
*/
int	idGameLocal::NumAAS( void ) const {
	return aasList.Num();
}

/*
==================
idGameLocal::GetAAS
==================
*/
idAAS *idGameLocal::GetAAS( int num ) const {
	if ( ( num >= 0 ) && ( num < aasList.Num() ) ) {
		if ( aasList[ num ] && aasList[ num ]->GetSettings() ) {
			return aasList[ num ];
		}
	}
	return NULL;
}

/*
==================
idGameLocal::GetAAS
==================
*/
idAAS *idGameLocal::GetAAS( const char *name ) const {
	int i;

	for ( i = 0; i < aasNames.Num(); i++ ) {
		if ( aasNames[ i ] == name ) {
			if ( !aasList[ i ]->GetSettings() ) {
				return NULL;
			} else {
				return aasList[ i ];
			}
		}
	}
	return NULL;
}

/*
==================
idGameLocal::SetAASAreaState
==================
*/
void idGameLocal::SetAASAreaState( const idBounds &bounds, const int areaContents, bool closed ) {
	int i;

	for( i = 0; i < aasList.Num(); i++ ) {
		aasList[ i ]->SetAreaState( bounds, areaContents, closed );
	}
}

/*
==================
idGameLocal::AddAASObstacle
==================
*/
aasHandle_t idGameLocal::AddAASObstacle( const idBounds &bounds ) {
	int i;
	aasHandle_t obstacle;
	aasHandle_t check;

	if ( !aasList.Num() ) {
		return -1;
	}

	obstacle = aasList[ 0 ]->AddObstacle( bounds );
	for( i = 1; i < aasList.Num(); i++ ) {
		check = aasList[ i ]->AddObstacle( bounds );
		assert( check == obstacle );
	}

	return obstacle;
}

/*
==================
idGameLocal::RemoveAASObstacle
==================
*/
void idGameLocal::RemoveAASObstacle( const aasHandle_t handle ) {
	int i;

	for( i = 0; i < aasList.Num(); i++ ) {
		aasList[ i ]->RemoveObstacle( handle );
	}
}

/*
==================
idGameLocal::RemoveAllAASObstacles
==================
*/
void idGameLocal::RemoveAllAASObstacles( void ) {
	int i;

	for( i = 0; i < aasList.Num(); i++ ) {
		aasList[ i ]->RemoveAllObstacles();
	}
}

// RAVEN BEGIN
// mwhitlock: added entity memory usage stuff.
/*
==================
idGameLocal::GetEntityMemoryUsage

Compute combined total memory footprint of server and client entity storage.
==================
*/

size_t idGameLocal::GetEntityMemoryUsage ( void ) const {

	// Server ents.
	size_t serverEntitiesSize = 0;
	for( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
		 serverEntitiesSize += sizeof( idEntity );
	}
	
	// Client ents.
	size_t clientEntitiesSize = 0;
	for( rvClientEntity *ent = gameLocal.clientSpawnedEntities.Next() ; ent != NULL; ent = ent->spawnNode.Next() ) {
		 clientEntitiesSize += ent->Size();
	}
	
	return serverEntitiesSize + clientEntitiesSize;
}
// RAVEN END

/*
==================
idGameLocal::CheatsOk
==================
*/
bool idGameLocal::CheatsOk( bool requirePlayer ) {
	idPlayer *player;

	if ( isMultiplayer && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
		Printf( "Not allowed in multiplayer.\n" );
		return false;
	}

	if ( !developer.GetBool() ) {
		return false;
	}

	player = GetLocalPlayer();
	if ( !requirePlayer || ( player && ( player->health > 0 ) ) ) {
		return true;
	}

	Printf( "You must be alive to use this command.\n" );

	return false;
}

/*
===================
idGameLocal::RegisterEntity
===================
*/
void idGameLocal::RegisterEntity( idEntity *ent ) {
	int spawn_entnum;

	if ( spawnCount >= ( 1 << ( 32 - GENTITYNUM_BITS ) ) ) {
		Error( "idGameLocal::RegisterEntity: spawn count overflow" );
	}

	firstFreeIndex = Max( minSpawnIndex, firstFreeIndex );

	if ( !spawnArgs.GetInt( "spawn_entnum", "0", spawn_entnum ) ) {
		while ( entities[firstFreeIndex] && firstFreeIndex < ENTITYNUM_MAX_NORMAL ) {
			firstFreeIndex++;
		}
		if ( firstFreeIndex >= ENTITYNUM_MAX_NORMAL ) {
			Error( "no free entities" );
		}
		spawn_entnum = firstFreeIndex++;
	} else {
		assert( spawn_entnum < MAX_CLIENTS || spawn_entnum >= minSpawnIndex );
	}

	entities[ spawn_entnum ] = ent;
	spawnIds[ spawn_entnum ] = spawnCount++;
	ent->entityNumber = spawn_entnum;
	ent->spawnNode.AddToEnd( spawnedEntities );
	ent->spawnArgs.TransferKeyValues( spawnArgs );

	if ( spawn_entnum >= num_entities ) {
		num_entities++;
	}

// RAVEN BEGIN
// bdube: keep track of last time an entity was registered	
	entityRegisterTime = time;
// RAVEN END
}

/*
===================
idGameLocal::UnregisterEntity
===================
*/
void idGameLocal::UnregisterEntity( idEntity *ent ) {
	assert( ent );

	if ( editEntities ) {
		editEntities->RemoveSelectedEntity( ent );
	}

	if ( ( ent->entityNumber != ENTITYNUM_NONE || ent == entities[ ENTITYNUM_NONE ] ) && ( entities[ ent->entityNumber ] == ent ) ) {
		ent->spawnNode.Remove();
		entities[ ent->entityNumber ] = NULL;
		spawnIds[ ent->entityNumber ] = -1;
		if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < firstFreeIndex ) {
			firstFreeIndex = ent->entityNumber;
		}
		ent->entityNumber = ENTITYNUM_NONE;
	}

// RAVEN BEGIN
// bdube: keep track of last time an entity was registered	
	entityRegisterTime = time;
// RAVEN END
}

/*
===============
idGameLocal::SkipEntityIndex
===============
*/
void idGameLocal::SkipEntityIndex( void ) {
	assert( entities[ firstFreeIndex ] == NULL );
	firstFreeIndex++;
	if ( firstFreeIndex >= ENTITYNUM_MAX_NORMAL ) {
		Error( "no free entities" );
	}
}

/*
================
idGameLocal::SpawnEntityType
================
*/
idEntity *idGameLocal::SpawnEntityType( const idTypeInfo &classdef, const idDict *args, bool bIsClientReadSnapshot ) {
	idClass *obj;

#ifdef _DEBUG
	if ( isClient ) {
		assert( bIsClientReadSnapshot );
	}
#endif

// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
	if ( !classdef.IsType( idEntity::GetClassType() ) ) {
// RAVEN END
		Error( "Attempted to spawn non-entity class '%s'", classdef.classname );
	}

	try {
		if ( args ) {
			spawnArgs = *args;
		} else {
			spawnArgs.Clear();
		}
		obj = classdef.CreateInstance();
		obj->CallSpawn();
	}
	
	catch( idAllocError & ) {
		obj = NULL;
	}
	spawnArgs.Clear();

	return static_cast<idEntity *>(obj);
}

/*
===================
idGameLocal::SpawnClientEntityDef

Finds the spawn function for the client entity and calls it,
returning false if not found
===================
*/
bool idGameLocal::SpawnClientEntityDef( const idDict &args, rvClientEntity **cent, bool setDefaults, const char* spawn ) {
	const char	*classname;
	idTypeInfo	*cls;
	idClass		*obj;
	idStr		error;
	const char  *name;

	if ( cent ) {
		*cent = NULL;
	}

	spawnArgs = args;

	if ( spawnArgs.GetBool( "nospawn" ) ){
		//not meant to actually spawn, just there for some compiling process
		return false;
	}

	if ( spawnArgs.GetString( "name", "", &name ) ) {
		error = va( " on '%s'", name );
	}

	spawnArgs.GetString( "classname", NULL, &classname );

	const idDeclEntityDef *def = FindEntityDef( classname, false );
	if ( !def ) {
		// RAVEN BEGIN
		// jscott: a NULL classname would crash Warning()
		if( classname ) {
			Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
		}
		// RAVEN END
		return false;
	}

	spawnArgs.SetDefaults( &def->dict );

	// check if we should spawn a class object
	if( spawn == NULL ) {
		spawnArgs.GetString( "spawnclass", NULL, &spawn );
	}

	if ( spawn ) {

		cls = idClass::GetClass( spawn );
		if ( !cls ) {
			Warning( "Could not spawn '%s'.  Class '%s' not found%s.", classname, spawn, error.c_str() );
			return false;
		}

		obj = cls->CreateInstance();
		if ( !obj ) {
			Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
			return false;
		}

		obj->CallSpawn();

		if ( cent && obj->IsType( rvClientEntity::GetClassType() ) ) {
			*cent = static_cast<rvClientEntity*>(obj);
		}

		return true;
	}

	Warning( "%s doesn't include a spawnfunc%s.", classname, error.c_str() );
	return false;
}

/*
===================
idGameLocal::SpawnEntityDef

Finds the spawn function for the entity and calls it,
returning false if not found
===================
*/
bool idGameLocal::SpawnEntityDef( const idDict &args, idEntity **ent, bool setDefaults ) {
	const char	*classname;
	const char	*spawn;
	idTypeInfo	*cls;
	idClass		*obj;
	idStr		error;
	const char  *name;

	TIME_THIS_SCOPE( __FUNCLINE__);
	
	if ( ent ) {
		*ent = NULL;
	}

	spawnArgs = args;

	if ( spawnArgs.GetBool( "nospawn" ) )
	{//not meant to actually spawn, just there for some compiling process
		return false;
	}

	if ( spawnArgs.GetString( "name", "", &name ) ) {
// RAVEN BEGIN
// jscott: fixed sprintf to idStr
		error = va( " on '%s'", name );
// RAVEN END
	}
	spawnArgs.GetString( "classname", NULL, &classname );

	const idDeclEntityDef *def = FindEntityDef( classname, false );
	if ( !def ) {
// RAVEN BEGIN
// jscott: a NULL classname would crash Warning()
		if( classname ) {
			Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
		}
// RAVEN END
		return false;
	}

	spawnArgs.SetDefaults( &def->dict );

// RAVEN BEGIN
// rjohnson: entity usage stats
	if ( g_keepEntityStats.GetBool() ) {
		if ( idStr::Icmp( classname, "func_spawner" ) == 0 || 
			 idStr::Icmp( classname, "func_spawner_enemy" ) == 0 ) {
			// special case for spawners
			for( int i = 1; ; i++ ) {
				char		tempSpawn[128];
				const char	*tempClassname;

				sprintf( tempSpawn, "def_spawn_type%d", i );
				tempClassname = spawnArgs.GetString( tempSpawn, NULL );
				if ( tempClassname ) {
					const idDeclEntityDef *tempDef = FindEntityDef( tempClassname, false );

					if ( tempDef ) {
						idDict	tempArgs = tempDef->dict;

						tempArgs.Set( "mapFileName", mapFileNameStripped );
						entityUsageList.Insert( tempArgs );
					}
				} else {
					break;
				}
			}
		}
		else if ( def->dict.GetBool( "report_stats" ) ) {
			idDict	tempArgs = spawnArgs;
			tempArgs.Set( "mapFileName", mapFileNameStripped );
			entityUsageList.Insert( tempArgs );
		}
	}
// RAVEN END

	// check if we should spawn a class object
	spawnArgs.GetString( "spawnclass", NULL, &spawn );
	if ( spawn ) {

		cls = idClass::GetClass( spawn );
		if ( !cls ) {
			Warning( "Could not spawn '%s'.  Class '%s' not found%s.", classname, spawn, error.c_str() );
			return false;
		}

		obj = cls->CreateInstance();
		if ( !obj ) {
			Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
			return false;
		}

		obj->CallSpawn();

// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( ent && obj->IsType( idEntity::GetClassType() ) ) {
// RAVEN END
			*ent = static_cast<idEntity *>(obj);
		}

		return true;
	}

	// check if we should call a script function to spawn
	spawnArgs.GetString( "spawnfunc", NULL, &spawn );
	if ( spawn ) {
		const function_t *func = program.FindFunction( spawn );
		if ( !func ) {
			Warning( "Could not spawn '%s'.  Script function '%s' not found%s.", classname, spawn, error.c_str() );
			return false;
		}
		idThread *thread = new idThread( func );
		thread->DelayedStart( 0 );

		return true;
	}

	Warning( "%s doesn't include a spawnfunc or spawnclass%s.", classname, error.c_str() );
	return false;
}

// abahr:
idEntity* idGameLocal::SpawnEntityDef( const char* entityDefName, const idDict* additionalArgs ) {
	idDict			finalArgs;
	const idDict*	entityDict = NULL;
	idEntity*		entity = NULL;
	
	TIME_THIS_SCOPE( __FUNCLINE__);

	if( !entityDefName ) {
		return NULL;
	}

	entityDict = FindEntityDefDict( entityDefName, false );
	if( !entityDict ) {
		return NULL;
	}

	if( !additionalArgs ) {
		SpawnEntityDef( *entityDict, &entity );
	} else {
		finalArgs.Copy( *entityDict );
		finalArgs.Copy( *additionalArgs );
		SpawnEntityDef( finalArgs, &entity );
	}

	return entity;
}
// RAVEN END

/*
================
idGameLocal::FindEntityDef
================
*/
const idDeclEntityDef *idGameLocal::FindEntityDef( const char *name, bool makeDefault ) const {
	TIME_THIS_SCOPE( __FUNCLINE__);
	const idDecl *decl = NULL;
	if ( isMultiplayer ) {
		decl = declManager->FindType( DECL_ENTITYDEF, va( "%s_mp", name ), false );
	}
	if ( !decl ) {
		decl = declManager->FindType( DECL_ENTITYDEF, name, makeDefault );
	}
	return static_cast<const idDeclEntityDef *>( decl );
}

/*
================
idGameLocal::FindEntityDefDict
================
*/
const idDict *idGameLocal::FindEntityDefDict( const char *name, bool makeDefault ) const {
	TIME_THIS_SCOPE( __FUNCLINE__);
	const idDeclEntityDef *decl = FindEntityDef( name, makeDefault );
	return decl ? &decl->dict : NULL;
}

/*
================
idGameLocal::InhibitEntitySpawn
================
*/
bool idGameLocal::InhibitEntitySpawn( idDict &spawnArgs ) {
	
	bool result = false;

	if ( isMultiplayer ) {
		spawnArgs.GetBool( "not_multiplayer", "0", result );
	} else if ( g_skill.GetInteger() == 0 ) {
		spawnArgs.GetBool( "not_easy", "0", result );
	} else if ( g_skill.GetInteger() == 1 ) {
		spawnArgs.GetBool( "not_medium", "0", result );
	} else {
		spawnArgs.GetBool( "not_hard", "0", result );
	}

	const char *name;
#ifndef ID_DEMO_BUILD
	if ( g_skill.GetInteger() == 3 ) { 
		name = spawnArgs.GetString( "classname" );
		if ( idStr::Icmp( name, "item_medkit" ) == 0 || idStr::Icmp( name, "item_medkit_small" ) == 0 ) {
			result = true;
		}
	}
#endif

// RAVEN BEGIN
// bdube: suppress ents that don't match the entity filter
	const char* entityFilter;
	if ( serverInfo.GetString( "si_entityFilter", "", &entityFilter ) && *entityFilter ) {
		if ( spawnArgs.MatchPrefix ( "filter_" ) && !spawnArgs.GetBool ( va("filter_%s", entityFilter) ) ) {
			return true;
		}
	}
// RAVEN END

// RITUAL BEGIN
// squirrel: suppress ents that aren't supported in Buying modes (if that's the mode we're in)
	if ( mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
		if ( spawnArgs.GetBool( "disableSpawnInBuying", "0" ) ) {
			return true;
		}

		/// Don't spawn weapons or armor vests or ammo in Buying modes
		idStr classname = spawnArgs.GetString( "classname" );
		if( idStr::FindText( classname, "weapon_" ) == 0 || 
			idStr::FindText( classname, "item_armor_small" ) == 0 ||
			idStr::FindText( classname, "ammo_" ) == 0 ||
			idStr::FindText( classname, "item_armor_large" ) == 0 )
		{
			return true;
		}
	}
// RITUAL END

	// suppress deadzone triggers if we're not running DZ
	if ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "DeadZone" ) != 0 ) {
		if ( idStr::Icmp( spawnArgs.GetString( "classname" ), "trigger_controlzone" ) == 0 ) {
			return true;
		}
	}

	return result;
}

/*
================
idGameLocal::SetSkill
================
*/
void idGameLocal::SetSkill( int value ) {
	int skill_level;

	if ( value < 0 ) {
		skill_level = 0;
	} else if ( value > 3 ) {
		skill_level = 3;
	} else {
		skill_level = value;
	}

	g_skill.SetInteger( skill_level );
}

/*
==============
idGameLocal::GameState

Used to allow entities to know if they're being spawned during the initial spawn.
==============
*/
gameState_t	idGameLocal::GameState( void ) const {
	return gamestate;
}

/*
==============
idGameLocal::SpawnMapEntities

Parses textual entity definitions out of an entstring and spawns gentities.
==============
*/
// RAVEN BEGIN
// ddynerman: multiple game instances
void idGameLocal::SpawnMapEntities( int instance, unsigned short* entityNumIn, unsigned short* entityNumOut, int* startSpawnCount ) {
// RAVEN END
	int			i;
	int			num;
	int			inhibit;
	int			latchedSpawnCount = -1;
	idMapEntity	*mapEnt;
	int			numEntities;
	idDict		args;
	idDict		items;

	Printf( "Spawning entities\n" );
	
	TIME_THIS_SCOPE( __FUNCLINE__);

	if ( mapFile == NULL ) {
		Printf("No mapfile present\n");
		return;
	}

	SetSkill( g_skill.GetInteger() );

	numEntities = mapFile->GetNumEntities();
	if ( numEntities == 0 ) {
		Error( "...no entities" );
	}

// RAVEN BEGIN
// ddynerman:	on the server, perform world-initialization when spawning instance 0 only and while starting up
//				on the client, perform world-init when spawning while starting up, as client main instance may not be ID 0
//				always spawn world-init in single-player
	num = inhibit = 0;

	// the worldspawn is a special that performs any global setup
	// needed by a level
	if( ( gameLocal.isServer && instance == 0 && gamestate == GAMESTATE_STARTUP ) || (gameLocal.isClient && gamestate == GAMESTATE_STARTUP) || !gameLocal.isMultiplayer ) { 
		mapEnt = mapFile->GetEntity( 0 );
		args = mapEnt->epairs;
		args.SetInt( "spawn_entnum", ENTITYNUM_WORLD );
		
		// on the client, spawnCount won't always start at INITIAL_SPAWN_COUNT (see rvInstance::Populate())
		// make sure the world and physics ent get the right spawn id
		if( gameLocal.isClient && spawnCount != INITIAL_SPAWN_COUNT ) {
			latchedSpawnCount = spawnCount;
			spawnCount = INITIAL_SPAWN_COUNT;
		}

// abahr: precache stuff on worldSpawn like player def and other misc things
		CacheDictionaryMedia( &args );
// jnewquist: Use accessor for static class type
		if ( !SpawnEntityDef( args ) || !entities[ ENTITYNUM_WORLD ] || !entities[ ENTITYNUM_WORLD ]->IsType( idWorldspawn::GetClassType() ) ) {
			Error( "Problem spawning world entity" );
		}
		num++;

// bdube: dummy entity for client entities with physics
		args.Clear ( );
		args.SetInt( "spawn_entnum", ENTITYNUM_CLIENT );
// jnewquist: Use accessor for static class type 
		if ( !SpawnEntityType( rvClientPhysics::GetClassType(), &args, true ) || !entities[ ENTITYNUM_CLIENT ] ) {	
			Error( "Problem spawning client physics entity" );
		}

		if( gameLocal.isClient && latchedSpawnCount != -1 ) {
			spawnCount = latchedSpawnCount;
		}

		isMapEntity[ ENTITYNUM_CLIENT ] = true;
		isMapEntity[ ENTITYNUM_WORLD ] = true;
// RAVEN END
	}

	// capture spawn count of start of map entities (after we've spawned in the physics ents)
	if ( startSpawnCount ) {
		(*startSpawnCount) = spawnCount;
	}

	for ( i = 1 ; i < numEntities ; i++ ) {

		mapEnt = mapFile->GetEntity( i );
		args = mapEnt->epairs;

// RAVEN BEGIN
// ddynerman: merge the dicts ahead of SpawnEntityDef() so we can inhibit using merged info
		const idDeclEntityDef* entityDef = FindEntityDef( args.GetString( "classname" ), false );
		
		if( entityDef == NULL ) {
			gameLocal.Error( "idGameLocal::SpawnMapEntities() - Unknown entity classname '%s'\n", args.GetString( "classname" ) );
			return;
		}
		args.SetDefaults( &(entityDef->dict) );
// RAVEN END

		if ( !InhibitEntitySpawn( args ) ) {

			if( args.GetBool( "inv_item" ) ) {
				if( !items.GetBool( args.GetString( "inv_icon" ) ) ) {
					networkSystem->AddLoadingIcon( args.GetString( "inv_icon" ) );
					networkSystem->SetLoadingText( common->GetLocalizedString( args.GetString( "inv_name" ) ) );
					items.SetBool( args.GetString( "inv_icon" ), true );
				}
			}

			// precache any media specified in the map entity
			CacheDictionaryMedia( &args );
// RAVEN BEGIN
			if ( instance != 0 ) {
// ddynerman: allow this function to be called multiple-times to respawn map entities in other instances
				args.SetInt( "instance", instance );
				args.Set( "name", va( "%s_instance%d", args.GetString( "name" ), instance ) );
				//gameLocal.Printf( "Instance %d: Spawning %s (class: %s)\n", instance, args.GetString( "name" ), args.GetString( "classname" ) ); 
				
				// need a better way to do this - map entities that target other map entities need
				// to target the ones in the correct instance.  
				idStr target;
				if( args.GetString( "target", "", target ) ) {
					args.Set( "target", va( "%s_instance%d", target.c_str(), instance ) );
				}

				// and also udpate binds on a per-instance bases
				idStr bind;
				if( args.GetString( "bind", "", bind ) ) {
					args.Set( "bind", va( "%s_instance%d", bind.c_str(), instance ) );
				}

				// and also door triggers
				idStr triggerOnOpen;
				if( args.GetString( "triggerOnOpen", "", triggerOnOpen ) ) {
					args.Set( "triggerOnOpen", va( "%s_instance%d", triggerOnOpen.c_str(), instance ) );
				}

				idStr triggerOpened;
				if( args.GetString( "triggerOpened", "", triggerOpened ) ) {
					args.Set( "triggerOpened", va( "%s_instance%d", triggerOpened.c_str(), instance ) );
				}
			}

			idEntity* ent = NULL;
			SpawnEntityDef( args, &ent );
			//common->Printf( "pop: spawn map ent %d at %d ( %s )\n", i, ent->entityNumber, args.GetString( "name" ) );
	
			if ( ent && entityNumOut ) {
				entityNumOut[ i ] = ent->entityNumber;
			}

			if ( gameLocal.GetLocalPlayer() && ent && gameLocal.isServer && instance != gameLocal.GetLocalPlayer()->GetInstance() ) {
				// if we're spawning entities not in our instance, tell them not to draw
				ent->BecomeInactive( TH_UPDATEVISUALS );
			}

			if ( ent ) {
				isMapEntity[ ent->entityNumber ] = true;
			}
// RAVEN END
			num++;
		} else {
			// keep counting and leave an empty slot in the entity list for inhibited entities
			// this so we maintain the same layout as the server and don't change it across restarts with different inhibit schemes
			//common->Printf( "pop: skip map ent %d at index %d ( %s )\n", i, firstFreeIndex, args.GetString( "name" ) );
			SkipEntityIndex();
			inhibit++;
		}
	}

	Printf( "...%i entities spawned, %i inhibited\n\n", num, inhibit );
}

/*
================
idGameLocal::AddEntityToHash
================
*/
void idGameLocal::AddEntityToHash( const char *name, idEntity *ent ) {
	if ( FindEntity( name ) ) {
		Error( "Multiple entities named '%s'", name );
	}
	entityHash.Add( entityHash.GenerateKey( name, true ), ent->entityNumber );
}

/*
================
idGameLocal::RemoveEntityFromHash
================
*/
bool idGameLocal::RemoveEntityFromHash( const char *name, idEntity *ent ) {
	int hash, i;

	hash = entityHash.GenerateKey( name, true );
	for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
		if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 ) {
			entityHash.Remove( hash, i );
			return true;
		}
	}
	return false;
}

/*
================
idGameLocal::GetTargets
================
*/
int idGameLocal::GetTargets( const idDict &args, idList< idEntityPtr<idEntity> > &list, const char *ref ) const {
	int i, num, refLength;
	const idKeyValue *arg;
	idEntity *ent;

	list.Clear();

	refLength = strlen( ref );
	num = args.GetNumKeyVals();
	for( i = 0; i < num; i++ ) {

		arg = args.GetKeyVal( i );
		if ( arg->GetKey().Icmpn( ref, refLength ) == 0 ) {

			ent = FindEntity( arg->GetValue() );
			if ( ent ) {
				idEntityPtr<idEntity> &entityPtr = list.Alloc();
                entityPtr = ent;
			}
		}
	}

	return list.Num();
}

/*
=============
idGameLocal::GetTraceEntity

returns the master entity of a trace.  for example, if the trace entity is the player's head, it will return the player.
=============
*/
idEntity *idGameLocal::GetTraceEntity( const trace_t &trace ) const {
	idEntity *master;

	if ( !entities[ trace.c.entityNum ] ) {
		return NULL;
	}
	master = entities[ trace.c.entityNum ]->GetBindMaster();
	if ( master ) {
		return master;
	}
	return entities[ trace.c.entityNum ];
}

/*
=============
idGameLocal::ArgCompletion_EntityName

Argument completion for entity names
=============
*/
void idGameLocal::ArgCompletion_EntityName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
	int i;

	for( i = 0; i < gameLocal.num_entities; i++ ) {
		if ( gameLocal.entities[ i ] ) {
			callback( va( "%s %s", args.Argv( 0 ), gameLocal.entities[ i ]->name.c_str() ) );
		}
	}
}

/*
=============
idGameLocal::ArgCompletion_AIName

Argument completion for idAI entity names
=============
*/
void idGameLocal::ArgCompletion_AIName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
	int i;

	for( i = 0; i < gameLocal.num_entities; i++ ) {
		if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idAI::GetClassType() ) ) {
			callback( va( "%s %s", args.Argv( 0 ), gameLocal.entities[ i ]->name.c_str() ) );
		}
	}
}

/*
=============
idGameLocal::FindEntity

Returns the entity whose name matches the specified string.
=============
*/
idEntity *idGameLocal::FindEntity( const char *name ) const {
	int hash, i;

	hash = entityHash.GenerateKey( name, true );
	for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
		if ( entities[i] && entities[i]->name.Icmp( name ) == 0 ) {
			return entities[i];
		}
	}

	return NULL;
}

/*
=============
idGameLocal::FindEntityUsingDef

Searches all active entities for the next one using the specified entityDef.

Searches beginning at the entity after from, or the beginning if NULL
NULL will be returned if the end of the list is reached.
=============
*/
idEntity *idGameLocal::FindEntityUsingDef( idEntity *from, const char *match ) const {
	idEntity	*ent;

	if ( !from ) {
		ent = spawnedEntities.Next();
	} else {
		ent = from->spawnNode.Next();
	}

	for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
		assert( ent );
		if ( idStr::Icmp( ent->GetEntityDefName(), match ) == 0 ) {
			return ent;
		}
	}

	return NULL;
}

/*
=============
idGameLocal::FindTraceEntity

Searches all active entities for the closest ( to start ) match that intersects
the line start,end
=============
*/
idEntity *idGameLocal::FindTraceEntity( idVec3 start, idVec3 end, const idTypeInfo &c, const idEntity *skip ) const {
	idEntity *ent;
	idEntity *bestEnt;
	float scale;
	float bestScale;
	idBounds b;

	bestEnt = NULL;
	bestScale = 1.0f;
	for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
		if ( ent->IsType( c ) && ent != skip ) {
			b = ent->GetPhysics()->GetAbsBounds().Expand( 16 );
			if ( b.RayIntersection( start, end-start, scale ) ) {
				if ( scale >= 0.0f && scale < bestScale ) {
					bestEnt = ent;
					bestScale = scale;
				}
			}
		}
	}

	return bestEnt;
}

/*
================
idGameLocal::EntitiesWithinRadius
================
*/
int idGameLocal::EntitiesWithinRadius( const idVec3 org, float radius, idEntity **entityList, int maxCount ) const {
	idEntity *ent;
	idBounds bo( org );
	int entCount = 0;

	bo.ExpandSelf( radius );
	for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
		if ( ent->GetPhysics()->GetAbsBounds().IntersectsBounds( bo ) ) {
			entityList[entCount++] = ent;
		}
	}

	return entCount;
}

/*
=================
idGameLocal::KillBox

Kills all entities that would touch the proposed new positioning of ent. The ent itself will not being killed.
Checks if player entities are in the teleporter, and marks them to die at teleport exit instead of immediately.
If catch_teleport, this only marks teleport players for death on exit
=================
*/
void idGameLocal::KillBox( idEntity *ent, bool catch_teleport ) {
	int			i;
	int			num;
	idEntity *	hit;
	idClipModel *cm;
	idClipModel *clipModels[ MAX_GENTITIES ];
	idPhysics	*phys;

	phys = ent->GetPhysics();
	if ( !phys->GetNumClipModels() ) {
		return;
	}

// RAVEN BEGIN
// ddynerman: multiple clip worlds
	num = ClipModelsTouchingBounds( ent, phys->GetAbsBounds(), phys->GetClipMask(), clipModels, MAX_GENTITIES );
// RAVEN END
	for ( i = 0; i < num; i++ ) {
		cm = clipModels[ i ];

		// don't check render entities
		if ( cm->IsRenderModel() ) {
			continue;
		}

		hit = cm->GetEntity();
		if ( ( hit == ent ) || !hit->fl.takedamage ) {
			continue;
		}

		if ( !phys->ClipContents( cm ) ) {
			continue;
		}

		// nail it
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( hit->IsType( idPlayer::GetClassType() ) && static_cast< idPlayer * >( hit )->IsInTeleport() ) {
// RAVEN END
			static_cast< idPlayer * >( hit )->TeleportDeath( ent->entityNumber );
		} else if ( !catch_teleport ) {
			hit->Damage( ent, ent, vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
		}

		if ( !gameLocal.isMultiplayer ) {
			// let the mapper know about it
			Warning( "'%s' telefragged '%s'", ent->name.c_str(), hit->name.c_str() );
		}
	}
}

/*
================
idGameLocal::RequirementMet
================
*/
bool idGameLocal::RequirementMet( idEntity *activator, const idStr &requires, int removeItem ) {
	if ( requires.Length() ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( activator->IsType( idPlayer::GetClassType() ) ) {
// RAVEN END
			idPlayer *player = static_cast<idPlayer *>(activator);
			idDict *item = player->FindInventoryItem( requires );
			if ( item ) {
				if ( removeItem ) {
					player->RemoveInventoryItem( item );
				}
				return true;
			} else {
				return false;
			}
		}
	}

	return true;
}


/*
================
idGameLocal::IsWeaponsStayOn
================
*/
bool idGameLocal::IsWeaponsStayOn( void ) {
	/// Override weapons stay when buying is active
	if( isMultiplayer && mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
		return false;
	}

	return serverInfo.GetBool( "si_weaponStay" );
}


/*
============
idGameLocal::AlertAI
============
*/
void idGameLocal::AlertAI( idEntity *ent ) {
// RAVEN BEGIN
// bdube: merged
	if ( ent ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( ent->IsType( idActor::GetClassType() ) ) {
// RAVEN END
			// alert them for the next frame
			lastAIAlertActorTime = time + GetMSec();
			lastAIAlertActor = static_cast<idActor *>( ent );
		} else {
			lastAIAlertEntityTime = time + GetMSec();
			lastAIAlertEntity = ent;
		}
	} else {
		lastAIAlertEntityTime = 0;
		lastAIAlertActorTime = 0;
		lastAIAlertEntity = NULL;
		lastAIAlertActor = NULL;
	}
// RAVEN END
}

// RAVEN BEGIN
// bdube: alert entity returns an entity, alert actor a an actor
/*
============
idGameLocal::GetAlertEntity
============
*/
idEntity *idGameLocal::GetAlertEntity( void ) {
	if ( lastAIAlertEntityTime >= time ) {
		return lastAIAlertEntity.GetEntity();
	}

	return NULL;
}

/*
============
idGameLocal::GetAlertActor
============
*/
idActor *idGameLocal::GetAlertActor( void ) {
	if ( lastAIAlertActorTime >= time ) {
		return lastAIAlertActor.GetEntity();
	}

	return NULL;
}
// RAVEN END

/*
============
SortClipModelsByEntity
============
*/
static int SortClipModelsByEntity( const void* left, const void* right ) {
	idEntity* leftEntity  = left  ? (*((const idClipModel**)left))->GetEntity() : NULL;
	idEntity* rightEntity = right ? (*((const idClipModel**)right))->GetEntity() : NULL;

	int entityNumLeft = (leftEntity) ? leftEntity->entityNumber : 0;
	int entityNumRight = (rightEntity) ? rightEntity->entityNumber : 0;

	return entityNumLeft - entityNumRight;
}

// RAVEN BEGIN
/*
============
idGameLocal::RadiusDamage
Returns the number of actors damaged
============
*/
// abahr: changed to work with deathPush
void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignoreDamage, idEntity *ignorePush, const char *damageDefName, float dmgPower, int* hitCount ) {
	float		dist, damageScale, attackerDamageScale, attackerPushScale;
	idEntity*	ent = NULL;
	idEntity*	lastEnt = NULL;
	idClipModel* clipModel = NULL;
	idClipModel* clipModelList[ MAX_GENTITIES ];
	int			numListedClipModels;
	modelTrace_t	result;
	idVec3 		v, damagePoint, dir;
	int			i, damage, radius, push;

	const idDict *damageDef = FindEntityDefDict( damageDefName, false );
	if ( !damageDef ) {
		Warning( "Unknown damageDef '%s'", damageDefName );
		return;
	}

	damageDef->GetInt( "damage", "20", damage );
	damageDef->GetInt( "radius", "50", radius );
	damageDef->GetInt( "push", va( "%d", damage * 100 ), push );
	damageDef->GetFloat( "attackerDamageScale", "0.5", attackerDamageScale );
	if( gameLocal.isMultiplayer ) {
		damageDef->GetFloat( "attackerPushScale", "2", attackerPushScale );
	} else {
		damageDef->GetFloat( "attackerPushScale", "0", attackerPushScale );
	}

	if ( radius < 1 ) {
		radius = 1;
	}


// ddynerman: multiple clip worlds
	numListedClipModels = ClipModelsTouchingBounds( inflictor, idBounds(origin).Expand(radius), MASK_ALL, clipModelList, MAX_GENTITIES );
	if( numListedClipModels > 0 ) {
		//Sort list by unique entities for easier searching
		qsort( clipModelList, numListedClipModels, sizeof(clipModelList[0]), SortClipModelsByEntity );
	}

	if ( inflictor ) {
		inflictor = inflictor->GetDamageEntity ( );
	}
	if ( attacker ) {
		attacker = attacker->GetDamageEntity ( );
	}
	if ( ignoreDamage ) {
		ignoreDamage = ignoreDamage->GetDamageEntity ( );
	}

	for( int c = 0; c < numListedClipModels; ++c ) {
		clipModel = clipModelList[ c ];
		assert( clipModel );

		ent = clipModel->GetEntity();
		
		// Skip all entitys that arent primary damage entities
		if ( !ent || ent != ent->GetDamageEntity ( ) ) {
			continue;
		}

		// Dont damage inflictor or the ignore entity
		if( ent == inflictor || ent == ignoreDamage ) {
			continue;
		}

		idBounds absBounds = clipModel->GetAbsBounds();

		// find the distance from the edge of the bounding box
		for ( i = 0; i < 3; i++ ) {
			if ( origin[ i ] < absBounds[0][ i ] ) {
				v[ i ] = absBounds[0][ i ] - origin[ i ];
			} else if ( origin[ i ] > absBounds[1][ i ] ) {
				v[ i ] = origin[ i ] - absBounds[1][ i ];
			} else {
				v[ i ] = 0;
			}
		}

		dist = v.Length();
		if ( dist >= radius ) {
			continue;
		}

		if( gameRenderWorld->FastWorldTrace(result, origin, absBounds.GetCenter()) ) {
			continue;
		}
		
		RadiusPushClipModel ( inflictor, origin, push, clipModel );

		// Only damage unique entities.  This works because we have a sorted list
		if( lastEnt == ent ) {
			continue;
		}

		lastEnt = ent;

		if ( ent->CanDamage( origin, damagePoint, ignoreDamage ) ) {						
			// push the center of mass higher than the origin so players
			// get knocked into the air more
			if( gameLocal.isMultiplayer ) {
				// fudge the direction in MP to account for player height difference and origin shift
				// 31.875 = origin is 23.875 units lower in Q4 than Q3 + player is 8 units taller in Q4
				dir = ( ent->GetPhysics()->GetOrigin() + idVec3( 0.0f, 0.0f, 31.875f ) ) - origin;
			} else {
				dir = ent->GetPhysics()->GetOrigin() - origin;
			}		
			
			dir[2] += 24;
 
 			// get the damage scale
			damageScale = dmgPower * ( 1.0f - dist / radius );
			
			if ( ent == attacker ) {
				damageScale *= attackerDamageScale;
			}

			dir.Normalize();
			ent->Damage( inflictor, attacker, dir, damageDefName, damageScale, CLIPMODEL_ID_TO_JOINT_HANDLE(ent->GetPhysics()->GetClipModel()->GetId()) );

			// for stats, count the first 
			if( attacker && attacker->IsType( idPlayer::GetClassType() ) && inflictor && inflictor->IsType( idProjectile::GetClassType() ) && ent->IsType( idPlayer::GetClassType() ) && hitCount ) {
				// with splash damage projectiles, one projectile fire can damage multiple people.  If anyone is hit, 
				// the shot counts as a hit but only report accuracy on the first one to avoid accuracies > 100%
				statManager->WeaponHit( (const idActor*)attacker, ent, ((idProjectile*)inflictor)->methodOfDeath, (*hitCount) == 0 );
				(*hitCount)++;
			}
		} 
	}
}
// RAVEN END

/*
==============
idGameLocal::RadiusPush
==============
*/
void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake ) {
	int i, numListedClipModels;
	idClipModel *clipModel;
	idClipModel *clipModelList[ MAX_GENTITIES ];
	idVec3 dir;
	idBounds bounds;
	modelTrace_t result;
	idEntity *ent;
	float scale;

// RAVEN BEGIN
// abahr: need to use gravity instead of assuming z is up
	dir = -GetCurrentGravity( const_cast<idEntity*>(inflictor) ).ToNormal();
// RAVEN END

	bounds = idBounds( origin ).Expand( radius );

	// get all clip models touching the bounds
// RAVEN BEGIN
// ddynerman: multiple clip worlds
	numListedClipModels = ClipModelsTouchingBounds( inflictor, bounds, -1, clipModelList, MAX_GENTITIES );

// bdube: getdamageentity
	if ( inflictor ) {
		inflictor = ((idEntity*)inflictor)->GetDamageEntity ( );
	}
	if ( ignore ) {
		ignore = ((idEntity*)ignore)->GetDamageEntity ( );
	}
// RAVEN END

	// apply impact to all the clip models through their associated physics objects
	for ( i = 0; i < numListedClipModels; i++ ) {

		clipModel = clipModelList[i];

		// never push render models
		if ( clipModel->IsRenderModel() ) {
			continue;
		}

		ent = clipModel->GetEntity();
// RAVEN BEGIN
// bdube: damage entity		
		if ( !ent || ent != ent->GetDamageEntity ( ) ) {
			continue;
		}
// RAVEN END
		
		// never push projectiles
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( ent->IsType( idProjectile::GetClassType() ) ) {
// RAVEN END
			continue;
		}

		// players use "knockback" in idPlayer::Damage
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( ent->IsType( idPlayer::GetClassType() ) && !quake ) {
// RAVEN END
			continue;
		}

		// don't push the ignore entity
		if ( ent == ignore ) {
			continue;
		}

		if ( gameRenderWorld->FastWorldTrace( result, origin, clipModel->GetOrigin() ) ) {
			continue;
		}

		// scale the push for the inflictor
		if ( ent == inflictor ) {
			scale = inflictorScale;
		} else {
			scale = 1.0f;
		}

		if ( quake ) {
			clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), clipModel->GetOrigin(), scale * push * dir );
		} else {
// RAVEN BEGIN
// bdube: inflictor
			RadiusPushClipModel( (idEntity*)inflictor, origin, scale * push, clipModel );
// RAVEN END
		}
	}
}

/*
==============
idGameLocal::RadiusPushClipModel
==============
*/
// RAVEN BEGIN
// bdube: inflictor
void idGameLocal::RadiusPushClipModel( idEntity* inflictor, const idVec3 &origin, const float push, const idClipModel *clipModel ) {
// RAVEN END
	int i, j;
	float dot, dist, area;
	const idTraceModel *trm;
	const traceModelPoly_t *poly;
	idFixedWinding w;
	idVec3 v, localOrigin, center, impulse;

	trm = clipModel->GetTraceModel();
	if ( !trm || 1 ) {
		impulse = clipModel->GetAbsBounds().GetCenter() - origin;
		impulse.Normalize();
// RAVEN BEGIN
// abahr: removed because z isn't always up
		//impulse.z += 1.0f;
		//impulse = idVec3( 0.0, 0.0, 1.0 );
		clipModel->GetEntity()->ApplyImpulse( inflictor, clipModel->GetId(), clipModel->GetOrigin(), push * impulse, true );
// RAVEN END
		return;
	}

	localOrigin = ( origin - clipModel->GetOrigin() ) * clipModel->GetAxis().Transpose();
	for ( i = 0; i < trm->numPolys; i++ ) {
		poly = &trm->polys[i];

		center.Zero();
		for ( j = 0; j < poly->numEdges; j++ ) {
			v = trm->verts[ trm->edges[ abs(poly->edges[j]) ].v[ INTSIGNBITSET( poly->edges[j] ) ] ];
			center += v;
			v -= localOrigin;
			v.NormalizeFast();	// project point on a unit sphere
			w.AddPoint( v );
		}
		center /= poly->numEdges;
		v = center - localOrigin;
		dist = v.NormalizeFast();
		dot = v * poly->normal;
		if ( dot > 0.0f ) {
			continue;
		}
		area = w.GetArea();
		// impulse in polygon normal direction
		impulse = poly->normal * clipModel->GetAxis();
		// always push up for nicer effect
		impulse.z -= 1.0f;
		// scale impulse based on visible surface area and polygon angle
		impulse *= push * ( dot * area * ( 1.0f / ( 4.0f * idMath::PI ) ) );
		// scale away distance for nicer effect
		impulse *= ( dist * 2.0f );
		// impulse is applied to the center of the polygon
		center = clipModel->GetOrigin() + center * clipModel->GetAxis();

// RAVEN BEGIN
// bdube: inflictor
		clipModel->GetEntity()->ApplyImpulse( inflictor, clipModel->GetId(), center, impulse );
// RAVEN END
	}
}

/*
===============
idGameLocal::ProjectDecal
===============
*/
void idGameLocal::ProjectDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float size, const char *material, float angle ) {
	float s, c;
	idMat3 axis, axistemp;
	idFixedWinding winding;
	idVec3 windingOrigin, projectionOrigin;

	static idVec3 decalWinding[4] = {
		idVec3(  1.0f,  1.0f, 0.0f ),
		idVec3( -1.0f,  1.0f, 0.0f ),
		idVec3( -1.0f, -1.0f, 0.0f ),
		idVec3(  1.0f, -1.0f, 0.0f )
	};

	if ( !g_decals.GetBool() ) {
		return;
	}

// RAVEN BEGIN
// rjohnson: no decals on dedicated server
	if ( isMultiplayer && !isClient && !isListenServer ) {
		// no decals on dedicated server
		return;
	}
// RAVEN END

	// randomly rotate the decal winding
	idMath::SinCos16( ( angle ) ? angle : random.RandomFloat() * idMath::TWO_PI, s, c );

	// winding orientation
	axis[2] = dir;
	axis[2].Normalize();
	axis[2].NormalVectors( axistemp[0], axistemp[1] );
	axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
	axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;

	windingOrigin = origin + depth * axis[2];
	if ( parallel ) {
		projectionOrigin = origin - depth * axis[2];
	} else {
		projectionOrigin = origin;
	}

	size *= 0.5f;

	winding.Clear();
	winding += idVec5( windingOrigin + ( axis * decalWinding[0] ) * size, idVec2( 1, 1 ) );
	winding += idVec5( windingOrigin + ( axis * decalWinding[1] ) * size, idVec2( 0, 1 ) );
	winding += idVec5( windingOrigin + ( axis * decalWinding[2] ) * size, idVec2( 0, 0 ) );
	winding += idVec5( windingOrigin + ( axis * decalWinding[3] ) * size, idVec2( 1, 0 ) );
	gameRenderWorld->ProjectDecalOntoWorld( winding, projectionOrigin, parallel, depth * 0.5f, declManager->FindMaterial( material ), time );
}

/*
==============
idGameLocal::BloodSplat
==============
*/
// RAVEN BEGIN
// ddynerman: multiple collision models
void idGameLocal::BloodSplat( const idEntity* ent, const idVec3 &origin, const idVec3 &dirArg, float size, const char *material ) {

	float halfSize = size * 0.5f;
	idVec3 verts[] = {	idVec3( 0.0f, +halfSize, +halfSize ),
						idVec3( 0.0f, +halfSize, -halfSize ),
						idVec3( 0.0f, -halfSize, -halfSize ),
						idVec3( 0.0f, -halfSize, +halfSize ) };
	idVec3 dir = dirArg;
	idTraceModel trm;
	idClipModel mdl;
	trace_t results;

// RAVEN BEGIN
// mekberg: changed from g_bloodEffects to g_decals
	if ( !g_decals.GetBool() ) {
		return;
	}
// RAVEN END

	size = halfSize + random.RandomFloat() * halfSize;
	trm.SetupPolygon( verts, 4 );
	mdl.LoadModel( trm, NULL );

	// I don't want dir to be axis aligned, as it is more likely to streak them (because most architecture is axis aligned
	dir.Set( dirArg.x*.1f, dirArg.y*.1f, -1 );
	dir.Normalize();

// RAVEN BEGIN
// ddynerman: multiple clip worlds
	Translation( ent, results, origin, origin + dir * 72.0f, &mdl, mat3_identity, CONTENTS_SOLID, NULL );
// RAVEN END
	ProjectDecal( results.endpos, dir, 2.0f * size, true, size, material );
}

/*
=============
idGameLocal::SetCamera
=============
*/
void idGameLocal::SetCamera( idCamera *cam ) {
	int i;
	idEntity *ent;
	idAI *ai;

	// this should fix going into a cinematic when dead.. rare but happens
	idPlayer *client = GetLocalPlayer();
	if ( client->health <= 0 || client->pfl.dead ) {
		return;
	}

	camera = cam;
	if ( camera ) {
// RAVEN BEGIN
// bdube: tool support
		inCinematic = false;
		if( !( gameLocal.editors & ( EDITOR_MODVIEW | EDITOR_PLAYBACKS ) ) ) {
			inCinematic = true;
		}
// RAVEN END

		if ( skipCinematic && camera->spawnArgs.GetBool( "disconnect" ) ) {
			camera->spawnArgs.SetBool( "disconnect", false );
			cvarSystem->SetCVarFloat( "r_znear", 3.0f );
			cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
			skipCinematic = false;
			return;
		}

		if ( time > cinematicStopTime ) {
			cinematicSkipTime = time + CINEMATIC_SKIP_DELAY;
		}

		// set r_znear so that transitioning into/out of the player's head doesn't clip through the view
		cvarSystem->SetCVarFloat( "r_znear", 1.0f );
		
		// hide all the player models
		for( i = 0; i < numClients; i++ ) {
			if ( entities[ i ] ) {
				client = static_cast< idPlayer* >( entities[ i ] );
				client->EnterCinematic();
			}
		}

		if ( !cam->spawnArgs.GetBool( "ignore_enemies" ) ) {
			// kill any active monsters that are enemies of the player
			for ( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
				if ( ent->cinematic || ent->fl.isDormant ) {
					// only kill entities that aren't needed for cinematics and aren't dormant
					continue;
				}
				
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
				if ( ent->IsType( idAI::GetClassType() ) ) {
// RAVEN END
					ai = static_cast<idAI *>( ent );
					if ( !ai->GetEnemy() || !ai->IsActive() ) {
						// no enemy, or inactive, so probably safe to ignore
						continue;
					}
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
				} else if ( ent->IsType( idProjectile::GetClassType() ) ) {
// RAVEN END
					// remove all projectiles
				} else if ( ent->spawnArgs.GetBool( "cinematic_remove" ) ) {
					// remove anything marked to be removed during cinematics
				} else {
					// ignore everything else
					continue;
				}

				// remove it
				DPrintf( "removing '%s' for cinematic\n", ent->GetName() );
				ent->PostEventMS( &EV_Remove, 0 );
			}
		}

		if ( inCinematic && g_autoSkipCinematics.GetBool() ) {
			SkipCinematic();
		}

	} else {
		inCinematic = false;
		cinematicStopTime = time + msec;

		// restore r_znear
		cvarSystem->SetCVarFloat( "r_znear", 3.0f );

		// show all the player models
		for( i = 0; i < numClients; i++ ) {
			if ( entities[ i ] ) {
				idPlayer *client = static_cast< idPlayer* >( entities[ i ] );
				client->ExitCinematic();
			}
		}
	}
}

// RAVEN BEGIN
// jscott: for portal skies
/*
=============
idGameLocal::GetPortalSky
=============
*/
idCamera *idGameLocal::GetPortalSky( void ) const
{
	if( !portalSkyVisible ) {

		return( NULL );
	}
	return( portalSky );
}
/*
=============
idGameLocal::SetPortalSky
=============
*/
void idGameLocal::SetPortalSky( idCamera *cam ) 
{
	portalSky = cam;
}
// RAVEN END

/*
=============
idGameLocal::GetCamera
=============
*/
idCamera *idGameLocal::GetCamera( void ) const {
	return camera;
}

/*
=============
idGameLocal::SkipCinematic
=============
*/
bool idGameLocal::SkipCinematic( void ) {
	if ( camera ) {
		if ( camera->spawnArgs.GetBool( "disconnect" ) ) {
			camera->spawnArgs.SetBool( "disconnect", false );
			cvarSystem->SetCVarFloat( "r_znear", 3.0f );
			cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
			skipCinematic = false;
			return false;
		}

		if ( camera->spawnArgs.GetBool( "instantSkip" ) ) {
			camera->Stop();
			return false;
		}
	}

	soundSystem->SetMute( true );
	if ( !skipCinematic ) {
		skipCinematic = true;
		cinematicMaxSkipTime = gameLocal.time + SEC2MS( g_cinematicMaxSkipTime.GetFloat() );
	}

	return true;
}

// RAVEN BEGIN
/*
======================
idGameLocal::StartViewEffect

For passing in an effect triggered view effect
======================
*/
void idGameLocal::StartViewEffect( int type, float time, float scale )
{
	idPlayer		*player;
	idPlayerView	*view;

	player = GetLocalPlayer();
	if( player )
	{
		view = &player->playerView;

		switch( type )
		{
		case VIEWEFFECT_DOUBLEVISION:
			view->SetDoubleVisionParms( time, scale );
			break;

		case VIEWEFFECT_SHAKE:
			if( !gameLocal.isMultiplayer ) {
				view->SetShakeParms( time, scale );
			}
			break;

		case VIEWEFFECT_TUNNEL:
			view->SetTunnelParms( time, scale );
			break;

		default:
			gameLocal.Warning( "Invalid view effect" );
			break;
		}
	}
}

/*
======================
idGameLocal::GetPlayerView
======================
*/
void idGameLocal::GetPlayerView( idVec3 &origin, idMat3 &axis )
{
	idPlayer		*player;
	renderView_t	*view;

	player = GetLocalPlayer();
	if( player )
	{
		view = player->GetRenderView();
		if ( !view ) {
			player->CalculateRenderView();
			view = player->GetRenderView();
			assert( view );
		}
		origin = view->vieworg;
		axis = view->viewaxis;
	}
	else
	{
		origin = vec3_origin;
		axis = mat3_identity;
	}
}

/*
======================
idGameLocal::Translation

small portion of physics required for the effects system
======================
*/
void idGameLocal::Translation( trace_t &trace, idVec3 &source, idVec3 &dest, idTraceModel *trm, int clipMask ) {
	if( !trm ) {
		clip[0]->Translation( trace, source, dest, NULL, mat3_identity, clipMask, NULL, NULL );
	} else {
		idClipModel		cm;
	
		cm.LoadModel( *trm, NULL );
		clip[0]->Translation( trace, source, dest, &cm, mat3_identity, clipMask, NULL, NULL );
	}
}

/*
======================
idGameLocal::SpawnClientMoveable
======================
*/
void idGameLocal::SpawnClientMoveable( const char* name, int lifetime, const idVec3& origin, const idMat3& axis, const idVec3& velocity, const idVec3& angular_velocity ) {
	const bool debugDebris = cvarSystem && cvarSystem->GetCVarInteger( "bse_debug" ) > 0;

	// find the debris def
	const idDict* args = gameLocal.FindEntityDefDict( name, false );
	if ( !args ) {
		if ( debugDebris ) {
			common->Warning( "BSE debris: entityDef '%s' not found", name ? name : "" );
		}
		return;
	}

	// Ensure client moveables never last forever
	if ( lifetime <= 0 ) {
		lifetime = SEC2MS(args->GetFloat( "duration", "5" ));
	}
	int burn_time = idMath::ClampInt( 0, lifetime, SEC2MS(args->GetFloat( "burn_time", "2.5" )) );
	
	// Spawn the debris
	
	rvClientMoveable* cent = NULL;
	// force the args to spawn a rvClientMoveable
	SpawnClientEntityDef( *args, (rvClientEntity**)(&cent), false, "rvClientMoveable" );
	
	if( !cent ) {
		if ( debugDebris ) {
			common->Warning( "BSE debris: failed to spawn client moveable '%s'", name ? name : "" );
		}
		return;
	}

	if ( debugDebris ) {
		common->Printf( "BSE debris: spawned client moveable '%s' lifetime=%d\n", name ? name : "", lifetime );
	}
 
	cent->SetOrigin( origin );
	cent->SetAxis( axis );
		
	cent->GetPhysics()->SetLinearVelocity( velocity );
	cent->GetPhysics()->SetAngularVelocity( angular_velocity );

	if ( !burn_time ) {
		//just disappear
		cent->PostEventMS( &EV_Remove, lifetime );	
	} else {
		cent->PostEventMS( &CL_FadeOut, lifetime-burn_time, burn_time );
	}
}

/*
======================
idGameLocal::DebugSet
======================
*/
void idGameLocal::DebugSetString( const char* name, const char* value ) {
	gameDebug.SetString( name, value );
}
void idGameLocal::DebugSetFloat( const char* name, float value ) {
	gameDebug.SetFloat( name, value );
}
void idGameLocal::DebugSetInt( const char* name, int value ) {
	gameDebug.SetInt( name, value );
}

/*
======================
idGameLocal::DebugGetStat
======================
*/
const char* idGameLocal::DebugGetStatString ( const char* name ) {
	return gameDebug.GetStatString( name );
}

int idGameLocal::DebugGetStatInt ( const char* name ) {
	return gameDebug.GetStatInt( name );
}

float idGameLocal::DebugGetStatFloat ( const char* name ) {
	return gameDebug.GetStatFloat( name );
}

/*
======================
idGameLocal::IsDebugHudActive
======================
*/
bool idGameLocal::IsDebugHudActive ( void ) const {
	return gameDebug.IsHudActive( DBGHUD_ANY );
}


// rjohnson: added player info support for note taking system
/*
======================
idGameLocal::GetPlayerInfo
======================
*/
bool idGameLocal::GetPlayerInfo( idVec3 &origin, idMat3 &axis, int PlayerNum, idAngles *deltaViewAngles, int reqClientNum ) {
	idPlayer	*player;

	if ( PlayerNum == -1 ) {
		player = GetLocalPlayer();
	} else {
		player = GetClientByNum( PlayerNum );
	}

	if( reqClientNum != -1 ) {
		idPlayer* reqClient = GetClientByNum( reqClientNum );
		if( reqClient && player ) {
			if( reqClient->GetInstance() != player->GetInstance() ) {
				return false;
			}
		}
	}

	if ( !player ) {
		return false;
	}

	player->GetViewPos( origin, axis );
	origin = player->GetPhysics()->GetOrigin();

	if ( deltaViewAngles ) {
		*deltaViewAngles = player->GetDeltaViewAngles();
	}

	return true;
};

/*
======================
idGameLocal::SetCurrentPlayerInfo
======================
*/
void idGameLocal::SetPlayerInfo( idVec3 &origin, idMat3 &axis, int PlayerNum ) {
	idPlayer	*player;

	if ( PlayerNum == -1 ) {
		player = GetLocalPlayer();
	} else {
		player = GetClientByNum( PlayerNum );
	}

	if ( !player ) {
		return;
	}

	player->Teleport( origin, axis.ToAngles(), NULL );
// RAVEN BEGIN
// ddynerman: save the current thinking entity for instance-dependent
	currentThinkingEntity = player;
	player->CalculateFirstPersonView();
	player->CalculateRenderView();
	currentThinkingEntity = NULL;
// RAVEN END

	return;
};

bool idGameLocal::PlayerChatDisabled( int clientNum ) {
	if( clientNum < 0 || clientNum >= MAX_CLIENTS || !entities[ clientNum ] ) {
		return false;
	}

	return !( ((idPlayer*)entities[ clientNum ])->isChatting || ((idPlayer*)entities[ clientNum ])->pfl.dead );
}

void idGameLocal::SetViewComments( const char *text ) {
	idPlayer	*player;

	player = GetLocalPlayer();

	if ( !player ) {
		return;
	}

	if ( text ) {
		player->hud->SetStateString( "viewcomments", text );
		player->hud->HandleNamedEvent( "showViewComments" );
	}
	else {
		player->hud->SetStateString( "viewcomments", "" );
		player->hud->HandleNamedEvent( "hideViewComments" );
	}
}

/*
===================
idGameLocal::GetNumGravityAreas
===================
*/
int	idGameLocal::GetNumGravityAreas() const {
	return gravityInfo.Num();
}

/*
===================
idGameLocal::GetGravityInfo
===================
*/
const rvGravityArea* idGameLocal::GetGravityInfo( int index ) const {
	return gravityInfo[ index ];
}

/*
===================
idGameLocal::SetGravityArea
===================
*/
void idGameLocal::SetGravityInfo( int index, rvGravityArea* info ) {
	gravityInfo[ index ] = info;
}

/*
===================
idGameLocal::AddUniqueGravityInfo
===================
*/
void idGameLocal::AddUniqueGravityInfo( rvGravityArea* info ) {
	gravityInfo.AddUnique( info );
}

/*
===================
idGameLocal::GetCurrentGravityInfoIndex
===================
*/
int idGameLocal::GetCurrentGravityInfoIndex( const idVec3& origin ) const {
	int numGravityAreas = GetNumGravityAreas();
	if( !numGravityAreas ) {
		return -1;
	}

	int areaNum = gameRenderWorld->PointInArea( origin );

	for( int ix = 0; ix < numGravityAreas; ++ix ) {
		if( !gameRenderWorld->AreasAreConnected(GetGravityInfo(ix)->GetArea(), areaNum, PS_BLOCK_GRAVITY) ) {
			continue;
		}
		 
		return ix;
	}

	return -1;
}

/*
===================
idGameLocal::InGravityArea
===================
*/
bool idGameLocal::InGravityArea( idEntity* entity ) const {
	return GetCurrentGravityInfoIndex( entity ) >= 0;
}

/*
===================
idGameLocal::GetCurrentGravityInfoIndex
===================
*/
int idGameLocal::GetCurrentGravityInfoIndex( idEntity* entity ) const {
	return GetCurrentGravityInfoIndex( entity->GetPhysics()->GetOrigin() );
}

/*
===================
idGameLocal::GetCurrentGravity
===================
*/
const idVec3 idGameLocal::GetCurrentGravity( idEntity* entity ) const {
	int index = GetCurrentGravityInfoIndex( entity );
	return (index >= 0) ? gravityInfo[ index ]->GetGravity(entity) : GetGravity();
}

/*
===================
idGameLocal::GetCurrentGravity
===================
*/
const idVec3 idGameLocal::GetCurrentGravity( const idVec3& origin, const idMat3& axis ) const {
	int index = GetCurrentGravityInfoIndex( origin );
	return (index >= 0) ? gravityInfo[ index ]->GetGravity(origin, axis, MASK_SOLID, NULL) : GetGravity();
}

/*
===================
idGameLocal::InGravityArea
===================
*/
bool idGameLocal::InGravityArea( rvClientEntity* entity ) const {
	return GetCurrentGravityInfoIndex( entity ) >= 0;
}

/*
===================
idGameLocal::GetCurrentGravityInfoIndex
===================
*/
int idGameLocal::GetCurrentGravityInfoIndex( rvClientEntity* entity ) const {
	return GetCurrentGravityInfoIndex( entity->GetPhysics()->GetOrigin() );
}

/*
===================
idGameLocal::GetCurrentGravity
===================
*/
const idVec3 idGameLocal::GetCurrentGravity( rvClientEntity* entity ) const {
	int index = GetCurrentGravityInfoIndex( entity );
	return (index >= 0) ? gravityInfo[ index ]->GetGravity(entity) : GetGravity();
}

/*
===================
idGameLocal::ReferenceScriptObjectProxy
===================
*/
idEntity* idGameLocal::ReferenceScriptObjectProxy( const char* scriptObjectName ) {
	idEntity*		proxy = NULL;
	idEntityPtr<idEntity> safeProxy;
	idDict			args;
	idScriptObject* object = NULL;

	for( int ix = 0; ix < scriptObjectProxies.Num(); ++ix ) {
		proxy = scriptObjectProxies[ ix ].GetEntity();
		assert( proxy );
		
		object = &proxy->scriptObject;
		if( !object->data ) {
			object->SetType( scriptObjectName );
			proxy->ConstructScriptObject();
			return proxy;
		}
	}

	args.Set( "classname", "func_static" );
	args.Set( "scriptobject", scriptObjectName );
	args.SetBool( "noclipmodel", true );
	bool spawned = SpawnEntityDef(args, &proxy);
	if ( !spawned ) {
		assert( 0 );
	}
	safeProxy = proxy;
	scriptObjectProxies.AddUnique( safeProxy );
	return proxy;
}

/*
===================
idGameLocal::ReleaseScriptObjectProxy
===================
*/
void idGameLocal::ReleaseScriptObjectProxy( const char* proxyName ) {
	idScriptObject* object = NULL;
	idEntity*		entity = NULL;

	for( int ix = 0; ix < scriptObjectProxies.Num(); ++ix ) {
		entity = scriptObjectProxies[ ix ].GetEntity();
		if( entity && !idStr::Icmp(entity->GetName(), proxyName) ) {
			object = &entity->scriptObject;
			if( !object ) {
				continue;
			}
			
			entity->DeconstructScriptObject();
			object->Free();
		}
	}
}

// RAVEN BEGIN
// rjohnson: entity usage stats
void idGameLocal::ListEntityStats( const idCmdArgs &args ) {
	int				i, j;
	idStr			currentMap;
	idList<idStr>	uniqueMapNames;


	for( i = 1; i < args.Argc(); i++ ) {
		if ( idStr::Icmp( args.Argv( i ), "clear" ) == 0 ) {
			entityUsageList.Clear();
			common->Printf("Entity stats cleared.\n");
			return;
		}
	}

	for( i = 0; i < entityUsageList.Num(); i++ ) {
		entityUsageList[ i ].SetInt( "reported_stat", false );
	}

	for( i = 0; i < entityUsageList.Num(); i++ ) {
		idStr	mapFileName, className;
		int		count;

		if ( entityUsageList[ i ].GetInt( "reported_stat" ) ) {
			continue;
		}

		entityUsageList[ i ].GetString( "mapFileName", "none", mapFileName );
		if ( currentMap != mapFileName )
		{
			if ( i ) {
				common->Printf( "\n" );
			}
			common->Printf( "================ %s ================\n", mapFileName.c_str() );
			currentMap = mapFileName;
			uniqueMapNames.Insert( mapFileName );
		}

		entityUsageList[ i ].GetString( "classname", "none", className );
		count = 0;

		for( j = i; j < entityUsageList.Num(); j++ ) {
			idStr	checkMapFileName, checkClassName;

			entityUsageList[ j ].GetString( "mapFileName", "none", checkMapFileName );
			if ( checkMapFileName != mapFileName ) {
				break;
			}

			entityUsageList[ j ].GetString( "classname", "none", checkClassName );

			if ( checkClassName == className ) {
				entityUsageList[ j ].SetInt( "reported_stat", 1 );
				count++;
			}
		}

		common->Printf("%d\t%s\n", count, className.c_str() );
	}

	common->Printf( "\n" );
	common->Printf( "\n" );
	common->Printf( "================ OVERALL ================\n" );

	for( i = 0; i < entityUsageList.Num(); i++ ) {
		idStr	mapFileName, className;
		int		count;

		if ( entityUsageList[ i ].GetInt( "reported_stat" ) == 2 ) {
			continue;
		}

		entityUsageList[ i ].GetString( "classname", "none", className );
		count = 0;

		for( j = i; j < entityUsageList.Num(); j++ ) {
			idStr	checkClassName;

			entityUsageList[ j ].GetString( "classname", "none", checkClassName );

			if ( checkClassName == className ) {
				entityUsageList[ j ].SetInt( "reported_stat", 2 );
				count++;
			}
		}

		common->Printf("%d\t%s\n", count, className.c_str() );
	}

	idFile *FH = fileSystem->OpenFileWrite( "EntityStats.csv" );
	if ( FH ) {
		int	size = sizeof( int ) * uniqueMapNames.Num();
		int	*count = ( int * )_alloca( size );

		FH->Printf("\"Definition\"");
		for( i = 0; i < uniqueMapNames.Num(); i++ ) {
			FH->Printf( ",\"%s\"", uniqueMapNames[ i ].c_str() );
		}
		FH->Printf(",Total\n");

		for( i = 0; i < entityUsageList.Num(); i++ ) {
			idStr	className;
			int		total;

			if ( entityUsageList[ i ].GetInt( "reported_stat" ) == 3 ) {
				continue;
			}

			entityUsageList[ i ].GetString( "classname", "none", className );

			memset( count, 0, size );
			for( j = i; j < entityUsageList.Num(); j++ )
			{
				idStr	checkMapFileName, checkClassName;

				entityUsageList[ j ].GetString( "classname", "none", checkClassName );

				if ( checkClassName == className ) {
					entityUsageList[ j ].SetInt( "reported_stat", 3 );
					entityUsageList[ j ].GetString( "mapFileName", "none", checkMapFileName );

					int loc = uniqueMapNames.FindIndex( checkMapFileName );
					if ( loc >= 0 ) {
						count[ loc ]++;
					}
				}
			}

			total = 0;
			FH->Printf( "\"%s\"", className.c_str() );
			for( j = 0; j < uniqueMapNames.Num(); j++ ) {
				FH->Printf( ",%d", count[ j ] );
				total += count[ j ];
			}
			FH->Printf( ",%d\n", total );
		}

		fileSystem->CloseFile( FH );
	}
}
// RAVEN END

/*
======================
idGameLocal::SpreadLocations

Now that everything has been spawned, associate areas with location entities
======================
*/
void idGameLocal::SpreadLocations() {
	idEntity *ent;

// RAVEN BEGIN
	if( !gameRenderWorld ) {
		common->Error( "GameRenderWorld is NULL!" );
	}
// RAVEN END

	// allocate the area table
	int	numAreas = gameRenderWorld->NumAreas();
	locationEntities = new idLocationEntity *[ numAreas ];
	memset( locationEntities, 0, numAreas * sizeof( *locationEntities ) );

	// for each location entity, make pointers from every area it touches
	for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if ( !ent->IsType( idLocationEntity::GetClassType() ) ) {
// RAVEN END
			continue;
		}
		idVec3	point = ent->spawnArgs.GetVector( "origin" );
		int areaNum = gameRenderWorld->PointInArea( point );
		if ( areaNum < 0 ) {
			Printf( "SpreadLocations: location '%s' is not in a valid area\n", ent->spawnArgs.GetString( "name" ) );
			continue;
		}
		if ( areaNum >= numAreas ) {
			Error( "idGameLocal::SpreadLocations: areaNum >= gameRenderWorld->NumAreas()" );
		}
		if ( locationEntities[areaNum] ) {
			Warning( "location entity '%s' overlaps '%s'", ent->spawnArgs.GetString( "name" ),
				locationEntities[areaNum]->spawnArgs.GetString( "name" ) );
			continue;
		}
		locationEntities[areaNum] = static_cast<idLocationEntity *>(ent);

		// spread to all other connected areas
		for ( int i = 0 ; i < numAreas ; i++ ) {
			if ( i == areaNum ) {
				continue;
			}
			if ( gameRenderWorld->AreasAreConnected( areaNum, i, PS_BLOCK_LOCATION ) ) {
				locationEntities[i] = static_cast<idLocationEntity *>(ent);
			}
		}
	}
}

/*
===================
idGameLocal::AddLocation
===================
*/
idLocationEntity* idGameLocal::AddLocation( const idVec3& point, const char* name ) {
	int areaNum = gameRenderWorld->PointInArea( point );
	if ( areaNum < 0 ) {
		Warning ( "idGameLocal::AddLocation: cannot add location entity '%s' at '%g %g %g'\n", name, point.x, point.y, point.z );
		return NULL;
	}
	if ( areaNum >= gameRenderWorld->NumAreas() ) {
		Error( "idGameLocal::AddLocation: areaNum >= gameRenderWorld->NumAreas()" );
	}
	if ( locationEntities[areaNum] ) {
		Warning ( "idGameLocal::AddLocation: location '%s' already exists at '%g %g %g'\n", locationEntities[areaNum]->GetName(), point.x, point.y, point.z );
		return NULL;
	}

	// Spawn the new location entity
	idDict args;
	args.Set ( "location", name );
	locationEntities[areaNum] = static_cast<idLocationEntity*>(SpawnEntityType ( idLocationEntity::GetClassType(), &args ));

	// spread to all other connected areas
	for ( int i = gameRenderWorld->NumAreas() - 1 ; i >= 0 ; i-- ) {
		if ( i == areaNum ) {
			continue;
		}
		if ( gameRenderWorld->AreasAreConnected( areaNum, i, PS_BLOCK_LOCATION ) ) {
			locationEntities[i] = static_cast<idLocationEntity *>(locationEntities[areaNum]);
		}
	}
	
	return locationEntities[areaNum];
}

/*
===================
idGameLocal::LocationForPoint

The player checks the location each frame to update the HUD text display
May return NULL
===================
*/
idLocationEntity *idGameLocal::LocationForPoint( const idVec3 &point ) {
	if ( !locationEntities ) {
		// before SpreadLocations() has been called
		return NULL;
	}

	int areaNum = gameRenderWorld->PointInArea( point );
	if ( areaNum < 0 ) {
		return NULL;
	}
	if ( areaNum >= gameRenderWorld->NumAreas() ) {
		Error( "idGameLocal::LocationForPoint: areaNum >= gameRenderWorld->NumAreas()" );
	}

	return locationEntities[ areaNum ];
}

/*
============
idGameLocal::SetPortalState
============
*/
void idGameLocal::SetPortalState( qhandle_t portal, int blockingBits ) {
	idBitMsg outMsg;
	byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];

	if ( !gameLocal.isClient ) {
		outMsg.Init( msgBuf, sizeof( msgBuf ) );
		outMsg.WriteByte( GAME_RELIABLE_MESSAGE_PORTAL );
		outMsg.WriteLong( portal );
		outMsg.WriteBits( blockingBits, NUM_RENDER_PORTAL_BITS );
		networkSystem->ServerSendReliableMessage( -1, outMsg );
	}
	gameRenderWorld->SetPortalState( portal, blockingBits );
}

/*
============
idGameLocal::sortSpawnPoints
============
*/
int idGameLocal::sortSpawnPoints( const void *ptr1, const void *ptr2 ) {
	const spawnSpot_t *spot1 = static_cast<const spawnSpot_t *>( ptr1 );
	const spawnSpot_t *spot2 = static_cast<const spawnSpot_t *>( ptr2 );
	float diff;

	diff = spot1->dist - spot2->dist;
	if ( diff < 0.0f ) {
		return 1;
	} else if ( diff > 0.0f ) {
		return -1;
	} else {
		return 0;
	}
}

// RAVEN BEGIN
// ddynerman: new gametype specific spawn code
// TODO this should be moved to idMultiplayerGame
/*
===========
idGameLocal::InitializeSpawns
randomize the order of the initial spawns
prepare for a sequence of initial player spawns
============
*/
void idGameLocal::InitializeSpawns( void ) {
	idEntity* spot = NULL;

	// initialize the spawns for clients as well, need them for free fly demo replays
	if ( !isMultiplayer ) {
		return;
	}

	spawnSpots.Clear();

	for( int i = 0; i < TEAM_MAX; i++ ) {
		teamSpawnSpots[i].Clear();
	}
	
	spot = FindEntityUsingDef( NULL, "info_player_team" );
	while( spot ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if( spot->IsType ( idPlayerStart::GetClassType() ) ) {
// RAVEN END
			if( !idStr::Icmp(spot->spawnArgs.GetString("team"), "strogg") ) {
				teamSpawnSpots[TEAM_STROGG].Append( static_cast<idPlayerStart*>(spot) );
			} else if( !idStr::Icmp(spot->spawnArgs.GetString("team"), "marine") ) {
				teamSpawnSpots[TEAM_MARINE].Append( static_cast<idPlayerStart*>(spot) );
			}

			// spawnSpots contains info_player_team as well as info_player_deathmatch
			spawnSpots.Append ( static_cast<idPlayerStart*>(spot) );
			
		}

		spot = FindEntityUsingDef( spot, "info_player_team" );
	}

	spot = FindEntityUsingDef( NULL, "info_player_deathmatch" );
	while( spot ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type 
		if( spot->IsType ( idPlayerStart::GetClassType() ) ) {
// RAVEN END
			spawnSpots.Append ( static_cast<idPlayerStart*>(spot) );
		}
		spot = FindEntityUsingDef( spot, "info_player_deathmatch" );
	}

	while( spot ) {
		// RAVEN BEGIN
		// jnewquist: Use accessor for static class type 
		if( spot->IsType ( idPlayerStart::GetClassType() ) ) {
			// RAVEN END
			if( !idStr::Icmp(spot->spawnArgs.GetString("team"), "strogg") ) {
				teamSpawnSpots[TEAM_STROGG].Append( static_cast<idPlayerStart*>(spot) );
			} else if( !idStr::Icmp(spot->spawnArgs.GetString("team"), "marine") ) {
				teamSpawnSpots[TEAM_MARINE].Append( static_cast<idPlayerStart*>(spot) );
			}

			// spawnSpots contains info_player_team as well as info_player_deathmatch
			spawnSpots.Append ( static_cast<idPlayerStart*>(spot) );

		}

		spot = FindEntityUsingDef( spot, "info_player_team" );
	}

	if( IsFlagGameType() && ( teamSpawnSpots[ TEAM_STROGG ].Num() == 0 || teamSpawnSpots[ TEAM_MARINE ].Num() == 0 ) ) {
		Error( "InitializeSpawns() - Map must have at least one Marine and one Strogg spawn for CTF gametype.");
	}

	if( spawnSpots.Num() == 0 ) {
		Error( "InitializeSpawns() - Map must have a spawn spot." );
	}

	common->Printf( "%d general spawns\n", spawnSpots.Num() );
	common->Printf( "%d team spawns (%d strogg/%d marine)\n", teamSpawnSpots[TEAM_STROGG].Num() + teamSpawnSpots[TEAM_MARINE].Num(),
														 teamSpawnSpots[TEAM_STROGG].Num(), teamSpawnSpots[TEAM_MARINE].Num());
}

/*
===========
idGameLocal::UpdateForwardSpawn
ddynerman: Updates forward spawn lists
===========
*/
void idGameLocal::UpdateForwardSpawns( rvCTFAssaultPlayerStart* point, int team ) {
	teamForwardSpawnSpots[ team ].Append( point );
}

/*
===========
idGameLocal::ClearForwardSpawn
ddynerman: Clears forward spawn lists
===========
*/
void idGameLocal::ClearForwardSpawns( void ) {
	for( int i = 0; i < TEAM_MAX; i++ ) {
		teamForwardSpawnSpots[ i ].Clear();
	}
}

/*
===========
idGameLocal::SpotWouldTelefrag
===========
*/
bool idGameLocal::SpotWouldTelefrag( idPlayer* player, idPlayerStart* spawn ) {
	idPlayer*	playerList[ MAX_CLIENTS ];
	idBounds	bound = player->GetPhysics()->GetBounds();

	bound.TranslateSelf( spawn->GetPhysics()->GetOrigin() );
	int numEntities = PlayersTouchingBounds( player, bound, CONTENTS_BODY, playerList, MAX_CLIENTS );

	return !( numEntities == 0 );
}

/*
===========
idGameLocal::SelectSpawnSpot
ddynerman: Selects a spawn spot randomly from spots furthest from the player
			This is taken from q3
===========
*/
idEntity* idGameLocal::SelectSpawnPoint( idPlayer* player ) {
	if( !isMultiplayer ) {
		idEntity* ent = FindEntityUsingDef( NULL, "info_player_start" );
		if ( !ent ) {
			Error( "No info_player_start on map.\n" );
		}
		return ent;
	}

	if ( player == NULL ) {
		return NULL;
	}

	// Give spectators any old random spot
	if ( player->team < 0 || player->team >= TEAM_MAX || player->spectating ) {
		common->DPrintf("Returning a random spot\n");
		return spawnSpots[ random.RandomInt( spawnSpots.Num() ) ];
	}

	idList<spawnSpot_t> weightedSpawns;
	idList<idPlayerStart*>* spawnArray = NULL;

	// Pick which spawns to use based on gametype
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	if( gameLocal.gameType == GAME_DM || gameLocal.gameType == GAME_TDM || gameLocal.gameType == GAME_TOURNEY ) {
		spawnArray = &spawnSpots;
	} 
	else if( IsFlagGameType() || gameLocal.gameType == GAME_DEADZONE ) {	
		if( teamForwardSpawnSpots[ player->team ].Num() ) {
			spawnArray = &teamForwardSpawnSpots[ player->team ];
		} else {
			spawnArray = &teamSpawnSpots[ player->team ];
		}
	}
// RITUAL END

	if ( spawnArray == NULL ) {
		Error( "SelectSpawnPoint() - invalid spawn list." );
		return NULL;
	}

	idVec3 refPos;
	if ( player->lastKiller != NULL && !player->lastKiller->spectating && player->lastKiller->GetInstance() == player->GetInstance() ) {
		refPos = player->lastKiller->GetPhysics()->GetOrigin();
	} else {
		refPos = player->GetPhysics()->GetOrigin();
	}

	for ( int i = 0; i < spawnArray->Num(); i++ ) {
		idPlayerStart* spot = (*spawnArray)[i];

		if ( spot->GetInstance() != player->GetInstance() || SpotWouldTelefrag( player, spot ) ) {
			continue;
		}

		idVec3	pos = spot->GetPhysics()->GetOrigin();
		float	dist = ( pos - refPos ).LengthSqr();

		spawnSpot_t	newSpot;
		
		newSpot.dist = dist;
		newSpot.ent = (*spawnArray)[ i ];
		weightedSpawns.Append( newSpot );
	}

	if ( weightedSpawns.Num() == 0 ) {
		// no spawns avaialable, spawn randomly
		common->DPrintf("no spawns avaialable, spawn randomly\n");
		return (*spawnArray)[ random.RandomInt( spawnArray->Num() ) ];
	}

	qsort( ( void * )weightedSpawns.Ptr(), weightedSpawns.Num(), sizeof( spawnSpot_t ), ( int (*)(const void *, const void *) )sortSpawnPoints );

	int rnd = rvRandom::flrand( 0.0, 1.0 ) * (weightedSpawns.Num() / 2);
	return weightedSpawns[ rnd ].ent;
}
/*
================
idGameLocal::UpdateServerInfoFlags
================
*/
// RAVEN BEGIN
// ddynerman: new gametype strings
void idGameLocal::SetGameType( void ) {
	gameType = GAME_SP;

	if ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "singleplayer" ) ) {
		mpGame.SetGameType();
	}
}
// RAVEN END

/*
================
idGameLocal::SetGlobalMaterial
================
*/
void idGameLocal::SetGlobalMaterial( const idMaterial *mat ) {
	globalMaterial = mat;
}

/*
================
idGameLocal::GetGlobalMaterial
================
*/
const idMaterial *idGameLocal::GetGlobalMaterial() {
	return globalMaterial;
}

/*
================
idGameLocal::GetSpawnId
================
*/
int idGameLocal::GetSpawnId( const idEntity* ent ) const {
	return ( gameLocal.spawnIds[ ent->entityNumber ] << GENTITYNUM_BITS ) | ent->entityNumber;
}

/*
================
idGameLocal::ThrottleUserInfo
================
*/
void idGameLocal::ThrottleUserInfo( void ) {
	mpGame.ThrottleUserInfo();
}

/*
===========
idGameLocal::ValidateServerSettings
============
*/
bool idGameLocal::ValidateServerSettings( const char* map, const char* gametype ) {
	// PickMap uses si_map directly
	// PickMap returns wether we would have to change the maps, which means settings are invalid
	assert( !idStr::Icmp( si_map.GetString(), map ) );
	if ( mpGame.PickMap( gametype, true ) ) {
		common->Printf( "map '%s' and gametype '%s' are not compatible\n", map, gametype );
		return false;
	}
	return true;
}

/*
===========
idGameLocal::NeedRestart
============
*/
bool idGameLocal::NeedRestart() {

	idDict		newInfo;
	const idKeyValue *keyval, *keyval2;

	newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );

	for ( int i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
		keyval = newInfo.GetKeyVal( i );
		keyval2 = serverInfo.FindKey( keyval->GetKey() );
		if ( !keyval2 ) {
			return true;
		}
		// a select set of si_ changes will cause a full restart of the server
		if ( keyval->GetValue().Icmp( keyval2->GetValue() ) && ( !keyval->GetKey().Icmp( "si_pure" ) || !keyval->GetKey().Icmp( "si_map" ) || !keyval->GetKey().Icmp( "si_fps" ) ) ) {
			return true;
		}
	}
	return false;
}

// RAVEN BEGIN
// jshepard: update player hud to alert to end of level
/*
===================
idGameLocal::UpdateEndLevel
===================
*/
void idGameLocal::UpdateEndLevel()	{
	idPlayer * player = GetLocalPlayer();

	if( player && player->GetHud() ) {
		player->GetHud()->HandleNamedEvent( "showExit" );
	}
}


// bdube: added
/*
================
idGameLocal::GetEffect

Get the handle of the effect with the given name
================
*/
const idDecl *idGameLocal::GetEffect ( const idDict& args, const char* effectName, const rvDeclMatType* materialType ) {
	const char *effectFile = NULL;
	const idDecl* effectDecl = NULL;

	float chance = args.GetFloat ( idStr("effectchance ") + effectName, "1" );	
	if ( random.RandomFloat ( ) > chance ) {
		return NULL;
	}

	// we should ALWAYS be playing sounds from the def.
	// hardcoded sounds MUST be avoided at all times because they won't get precached.
	assert( !idStr::Icmpn( effectName, "fx_", 3 ) );

	if ( materialType )	{
		idStr		temp;
		const char*	result = NULL;
		
		temp = effectName;
		temp += "_";

		temp += materialType->GetName();
	
		// See if the given material effect is specified
		idStr	testMP = temp;
		testMP += "_mp";

		result = args.GetString( testMP );

		if ( !result || !*result ) {
			result = args.GetString( temp );
		}
		if ( result && *result) {
			effectDecl = ( const idDecl * )declManager->FindEffect( result, false );
			if ( effectDecl ) {
				return effectDecl;
			}
		}
	}	

	// grab the non material effect name
	idStr alternateEffect = effectName;
	alternateEffect += "_mp";
	
	idStr classname = args.GetString( "classname" );
	if ( !alternateEffect.Icmp( "fx_fly_mp" ) ) {
		if ( 
			( !g_nailTrail.GetBool() && !classname.Icmpn( "projectile_nail", 15   ) ) ||
			( !g_rocketTrail.GetBool() && !classname.Icmpn( "projectile_rocket", 17 ) ) || 
			( !g_napalmTrail.GetBool() && !classname.Icmpn( "projectile_napalm", 17 ) ) ) 
		{
			alternateEffect += "_low";
		} else if ( !g_grenadeTrail.GetBool() && !classname.Icmpn( "projectile_grenade", 18 ) ) 
		{
			return NULL;
		}
	} else if ( !alternateEffect.Icmp( "fx_path_mp" ) ) 
	{
		if ( !g_railTrail.GetBool() && !classname.Icmpn( "hitscan_railgun", 15 ) ) 
		{
			alternateEffect += "_low";
		}
	}

	effectFile = args.GetString( alternateEffect );
	if ( effectFile && *effectFile ) {
		effectDecl = ( const idDecl * )declManager->FindEffect( effectFile, false );
		if ( effectDecl ) {
			return effectDecl;
		}
	}

	effectFile = args.GetString( effectName );
	if ( effectFile && *effectFile ) {
		effectDecl = ( const idDecl * )declManager->FindEffect( effectFile, false );
		if ( effectDecl ) {
			return effectDecl;
		}
	}

	// Compatibility fallback: keep grenade launcher smoke trails available even
	// when projectile defs omit fx_fly/fx_fly_mp or point at a missing decl.
	if ( !classname.Icmpn( "projectile_grenade", 18 ) &&
		 ( !idStr::Icmp( effectName, "fx_fly" ) || !idStr::Icmp( effectName, "fx_fly_mp" ) ) ) {
		effectDecl = ( const idDecl * )declManager->FindEffect( "effects/weapons/grenadelauncher/trail_mp", false );
		if ( !effectDecl ) {
			effectDecl = ( const idDecl * )declManager->FindEffect( "effects/weapons/grenadelauncher/trail", false );
		}
		if ( effectDecl ) {
			return effectDecl;
		}
	}

	if ( !effectFile || !*effectFile ) {
		return NULL;
	}

	return( ( const idDecl * )declManager->FindEffect( effectFile ) );
}

/*
================
idGameLocal::PlayEffect

Plays an effect at the given origin using the given direction
================
*/
rvClientEffect* idGameLocal::PlayEffect( 
	const idDecl			*effect, 
	const idVec3&			origin, 
	const idMat3&			axis, 
	bool					loop, 
	const idVec3&			endOrigin, 
	bool					broadcast,
	bool					predictedBit,
	effectCategory_t		category,
	const idVec4&			effectTint ) {

	if ( !effect ) {
		return NULL;
	}

	if ( !gameLocal.isNewFrame ) {
		return NULL;
	}

	if ( isServer && broadcast ) {
		idBitMsg	msg;
		byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
		idCQuat		quat;

		quat = axis.ToCQuat();
		msg.Init( msgBuf, sizeof( msgBuf ) );
		msg.BeginWriting();
		msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EFFECT );
		idGameLocal::WriteDecl( msg, effect );
		msg.WriteFloat( origin.x );
		msg.WriteFloat( origin.y );
		msg.WriteFloat( origin.z );
		msg.WriteFloat( quat.x );
		msg.WriteFloat( quat.y );
		msg.WriteFloat( quat.z );
		msg.WriteFloat( endOrigin.x );
		msg.WriteFloat( endOrigin.y );
		msg.WriteFloat( endOrigin.z );
		msg.WriteByte( category );
		msg.WriteBits( loop, 1 );
		msg.WriteBits( predictedBit, 1 );

		// send to everyone who has start or end in it's PVS
		SendUnreliableMessagePVS( msg, currentThinkingEntity, pvs.GetPVSArea( origin ), pvs.GetPVSArea( endOrigin ) );
	}

	if ( isServer && localClientNum < 0 ) {
		// no effects on dedicated server
		return NULL;
	}

	if ( bse->Filtered( effect->GetName(), category ) ) {
		// Effect filtered out
		return NULL;
	}
// RAVEN END

	if ( gameLocal.isListenServer && currentThinkingEntity && gameLocal.GetLocalPlayer() ) {
		if ( currentThinkingEntity->GetInstance() != gameLocal.GetLocalPlayer()->GetInstance() ) {
			return NULL;
		}
	}
	
	// mwhitlock: Dynamic memory consolidation
	RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_MULTIPLE_FRAME);
	rvClientEffect* clientEffect = new rvClientEffect( effect );
	RV_POP_HEAP();

	if( !clientEffect ) {
		common->Warning( "Failed to create effect \'%s\'\n", effect->GetName() );
		return NULL;
	}

	if( clientEffect->entityNumber == -1 ) {
		common->Warning( "Failed to spawn effect \'%s\'\n", effect->GetName() );
		delete clientEffect;
		return NULL;
	}

	clientEffect->SetOrigin( origin );
	clientEffect->SetAxis( axis );
	clientEffect->SetGravity( GetCurrentGravity( origin, axis ) );
	if ( !clientEffect->Play( gameLocal.time, loop, endOrigin ) ) {
		delete clientEffect;
		return NULL;
	}

	clientEffect->GetRenderEffect()->shaderParms[ SHADERPARM_RED ]		= effectTint[ 0 ];
	clientEffect->GetRenderEffect()->shaderParms[ SHADERPARM_GREEN ]	= effectTint[ 1 ];
	clientEffect->GetRenderEffect()->shaderParms[ SHADERPARM_BLUE ]		= effectTint[ 2 ];
	clientEffect->GetRenderEffect()->shaderParms[ SHADERPARM_ALPHA ]	= effectTint[ 3 ];

	return clientEffect;
}

void idGameLocal::CheckPlayerWhizzBy( idVec3 start, idVec3 end, idEntity* hitEnt, idEntity *attacker )
{
	//FIXME: make this client-side?  Work in MP?
	if ( gameLocal.isMultiplayer ) {
		return;
	}
	idPlayer* player = gameLocal.GetLocalPlayer();
	if ( !player ) {
		return;
	}
	if ( player->IsHidden() ) {
		return;
	}
	if ( player == attacker ) {
		return;
	}
	if ( player == hitEnt ) {
		return;
	}
	idVec3 center = player->firstPersonViewOrigin;
	idVec3 diff = end-center;
	if ( diff.Length() < 64.0f ) {
		//hit too close - didn't actually pass by, will hear impact sound instead
		return;
	}
	idVec3 closestPoint = player->firstPersonViewOrigin;
	if ( closestPoint.ProjectToLineSeg( start, end ) ) {
		//on line seg
		diff = closestPoint-center;
		if ( diff.Length() < 48.0f ) {
			//close enough to hear whizz-by
			idVec3 dir = end-start;
			dir.Normalize();
			idVec3 fxStart = closestPoint+dir*-32.0f;
			idVec3 fxEnd = closestPoint+dir*32.0f;
			player->PlayEffect( "fx_whizby", fxStart, player->firstPersonViewAxis, false, fxEnd );
		}
	}
}

/*
================
idGameLocal::HitScan

Run a hitscan trace from the given origin and direction
================
*/
idEntity* idGameLocal::HitScan( 
	const idDict&	hitscanDict, 
	const idVec3&	origOrigin, 
	const idVec3&	origDir, 
	const idVec3&	origFxOrigin, 
	idEntity*		owner, 
	bool			noFX,
	float			damageScale,
// twhitaker: added additionalIgnore parameter
	idEntity*		additionalIgnore,
	int				areas[ 2 ]
	) {

	idVec3		dir;
	idVec3		origin;
	idVec3		fxOrigin;
	idVec3		fxDir;
	idVec3		impulse;
	idVec4		hitscanTint( 1.0f, 1.0f, 1.0f, 1.0f );
	int			reflect;
	float		tracerChance;
	idEntity*	ignore;
	float		penetrate;

	if ( areas ) {
		areas[ 0 ] = pvs.GetPVSArea( origFxOrigin );
		areas[ 1 ] = -1;
	}

	ignore    = owner;
	penetrate = hitscanDict.GetFloat( "penetrate" );

	if( hitscanDict.GetBool( "hitscanTint" ) && owner->IsType( idPlayer::GetClassType() ) ) {
		hitscanTint = ((idPlayer*)owner)->GetHitscanTint();
	}

	// twhitaker: additionalIgnore parameter
	if ( !additionalIgnore ) {
		additionalIgnore = ignore;
	}	

	origin		 = origOrigin;
	fxOrigin	 = origFxOrigin;
	dir			 = origDir;
	tracerChance = ((g_perfTest_weaponNoFX.GetBool())?0:hitscanDict.GetFloat( "tracerchance", "0" ));

	// Apply player powerups
	if ( owner && owner->IsType( idPlayer::GetClassType() ) ) {
		damageScale *= static_cast<idPlayer*>(owner)->PowerUpModifier(PMOD_PROJECTILE_DAMAGE);
	}
	
	// Run reflections
	for ( reflect = hitscanDict.GetFloat( "reflect", "0" ); reflect >= 0; reflect-- ) {	
		idVec3		start;
		idVec3		end;
		idEntity*	ent;
		idEntity*	actualHitEnt;
		trace_t		tr;
		int			contents;
		int			collisionArea;
		idVec3		collisionPoint;
		bool		tracer;
		
		// Calculate the end point of the trace
		start    = origin;
		if ( g_perfTest_hitscanShort.GetBool() ) {
			end		 = start + (dir.ToMat3() * idVec3(idMath::ClampFloat(0,2048,hitscanDict.GetFloat ( "range", "2048" )),0,0));
		} else {
			end		 = start + (dir.ToMat3() * idVec3(hitscanDict.GetFloat ( "range", "40000" ),0,0));
		}
		if ( g_perfTest_hitscanBBox.GetBool() ) {
			contents = MASK_SHOT_BOUNDINGBOX|CONTENTS_PROJECTILE;
		} else {
			contents = MASK_SHOT_RENDERMODEL|CONTENTS_WATER|CONTENTS_PROJECTILE;
		}
		
		// Loop the traces to handle cases where something can be shot through
		while ( 1 ) {				
			// Trace to see if we hit any entities
// RAVEN BEGIN
// ddynerman: multiple clip worlds
			if ( hitscanDict.GetFloat( "trace_size", "0" ) > 0.0f )
			{
				float range = hitscanDict.GetFloat ( "range", "1024" );
				if ( range > 4096.0f )
				{
					assert( !(range > 4096.0f) );
					Warning( "idGameLocal::HitScan: hitscan def (%s) with trace_size must have max range of 4096!", hitscanDict.GetString( "classname" ) );
					range = idMath::ClampFloat( 0.0f, 4096.0f, range );
				}
				end	= start + (dir * range);

				idBounds traceBounds;
				traceBounds.Zero();
				traceBounds.ExpandSelf( hitscanDict.GetFloat( "trace_size", "0" ) );
				// twhitaker: additionalIgnore parameter
				TraceBounds( owner, tr, start, end, traceBounds, contents, additionalIgnore );
			}
			else
			{
				// twhitaker: additionalIgnore parameter
				TracePoint( owner, tr, start, end, contents, additionalIgnore );
			}
			//gameRenderWorld->DebugArrow( colorRed, start, end, 10, 5000 );
// RAVEN END
			
			// If the hitscan hit a no impact surface we can just return out
			//assert( tr.c.material );
			if ( tr.fraction >= 1.0f || (tr.c.material && tr.c.material->GetSurfaceFlags() & SURF_NOIMPACT) ) {					
				PlayEffect( hitscanDict, "fx_path", fxOrigin, dir.ToMat3(), false, tr.endpos, false, false, EC_IGNORE, hitscanTint );	
				if ( random.RandomFloat( ) < tracerChance ) {
					PlayEffect( hitscanDict, "fx_tracer", fxOrigin, dir.ToMat3(), false, tr.endpos );
					tracer = true;
				} else {
					tracer = false;
				}

				if ( areas ) {
					collisionArea = pvs.GetPVSArea( tr.endpos );
					if ( collisionArea != areas[0] ) {
						areas[1] = collisionArea;
					}
				}

				return NULL;
			}

			// computing the collisionArea from the collisionPoint fails sometimes
			if ( areas ) {
				collisionArea  = pvs.GetPVSArea( tr.c.point );
				if ( collisionArea != areas[0] ) {
					areas[1] = collisionArea;
				}
			}
			collisionPoint = tr.c.point - ( tr.c.normal * tr.c.point - tr.c.dist ) * tr.c.normal;
			ent			   = entities[ tr.c.entityNum ];
			actualHitEnt   = NULL;
			start		   = collisionPoint;

			// Keep tracing if we hit water
			if ( (ent->GetPhysics()->GetContents() & CONTENTS_WATER) || (tr.c.material && (tr.c.material->GetContentFlags() & CONTENTS_WATER)) ) {
				// Apply force to the water entity that was hit
				ent->ApplyImpulse( owner, tr.c.id, tr.c.point, -(hitscanDict.GetFloat( "push", "5000" )) * tr.c.normal );
				// Continue on excluding water
				contents &= (~CONTENTS_WATER);

				if ( !g_perfTest_weaponNoFX.GetBool() ) {
					if ( ent->CanPlayImpactEffect( owner, ent ) ) {
						if ( ent->IsType( idMover::GetClassType( ) ) ) {
							ent->PlayEffect( GetEffect( hitscanDict, "fx_impact", tr.c.materialType ), collisionPoint, tr.c.normal.ToMat3(), false, vec3_origin, false, false, EC_IMPACT, hitscanTint );
						} else {
							gameLocal.PlayEffect( GetEffect( hitscanDict, "fx_impact", tr.c.materialType ), collisionPoint, tr.c.normal.ToMat3(), false, vec3_origin, false, false, EC_IMPACT, hitscanTint );
						}
					}
				}

				continue;
			// Reflect off a bounce target?
			} else if ( (tr.c.material->GetSurfaceFlags ( ) & SURF_BOUNCE) && !hitscanDict.GetBool ( "noBounce" ) ) {
				reflect++;
			}

			// If the hit entity is bound to an actor use the actor instead
			if ( ent->fl.takedamage && ent->GetTeamMaster( ) && ent->GetTeamMaster( )->IsType ( idActor::GetClassType() ) ) {
				actualHitEnt = ent;
				ent = ent->GetTeamMaster( );
			}

			if ( !gameLocal.isClient ) {

				// Apply force to the entity that was hit
				ent->ApplyImpulse( owner, tr.c.id, tr.c.point, -tr.c.normal, &hitscanDict );

				// Handle damage to the entity
				if ( ent->fl.takedamage && !(( tr.c.material != NULL ) && ( tr.c.material->GetSurfaceFlags() & SURF_NODAMAGE )) ) {		
					const char*	damage;
				
					damage    = NULL;

					// RAVEN BEGIN
					// jdischler: code from the other project..to ensure that if an attached head is hit, the body will use the head joint
					//	otherwise damage zones for head attachments no-worky
					int hitJoint = CLIPMODEL_ID_TO_JOINT_HANDLE(tr.c.id);
					if ( ent->IsType(idActor::GetClassType()) )
						{
							idActor* entActor = static_cast<idActor*>(ent);
							if ( entActor && entActor->GetHead() && entActor->GetHead()->IsType(idAFAttachment::GetClassType()) )
								{
									idAFAttachment* headEnt = static_cast<idAFAttachment*>(entActor->GetHead());
									if ( headEnt && headEnt->entityNumber == tr.c.entityNum )
										{//hit ent's head, get the proper joint for the head
											hitJoint = entActor->GetAnimator()->GetJointHandle("head");
										}
								}
						}	
					// RAVEN END
					// Inflict damage
					if ( tr.c.materialType ) {
						damage = hitscanDict.GetString( va("def_damage_%s", tr.c.materialType->GetName()) );
					}
					if ( !damage || !*damage ) {
						damage = hitscanDict.GetString ( "def_damage" );
					}

					if ( damage && damage[0] ) {
						// RAVEN BEGIN
						// ddynerman: stats
						if( owner->IsType( idPlayer::GetClassType() ) && ent->IsType( idActor::GetClassType() ) && ent != owner && !((idPlayer*)owner)->pfl.dead ) {
							statManager->WeaponHit( (idActor*)owner, ent, ((idPlayer*)owner)->GetCurrentWeapon() );
						}
						// RAVEN END
						ent->Damage( owner, owner, dir, damage, damageScale, hitJoint );
					}

					// Let the entity add its own damage effect
					if ( !g_perfTest_weaponNoFX.GetBool() ) {
						ent->AddDamageEffect ( tr, dir, damage, owner );
					}
				} else { 
					if ( actualHitEnt
						 && actualHitEnt != ent
						 && (tr.c.material->GetSurfaceFlags ( ) & SURF_BOUNCE)
						 && actualHitEnt->spawnArgs.GetBool( "takeBounceDamage" ) )
						{//bleh...
							const char*	damage = NULL;
							// Inflict damage
							if ( tr.c.materialType ) {
								damage = hitscanDict.GetString( va("def_damage_%s", tr.c.materialType->GetName()) );
							}
							if ( !damage || !*damage ) {
								damage = hitscanDict.GetString ( "def_damage" );
							}
							if ( damage && damage[0] ) {
								actualHitEnt->Damage( owner, owner, dir, damage, damageScale, CLIPMODEL_ID_TO_JOINT_HANDLE( tr.c.id ) );
							}
						}
					if ( !g_perfTest_weaponNoFX.GetBool() ) {
						ent->AddDamageEffect( tr, dir, hitscanDict.GetString ( "def_damage" ), owner );
					}
				}
			}


			// Pass through actors
			if ( ent->IsType ( idActor::GetClassType() ) && penetrate > 0.0f ) {			
				start = collisionPoint;
				additionalIgnore = ent;
				damageScale *= penetrate;
				continue;
			}
			break;
		}
			
		// Path effect 
		fxDir = collisionPoint - fxOrigin;
		fxDir.Normalize( );
		PlayEffect( hitscanDict, "fx_path", fxOrigin, fxDir.ToMat3(), false, collisionPoint, false, false, EC_IGNORE, hitscanTint );	
		if ( !ent->fl.takedamage && random.RandomFloat ( ) < tracerChance ) {
			PlayEffect( hitscanDict, "fx_tracer", fxOrigin, fxDir.ToMat3(), false, collisionPoint );
			tracer = true;
		} else {
			tracer = false;
		}

		if ( !reflect ) {
			//on initial trace only
			if ( hitscanDict.GetBool( "doWhizz" ) ) {
				//play whizz-by sound if trace is close to player's head
				CheckPlayerWhizzBy( origin, collisionPoint, ent, owner );
			}
		}

		// Play a different effect when reflecting
		if ( !reflect || ent->fl.takedamage ) {
			idMat3 axis;
			
			// Effect axis when hitting actors is along the direction of impact because actor models are 
			// very detailed.
			if ( ent->IsType ( idActor::GetClassType() ) ) {
				axis = ((-dir + tr.c.normal) * 0.5f).ToMat3();
			} else {
				axis = tr.c.normal.ToMat3();
			}
			
			if ( !g_perfTest_weaponNoFX.GetBool() ) {
				if ( ent->CanPlayImpactEffect( owner, ent ) ) {
					if ( ent->IsType( idMover::GetClassType( ) ) ) {
						ent->PlayEffect( GetEffect( hitscanDict, "fx_impact", tr.c.materialType ), collisionPoint, axis, false, vec3_origin, false, false, EC_IMPACT, hitscanTint );
					} else {
						gameLocal.PlayEffect( GetEffect( hitscanDict, "fx_impact", tr.c.materialType ), collisionPoint, axis, false, vec3_origin, false, false, EC_IMPACT, hitscanTint );
					}
				}
			}
			
			// End of reflection
			return ent;
		} else {
			PlayEffect( GetEffect( hitscanDict, "fx_reflect", tr.c.materialType ), collisionPoint, tr.c.normal.ToMat3() );
		}
		
		// Calc new diretion based on bounce
		origin = start;
		fxOrigin = start;
		dir = ( dir - ( 2.0f * DotProduct( dir, tr.c.normal ) * tr.c.normal ) );
		dir.Normalize( );

		// Increase damage scale on reflect		
		damageScale += hitscanDict.GetFloat( "reflect_powerup", "0" );
	}	
	
	assert( false );
	
	return NULL;
}

/*
===================
idGameLocal::RegisterClientEntity
===================
*/
void idGameLocal::RegisterClientEntity( rvClientEntity *cent ) {
	int entityNumber;

	assert ( cent );

	if ( clientSpawnCount >= ( 1 << ( 32 - CENTITYNUM_BITS ) ) ) {
//		Error( "idGameLocal::RegisterClientEntity: spawn count overflow" );
		clientSpawnCount = INITIAL_SPAWN_COUNT;
	}

	// Find a free entity index to use
	while( clientEntities[firstFreeClientIndex] && firstFreeClientIndex < MAX_CENTITIES ) {
		firstFreeClientIndex++;
	}

	if ( firstFreeClientIndex >= MAX_CENTITIES ) {
		cent->PostEventMS ( &EV_Remove, 0 );
		Warning( "idGameLocal::RegisterClientEntity: no free client entities" );
		return;
	}

	entityNumber = firstFreeClientIndex++;

	// Add the client entity to the lists
	clientEntities[ entityNumber ] = cent;
	clientSpawnIds[ entityNumber ] = clientSpawnCount++;
	cent->entityNumber = entityNumber;
	cent->spawnNode.AddToEnd( clientSpawnedEntities );
	cent->spawnArgs.TransferKeyValues( spawnArgs );

	if ( entityNumber >= num_clientEntities ) {
		num_clientEntities++;
	}
}

/*
===================
idGameLocal::UnregisterClientEntity
===================
*/
void idGameLocal::UnregisterClientEntity( rvClientEntity* cent ) {
	assert( cent );
	
	// No entity number then it failed to register
	if ( cent->entityNumber == -1 ) {
		return;
	}
	
	cent->spawnNode.Remove ( );
	cent->bindNode.Remove ( );

	if ( clientEntities [ cent->entityNumber ] == cent ) {
		clientEntities [ cent->entityNumber ] = NULL;
		clientSpawnIds[ cent->entityNumber ] = -1;
		if ( cent->entityNumber < firstFreeClientIndex ) {
			firstFreeClientIndex = cent->entityNumber;
		}
		cent->entityNumber = -1;
	}
}

/*
===================
idGameLocal::Translation
===================
*/
bool idGameLocal::Translation( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity, const idEntity *passEntity2 ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if ( clipWorld ) {
		return clipWorld->Translation( results, start, end, mdl, trmAxis, contentMask, passEntity, passEntity2 );
	} 
	
	return false;
}

/*
===================
idGameLocal::Rotation
===================
*/
bool idGameLocal::Rotation( const idEntity* ent, trace_t &results, const idVec3 &start, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->Rotation( results, start, rotation, mdl, trmAxis, contentMask, passEntity );
	}

	return false;
}

/*
===================
idGameLocal::Motion
===================
*/
bool idGameLocal::Motion( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->Motion( results, start, end, rotation, mdl, trmAxis, contentMask, passEntity );
	}

	return false;
}


/*
===================
idGameLocal::Contacts
===================
*/
int idGameLocal::Contacts( const idEntity* ent, contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->Contacts( contacts, maxContacts, start, dir, depth, mdl, trmAxis, contentMask, passEntity );
	}

	return 0;
}

/*
===================
idGameLocal::Contents
===================
*/
int idGameLocal::Contents( const idEntity* ent, const idVec3 &start, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity, idEntity **touchedEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->Contents( start, mdl, trmAxis, contentMask, passEntity, touchedEntity );
	}

	return 0;
}

/*
===================
idGameLocal::TracePoint
===================
*/
bool idGameLocal::TracePoint( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, int contentMask, const idEntity *passEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->TracePoint( results, start, end, contentMask, passEntity );
	}

	return false;
}

/*
===================
idGameLocal::TraceBounds
===================
*/
bool idGameLocal::TraceBounds( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, const idBounds &bounds, int contentMask, const idEntity *passEntity ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->TraceBounds( results, start, end, bounds, contentMask, passEntity );
	}

	return false;
}

/*
===================
idGameLocal::TranslationModel
===================
*/
void idGameLocal::TranslationModel( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, idCollisionModel *model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		clipWorld->TranslationModel( results, start, end, mdl, trmAxis, contentMask, model, modelOrigin, modelAxis );
	}
}

/*
===================
idGameLocal::RotationModel
===================
*/
void idGameLocal::RotationModel( const idEntity* ent, trace_t &results, const idVec3 &start, const idRotation &rotation, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, idCollisionModel *model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		clipWorld->RotationModel( results, start, rotation, mdl, trmAxis, contentMask, model, modelOrigin, modelAxis );
	}
}

/*
===================
idGameLocal::ContactsModel
===================
*/
int idGameLocal::ContactsModel( const idEntity* ent, contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, idCollisionModel *model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->ContactsModel( contacts, maxContacts, start, dir, depth, mdl, trmAxis, contentMask, model, modelOrigin, modelAxis );
	}

	return 0;
}

/*
===================
idGameLocal::ContentsModel
===================
*/
int idGameLocal::ContentsModel( const idEntity* ent, const idVec3 &start, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, idCollisionModel *model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->ContentsModel( start, mdl, trmAxis, contentMask, model, modelOrigin, modelAxis );
	}

	return 0;
}

/*
===================
idGameLocal::TranslationEntities
===================
*/	
void idGameLocal::TranslationEntities( const idEntity* ent, trace_t &results, const idVec3 &start, const idVec3 &end, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity, const idEntity *passEntity2 ) {
	idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		clipWorld->TranslationEntities( results, start, end, mdl, trmAxis, contentMask, passEntity, passEntity2 );
	}
}

/*
===================
idGameLocal::GetModelContactFeature
===================
*/
bool idGameLocal::GetModelContactFeature( const idEntity* ent, const contactInfo_t &contact, const idClipModel *clipModel, idFixedWinding &winding ) const {
	const idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->GetModelContactFeature( contact, clipModel, winding );
	}

	return false;
}

/*
===================
idGameLocal::EntitiesTouchingBounds
===================
*/
int idGameLocal::EntitiesTouchingBounds	( const idEntity* ent, const idBounds &bounds, int contentMask, idEntity **entityList, int maxCount ) const {
	const idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->EntitiesTouchingBounds( bounds, contentMask, entityList, maxCount );
	}

	return 0;
}

/*
===================
idGameLocal::ClipModelsTouchingBounds
===================
*/
int idGameLocal::ClipModelsTouchingBounds( const idEntity* ent, const idBounds &bounds, int contentMask, idClipModel **clipModelList, int maxCount ) const {
	const idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->ClipModelsTouchingBounds( bounds, contentMask, clipModelList, maxCount );
	}

	return 0;
}

/*
===================
idGameLocal::PlayersTouchingBounds
===================
*/
int idGameLocal::PlayersTouchingBounds	( const idEntity* ent, const idBounds &bounds, int contentMask, idPlayer **entityList, int maxCount ) const {
	const idClip* clipWorld = GetEntityClipWorld( ent );
	
	if( clipWorld ) {
		return clipWorld->PlayersTouchingBounds( bounds, contentMask, entityList, maxCount );
	}

	return 0;
}

/*
===================
idGameLocal::GetWorldBounds
===================
*/
const idBounds& idGameLocal::GetWorldBounds( const idEntity* ent ) const {
	const idClip* clipWorld = GetEntityClipWorld( ent );
	
	if ( clipWorld ) {
		return clipWorld->GetWorldBounds();
	}

	return clip[ 0 ]->GetWorldBounds();
}

/*
===================
idGameLocal::GetEntityClipWorld
===================
*/
idClip* idGameLocal::GetEntityClipWorld( const idEntity* ent ) {
	if( ent == NULL ) {
		return clip[ 0 ];
	}

	if( ent->GetClipWorld() < 0 || ent->GetClipWorld() >= clip.Num() ) {
		Warning( "idGameLocal::GetEntityClipWorld() - invalid clip world %d on entity %s (valid range: 0 - %d)\n", ent->GetClipWorld(), ent->GetClassname(), clip.Num() - 1 );
		return NULL;
	}
	return clip[ ent->GetClipWorld() ];
}

/*
===================
idGameLocal::GetEntityClipWorld
===================
*/
const idClip* idGameLocal::GetEntityClipWorld( const idEntity* ent ) const {
	if( ent == NULL ) {
		return clip[ 0 ];
	}

	if( ent->GetClipWorld() < 0 || ent->GetClipWorld() >= clip.Num() ) {
		Warning( "idGameLocal::GetEntityClipWorld() - invalid clip world %d on entity %s (valid range: 0 - %d)\n", ent->GetClipWorld(), ent->GetClassname(), clip.Num() - 1  );
		return NULL;
	}
	return clip[ ent->GetClipWorld() ];
}

/*
===================
idGameLocal::AddClipWorld
===================
*/
int idGameLocal::AddClipWorld( int id ) {
	if( id >= clip.Num() ) {
		// if we want an index higher in the list, fill the intermediate indices with empties
		for( int i = clip.Num(); i <= id; i++ ) {
			clip.Append( NULL );
		}
	}

	if( clip[ id ] == NULL ) {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
		RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
// RAVEN END
		clip[ id ] = new idClip();
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
		RV_POP_HEAP();
// RAVEN END
		clip[ id ]->Init();
	}
	return id;
}

/*
===================
idGameLocal::RemoveClipWorld
===================
*/
void idGameLocal::RemoveClipWorld( int id ) {
	assert( id >= 0 && id < clip.Num() );

	clip[ id ]->Shutdown();
	delete clip[ id ];
	clip[ id ] = NULL;
}

/*
===================
idGameLocal::ShutdownInstances
===================
*/
void idGameLocal::ShutdownInstances( void ) {
	if( gamestate == GAMESTATE_UNINITIALIZED ) {
		return;
	}

	instances.DeleteContents( true );

	// free the trace model used for the defaultClipModel
	idClip::FreeDefaultClipModel();
}

/*
===================
idGameLocal::AddInstance
===================
*/
int idGameLocal::AddInstance( int id, bool deferPopulate ) {
	if ( id == -1 ) {
		id = instances.Num();
	}

	if ( id >= instances.Num() ) {
		// if we want an index higher in the list, fill the intermediate indices with empties
		for( int i = instances.Num(); i <= id; i++ ) {
			instances.Append( NULL );
		}
	}

	if ( instances[ id ] == NULL ) {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
		RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
		instances[ id ] = new rvInstance( id, deferPopulate );
		RV_POP_HEAP();
// RAVEN END

		if ( !deferPopulate ) {
			// keep track of the high watermark
			ServerSetEntityIndexWatermark( id );
		}

		common->DPrintf( "idGameLocal::AddInstance(): Adding instance %d\n", instances[ id ]->GetInstanceID() );
	} else {
		common->DPrintf( "idGameLocal::AddInstance(): Instance %d already exists\n", instances[ id ]->GetInstanceID() );
	}
	
	// keep the min spawn index correctly set
	ServerSetMinSpawnIndex();
	
	return instances[ id ]->GetInstanceID();
}

/*
===================
idGameLocal::RemoveInstance
===================
*/
void idGameLocal::RemoveInstance( int id ) {
	delete instances[ id ];
	instances[ id ] = NULL;
}

/*
===================
idGameLocal::GetPlayerName
Returns the specified player name, max of 64 chars
===================
*/
void idGameLocal::GetPlayerName( int clientNum, char* name ) {
	if( !gameLocal.entities[ clientNum ] ) {
		return;
	}

	idStr::Copynz( name, gameLocal.GetUserInfo( clientNum )->GetString( "ui_name" ), 64 );
}

/*
===================
idGameLocal::GetPlayerClan
Returns the specified player clan, max of 64 chars
===================
*/
void idGameLocal::GetPlayerClan( int clientNum, char* clan ) {
	if( !gameLocal.entities[ clientNum ] ) {
		return;
	}

	idStr::Copynz( clan, gameLocal.GetUserInfo( clientNum )->GetString( "ui_clan" ), 64 );
}

/*
===================
idGameLocal::SetFriend
===================
*/
void idGameLocal::SetFriend( int clientNum, bool isFriend ) {
	if( !gameLocal.GetLocalPlayer() ) {
		Warning( "idGameLocal::SetFriend() - SetFriend() called with NULL local player\n" );
		return;
	}

	gameLocal.GetLocalPlayer()->SetFriend( clientNum, isFriend );
}

/*
===================
idGameLocal::GetLongGametypeName
===================
*/
const char*	idGameLocal::GetLongGametypeName( const char* gametype ) {
	return mpGame.GetLongGametypeName( gametype );
}

void idGameLocal::Cmd_PrintMapEntityNumbers_f( const idCmdArgs& args ) {
	int instance = 0;

	if ( args.Argc() > 1 ) {
		instance = atoi( args.Argv( 1 ) );
	} 

	if( gameLocal.instances[ instance ] ) {
		gameLocal.instances[ instance ]->PrintMapNumbers();
	}
}

void idGameLocal::Cmd_PrintSpawnIds_f( const idCmdArgs& args ) {
	for( int i = 0; i < MAX_GENTITIES; i++ ) {
		if( gameLocal.entities[ i ] ) {
			gameLocal.Printf( "Spawn id %d: %d\n", i, gameLocal.spawnIds[ i ] );
		}
	}
}

/*
===============
idGameLocal::IsTeamPowerups
===============
*/
bool idGameLocal::IsTeamPowerups( void ) {
	if ( !serverInfo.GetBool( "si_isBuyingEnabled" ) ) {
		return false;
	}
	if ( !IsTeamGameType() ) {
		return false;
	}
	return ( gameType != GAME_ARENA_CTF );
}

/*
===============
idGameLocal::GetRandomBotName
===============
*/
void idGameLocal::GetRandomBotName( int clientNum, idStr& name ) {
	(void)clientNum;
	name = "Quake4Bot";
}

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
/*
===================
idGameLocal::FlushBeforelevelLoad
===================
*/
void idGameLocal::FlushBeforelevelLoad( void )
{
	TIME_THIS_SCOPE( __FUNCLINE__);

#ifndef _XENON
	MapShutdown();
#else
	mpGame.Clear();
#endif
	for(int i = 0; i < aasNames.Num(); i++)
	{
		aasList[i]->Shutdown();
	}
}
#endif
// RAVEN END

// dluetscher: moved the overloaded new/delete to sys_local.cpp and Game_local.cpp (from Heap.h)
//			   so that the tools.dll will link.
#if !defined(_XBOX) && (defined(ID_REDIRECT_NEWDELETE) || defined(_RV_MEM_SYS_SUPPORT))

#undef new
#undef delete
#undef Mem_Alloc
#undef Mem_Free

#ifdef ID_DEBUG_MEMORY
void *operator new( size_t s, int t1, int t2, char *fileName, int lineNumber ) {
	return Mem_Alloc( s, fileName, lineNumber, MemScopedTag_GetTopTag() );
}

void operator delete( void *p, int t1, int t2, char *fileName, int lineNumber ) {
	Mem_Free( p, fileName, lineNumber );
}

void *operator new[]( size_t s, int t1, int t2, char *fileName, int lineNumber ) {
	return Mem_Alloc( s, fileName, lineNumber, MemScopedTag_GetTopTag() );
}

void operator delete[]( void *p, int t1, int t2, char *fileName, int lineNumber ) {
	Mem_Free( p, fileName, lineNumber );
}

void *operator new( size_t s ) {
	return Mem_Alloc( s, "", 0, MemScopedTag_GetTopTag() );
}

void operator delete( void *p ) {
	Mem_Free( p, "", 0 );
}

void *operator new[]( size_t s ) {
	return Mem_Alloc( s, "", 0, MemScopedTag_GetTopTag() );
}

void operator delete[]( void *p ) {
	Mem_Free( p, "", 0 );
}

#else	// #ifdef ID_DEBUG_MEMORY

void *operator new( size_t s ) {
	return Mem_Alloc( s, MemScopedTag_GetTopTag() );
}

void operator delete( void *p ) {
	Mem_Free( p );
}

void *operator new[]( size_t s ) {
	return Mem_Alloc( s, MemScopedTag_GetTopTag() );
}

void operator delete[]( void *p ) {
	Mem_Free( p );
}
#endif	// #else #ifdef ID_DEBUG_MEMORY
#endif	// #if defined(ID_REDIRECT_NEWDELETE) || defined(_RV_MEM_SYS_SUPPORT)
// RAVEN END
