
#include "g_local.h"
#include "g_spawn.h"
#include "fields.h"
#include "ai_pathfinding.h"
#include "p_body.h"
#include "..\qcommon\configstring.h"

#if TIME_ENTS
	#include <windows.h>
#endif

void CalculatePlayerEntryWeapons(int *weaponsAvailable);

byte	entSoundsToCache[NUM_CLSFX];
char	entDebrisToCache[NUM_CLGHL];

//	1/12/99 sfs -- this should fix the crummy randomness in pakfile creating
static	cvar_t	*fs_createpak;

extern spawn_t			environSpawns[];
extern spawn_t			funcSpawns[];
extern spawn_t			triggerSpawns[];
extern spawn_t			targetSpawns[];
extern spawn_t			lightSpawns[];
extern spawn_t			bosniaSpawns[];
extern spawn_t			castleSpawns[];
extern spawn_t			iraqSpawns[];
extern spawn_t			genericSpawns[];
extern spawn_t			newyorkSpawns[];
extern spawn_t			siberiaSpawns[];
extern spawn_t			tokyoSpawns[];
extern spawn_t			ugandaSpawns[];
extern spawn_t			dmSpawns[];

void CleanUpGame (void);

//  info_...
//void SP_info_merc_start (edict_t *ent);
void SP_info_notnull (edict_t *self);
void SP_info_null (edict_t *self); 
void SP_info_player_start (edict_t *ent);
void SP_info_player_deathmatch (edict_t *ent);
void SP_info_player_team1 (edict_t *ent);
void SP_info_player_team2 (edict_t *ent);
//void SP_info_player_coop (edict_t *ent);
void SP_info_player_intermission (edict_t *ent);

int ai_loadAllBolts = 0;


// misc non-id stuff
void SP_func_remote_camera(edict_t *Self);
void SP_misc_ctf_base (edict_t *self);


// misc_ id stuff
void SP_path_corner (edict_t *self);
void SP_point_combat (edict_t *self);
void SP_misc_gib_arm (edict_t *self);
void SP_misc_gib_leg (edict_t *self);
void SP_misc_gib_head (edict_t *self);

void SP_spawner(edict_t *ent);
void SP_spawner_monster(edict_t *ent);
void SP_spawner_boosterpack(edict_t *ent);

//  worldspawn...
void SP_worldspawn (edict_t *ent);

//  testing things
void SP_ghoul_model (edict_t *ent);
void SP_test_model (edict_t *ent);
void SP_test_rj (edict_t *ent);

extern void SP_script_runner (edict_t *ent);

void InitEntSoundCaching(void);
void InitEntMetalSoundCaching(void);
void InitEntDebrisCaching(void);
void CacheEntDebrisAndSounds(edict_t *ent);
void CacheAllDebris(void);
void CacheAllWallEffects(void);
void CacheAllSounds(void);

spawn_t	spawns[] =
{
	//  info_...
//	{"info_merc_start",					SP_info_merc_start},
	{"info_null",						SP_info_null},
	{"info_notnull",					SP_info_notnull},
//	{"info_player_coop",				SP_info_player_coop},
	{"info_player_deathmatch",			SP_info_player_deathmatch},
	{"info_player_team1",				SP_info_player_team1},
	{"info_player_team2",				SP_info_player_team2},
	{"info_player_intermission",		SP_info_player_intermission},
	{"info_player_start",				SP_info_player_start},
	
	//  misc_nonid stuff
	{"func_remote_camera",				SP_func_remote_camera},
	{"misc_ctf_base",					SP_misc_ctf_base},

	//  misc_ id stuff
	{"path_corner",						SP_path_corner},
	{"point_combat",					SP_point_combat},

	//  monster_...
	{"m_x_romulan",					SP_m_x_romulan},
	{"m_x_tank",					SP_m_x_tank},
	{"m_x_snowcat",					SP_m_x_snowcat},
	{"m_x_husky",					SP_m_x_husky},
	{"m_x_rottweiler",				SP_m_x_rottweiler},	
	{"m_x_germanshep",				SP_m_x_germanshep},
	{"m_x_raiderdog",				SP_m_x_raiderdog},
	{"m_x_bull",					SP_m_x_bull},
	{"m_x_chopper_black",			SP_m_x_chopper_black},
	{"m_x_chopper_green",			SP_m_x_chopper_green},
	{"m_x_chopper_white",			SP_m_x_chopper_white},
	{"m_x_hind",					SP_m_x_hind},
	{"m_x_mcharacter",				SP_m_x_mcharacter},
	{"m_x_mcharacter_snow",			SP_m_x_mcharacter_snow},
	{"m_x_mcharacter_desert",		SP_m_x_mcharacter_desert},
	{"m_x_mmerc",					SP_m_x_mmerc},
	{"m_x_mhurtmerc",				SP_m_x_mhurtmerc},
	{"m_x_msam",					SP_m_x_msam},
	{"m_x_miraqboss",				SP_m_x_miraqboss},
	{"m_x_mraiderboss1",			SP_m_x_mraiderboss1},
	{"m_x_mraiderboss2",			SP_m_x_mraiderboss2},
	{"m_x_ftaylor",					SP_m_x_ftaylor},
	{"m_x_mskinboss",				SP_m_x_mskinboss},

	{"m_sib_mtrooper1a",			SP_m_sib_mtrooper1a},
	{"m_sib_mtrooper1b",			SP_m_sib_mtrooper1b},
	{"m_sib_mtrooper2",				SP_m_sib_mtrooper2},
	{"m_sib_mguard1",				SP_m_sib_mguard1},
	{"m_sib_fguard2",				SP_m_sib_fguard2},
	{"m_sib_mguard3",				SP_m_sib_mguard3},
	{"m_sib_mguard4",				SP_m_sib_mguard4},
	{"m_sib_mcleansuit",			SP_m_sib_mcleansuit},
	{"m_sib_eofficer",				SP_m_sib_eofficer},
	{"m_sib_mmechanic",				SP_m_sib_mmechanic},
	{"m_sib_escientist1",			SP_m_sib_escientist1},
	{"m_sib_fscientist2",			SP_m_sib_fscientist2},

	{"m_afr_msoldier1a",			SP_m_afr_msoldier1a},
	{"m_afr_msoldier1b",			SP_m_afr_msoldier1b},
	{"m_afr_msoldier2",				SP_m_afr_msoldier2},
	{"m_afr_msoldier3",				SP_m_afr_msoldier3},
	{"m_afr_msniper",				SP_m_afr_msniper},
	{"m_afr_ecommander",			SP_m_afr_ecommander},
	{"m_afr_eworker",				SP_m_afr_eworker},
	{"m_afr_mbrute",				SP_m_afr_mbrute},
	{"m_afr_mrocket",				SP_m_afr_mrocket},

	{"m_kos_mgrunt1",				SP_m_kos_mgrunt1},
	{"m_kos_mgrunt2",				SP_m_kos_mgrunt2},
	{"m_kos_mgrunt3",				SP_m_kos_mgrunt3},
	{"m_kos_msniper1a",				SP_m_kos_msniper1a},
	{"m_kos_msniper1b",				SP_m_kos_msniper1b},
	{"m_kos_mcomtroop",				SP_m_kos_mcomtroop},
	{"m_kos_eofficer",				SP_m_kos_eofficer},
	{"m_kos_mbrute1a",				SP_m_kos_mbrute1a},
	{"m_kos_mbrute1b",				SP_m_kos_mbrute1b},
	{"m_kos_mmechanic",				SP_m_kos_mmechanic},
	{"m_kos_mrebel",				SP_m_kos_mrebel},
	{"m_kos_mklaleader",			SP_m_kos_mklaleader},
	{"m_kos_erefugee",				SP_m_kos_erefugee},

	{"m_tok_mman1",					SP_m_tok_mman1},
	{"m_tok_mman2",					SP_m_tok_mman2},
	{"m_tok_mhench1",				SP_m_tok_mhench1},
	{"m_tok_mhench2",				SP_m_tok_mhench2},
	{"m_tok_mkiller",				SP_m_tok_mkiller},
	{"m_tok_fassassin",				SP_m_tok_fassassin},
	{"m_tok_mninja",				SP_m_tok_mninja},
	{"m_tok_mbrute",				SP_m_tok_mbrute},
	{"m_tok_fwoman1",				SP_m_tok_fwoman1},
	{"m_tok_fwoman2",				SP_m_tok_fwoman2},

	{"m_irq_msoldier1",				SP_m_irq_msoldier1},
	{"m_irq_msoldier2a",			SP_m_irq_msoldier2a},
	{"m_irq_msoldier2b",			SP_m_irq_msoldier2b},
	{"m_irq_mrepguard1a",			SP_m_irq_mrepguard1a},
	{"m_irq_mrepguard1b",			SP_m_irq_mrepguard1b},
	{"m_irq_mpolice",				SP_m_irq_mpolice},
	{"m_irq_eofficer",				SP_m_irq_eofficer},
	{"m_irq_mcommander",			SP_m_irq_mcommander},
	{"m_irq_mbrute1a",				SP_m_irq_mbrute1a},
	{"m_irq_mbrute1b",				SP_m_irq_mbrute1b},
	{"m_irq_mbodyguard",			SP_m_irq_mbodyguard},
	{"m_irq_mrocket",				SP_m_irq_mrocket},
	{"m_irq_msaddam",				SP_m_irq_msaddam},
	{"m_irq_moilworker",			SP_m_irq_moilworker},
	{"m_irq_eman1",					SP_m_irq_eman1},
	{"m_irq_mman2",					SP_m_irq_mman2},
	{"m_irq_fwoman1",				SP_m_irq_fwoman1},
	{"m_irq_fwoman2",				SP_m_irq_fwoman2},

	{"m_cas_mraider1",				SP_m_cas_mraider1},
	{"m_cas_mraider2a",				SP_m_cas_mraider2a},
	{"m_cas_mraider2b",				SP_m_cas_mraider2b},
	{"m_cas_mbrute",				SP_m_cas_mbrute},
	{"m_cas_ffemale",				SP_m_cas_ffemale},
	{"m_cas_mrocket",				SP_m_cas_mrocket},

	{"m_nyc_mskinhead1",			SP_m_nyc_mskinhead1},
	{"m_nyc_mskinhead2a",			SP_m_nyc_mskinhead2a},
	{"m_nyc_mskinhead2b",			SP_m_nyc_mskinhead2b},
	{"m_nyc_eskinhead3",			SP_m_nyc_eskinhead3},
	{"m_nyc_fskinchick",			SP_m_nyc_fskinchick},
	{"m_nyc_mpunk",					SP_m_nyc_mpunk},
	{"m_nyc_epunk",					SP_m_nyc_epunk},
	{"m_nyc_mswat",					SP_m_nyc_mswat},
	{"m_nyc_mswatleader",			SP_m_nyc_mswatleader},
	{"m_nyc_estockbroker",			SP_m_nyc_estockbroker},
	{"m_nyc_ebum",					SP_m_nyc_ebum},
	{"m_nyc_etourist",				SP_m_nyc_etourist},
	{"m_nyc_mpolitician",			SP_m_nyc_mpolitician},
	{"m_nyc_fwoman",				SP_m_nyc_fwoman},
	

	{"script_runner",					SP_script_runner},

	{"spawner",							SP_spawner},
	{"spawner_monster",					SP_spawner_monster},
	{"spawner_boosterpack",				SP_spawner_boosterpack},

	// worldspawn
	{"worldspawn",						SP_worldspawn},
	
	//  testing stuff
	{"ghoul_model",						SP_ghoul_model},
	{"test_model",						SP_test_model},
	{"test_rj",							SP_test_rj},


	{NULL,								NULL}
};

field_t fields[] =
{
	{"classname", FOFS(classname), F_STRING},
	{"origin", FOFS(s.origin), F_VECTOR},
	{"model", FOFS(model), F_STRING},
	{"spawnflags", FOFS(spawnflags), F_INT},
	{"speed", FOFS(speed), F_FLOAT},
	{"accel", FOFS(accel), F_FLOAT},
	{"decel", FOFS(decel), F_FLOAT},
	{"target", FOFS(target), F_STRING},
	{"targetname", FOFS(targetname), F_STRING},
	{"scripttarget", FOFS(scripttarget), F_STRING},
	{"pathtarget", FOFS(pathtarget), F_STRING},
	{"deathtarget", FOFS(deathtarget), F_STRING},
	{"killtarget", FOFS(killtarget), F_STRING},
	{"killfacing", FOFS(killfacing), F_STRING},
	{"combattarget", FOFS(combattarget), F_STRING},
	{"soundname", FOFS(soundName), F_STRING},
	{"spawn1", FOFS(spawn1), F_STRING},
	{"spawn2", FOFS(spawn2), F_STRING},
	{"spawn3", FOFS(spawn3), F_STRING},
	{"message", FOFS(message), F_STRING},
	{"sp_message", FOFS(sp_message), F_INT},
	{"team", FOFS(team), F_STRING},
	{"wait", FOFS(wait), F_FLOAT},
	{"delay", FOFS(delay), F_FLOAT},
	{"random", FOFS(random), F_FLOAT},
	{"style", FOFS(style), F_INT},
	{"count", FOFS(count), F_INT},
	{"health", FOFS(health), F_INT},
	{"sounds", FOFS(sounds), F_INT},
	{"light", 0, F_IGNORE},
	{"dmg", FOFS(dmg), F_INT},
	{"angles", FOFS(s.angles), F_VECTOR},
	{"angle", FOFS(s.angles), F_ANGLEHACK},
	{"mass", FOFS(mass), F_INT},
	{"volume", FOFS(volume), F_FLOAT},
	{"attenuation", FOFS(attenuation), F_FLOAT},
	{"map", FOFS(map), F_STRING},
	{"skin",FOFS(s.skinnum), F_INT},
	{"alertradius", FOFS(alertRadius), F_FLOAT},
	{"personality", FOFS(personality), F_INT},

	// temp spawn vars -- only valid when the spawn function is called
	{"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP},
	{"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP},
	{"height", STOFS(height), F_INT, FFL_SPAWNTEMP},
	{"noise", STOFS(noise), F_STRING, FFL_SPAWNTEMP},
	{"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP},
	{"item", STOFS(item), F_STRING, FFL_SPAWNTEMP},
	{"gravity", STOFS(gravity), F_STRING, FFL_SPAWNTEMP},
	{"sky", STOFS(sky), F_STRING, FFL_SPAWNTEMP},
	{"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP},
	{"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP},
	{"terrainset", STOFS(terrainSet), F_STRING, FFL_SPAWNTEMP},
	{"musicSet", STOFS(musicSet), F_STRING, FFL_SPAWNTEMP},
	{"color", STOFS(color), F_VECTOR, FFL_SPAWNTEMP},
	{"endpoint", STOFS(endpoint), F_VECTOR, FFL_SPAWNTEMP},
	{"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP},
	{"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP},
	{"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP},
	{"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP},
	{"nextmap", STOFS(nextmap), F_STRING, FFL_SPAWNTEMP},
	{"script", STOFS(script), F_STRING, FFL_SPAWNTEMP},
	{"killedvalue", STOFS(killedValue), F_INT, FFL_SPAWNTEMP},
	{"survivalvalue", STOFS(survivalValue), F_INT, FFL_SPAWNTEMP},
	{"material", STOFS(material), F_INT, FFL_SPAWNTEMP},
	{"scale", STOFS(scale), F_FLOAT, FFL_SPAWNTEMP},
	{"merctype", STOFS(merctype), F_INT, FFL_SPAWNTEMP},
	{"mercnum", STOFS(mercnum), F_INT, FFL_SPAWNTEMP},
	{"waitaction1", STOFS(waitAction1), F_STRING, FFL_SPAWNTEMP},
	{"waitaction2", STOFS(waitAction2), F_STRING, FFL_SPAWNTEMP},
	{"waitaction3", STOFS(waitAction3), F_STRING, FFL_SPAWNTEMP},
	{"waitaction4", STOFS(waitAction4), F_STRING, FFL_SPAWNTEMP},
	{"waitaction5", STOFS(waitAction5), F_STRING, FFL_SPAWNTEMP},
	{"waitaction6", STOFS(waitAction6), F_STRING, FFL_SPAWNTEMP},
	{"moveaction", STOFS(moveAction), F_STRING, FFL_SPAWNTEMP},
	{"screeneffect", STOFS(screeneffect), F_INT, FFL_SPAWNTEMP},
	{"spawnfrequency", STOFS(spawnfrequency), F_FLOAT, FFL_SPAWNTEMP},
	{"parm1", STOFS(parms[0]), F_STRING, FFL_SPAWNTEMP},
	{"parm2", STOFS(parms[1]), F_STRING, FFL_SPAWNTEMP},
	{"parm3", STOFS(parms[2]), F_STRING, FFL_SPAWNTEMP},
	{"parm4", STOFS(parms[3]), F_STRING, FFL_SPAWNTEMP},
	{"parm5", STOFS(parms[4]), F_STRING, FFL_SPAWNTEMP},
	{"parm6", STOFS(parms[5]), F_STRING, FFL_SPAWNTEMP},
	{"parm7", STOFS(parms[6]), F_STRING, FFL_SPAWNTEMP},
	{"parm8", STOFS(parms[7]), F_STRING, FFL_SPAWNTEMP},
	{"parm9", STOFS(parms[8]), F_STRING, FFL_SPAWNTEMP},
	{"parm10", STOFS(parms[9]), F_STRING, FFL_SPAWNTEMP},
	{"surfacetype", STOFS(surfaceType), F_INT, FFL_SPAWNTEMP},
	{"noweapons", STOFS(noweapons), F_INT, FFL_SPAWNTEMP},
	{"setweapons", STOFS(setweapons), F_INT, FFL_SPAWNTEMP},
	{"maxDeadHostages", STOFS(maxDeadHostages), F_INT, FFL_SPAWNTEMP},
	{"ambientSet", STOFS(ambientSet), F_STRING, FFL_SPAWNTEMP},
	{"startmusic", STOFS(startmusic), F_INT, FFL_SPAWNTEMP},
	{"forceHUD", STOFS(forceHUD), F_INT, FFL_SPAWNTEMP},
	{"weaponsAvailable", STOFS(weaponsAvailable), F_INT, FFL_SPAWNTEMP},
	{"ainame", STOFS(ai_name), F_STRING, FFL_SPAWNTEMP},
	
	// BSP dummy options
	{"distcull", STOFS(dummy), F_INT, FFL_SPAWNTEMP},
	{"chopsky", STOFS(dummy), F_INT, FFL_SPAWNTEMP},

	{NULL, 0, F_INT}
};

typedef spawn_t *spawnPtr;

spawnPtr spawnLists[] =
{
	environSpawns,
	funcSpawns,
	triggerSpawns,
	targetSpawns,
	lightSpawns,
	bosniaSpawns,
	castleSpawns,
	iraqSpawns,
	genericSpawns,
	newyorkSpawns,
	siberiaSpawns,
	tokyoSpawns,
	ugandaSpawns,
	dmSpawns,
	spawns,
	NULL
};

#if TIME_ENTS

void ClearTimings(bool DoCount)
{
	spawnPtr		*table;
	spawn_t			*s;

	for(table = spawnLists; *table; table++)
	{
		for (s=*table; s->name ; s++)
		{
			s->time_used = 0;
			if (DoCount)
			{
				s->count = 0;
			}
		}
	}
}

void PrintTimings(void)
{
	spawnPtr		*table;
	spawn_t			*s;
	char			temp[1024];
	unsigned long total=0;
	multimap<long,pair<char *,int> > sortit;

	for(table = spawnLists; *table; table++)
	{
		for (s=*table; s->name ; s++)
		{
			if (s->time_used)
			{
				sortit.insert(pair<long,pair<char *,int> >(-s->time_used,pair<char *,int>(s->name, s->count)));
				total+=s->time_used;
			}
		}
	}
	if (total)
	{
		OutputDebugString("Entity Timing Report\n");
		OutputDebugString("================================\n");
		multimap<long,pair<char *,int> >::iterator i;
		for (i=sortit.begin();i!=sortit.end();i++)
		{
			float p=100.0f*float(-(*i).first)/float(total);
			sprintf(temp,"%4.1f\t%3d\t%s\n",p,(*i).second.second,(*i).second.first);
			OutputDebugString(temp);
		}
		OutputDebugString("================================\n");
	}
}

#endif 
 
/*
===============
ED_CallSpawn

Finds the spawn function for the entity and calls it
===============
*/
void ED_CallSpawn (edict_t *ent)
{
	spawnPtr		*table;
	spawn_t			*s;
	Pickup			*pickup = NULL;

	if (!ent->classname)
	{
		gi.dprintf ("ED_CallSpawn: NULL classname\n");
		return;
	}

	if (ent->classname[0] == '_')
	{
		G_FreeEdict(ent);
		return;
	}

	// Don't spawn fluff objects.
	if (gl_pictip->value)
	{
		if ((strncmp (ent->classname, "misc_", 5)==0) && (ent->spawnflags & SF_FLUFF))
		{
			G_FreeEdict(ent);
			return;
		}
	}

	// Check item spawn functions.
	if (pickup = thePickupList.GetPickupFromEdict(ent))
	{
		if (dm->checkItemSpawn(ent, &pickup))
		{
			I_Spawn(ent, pickup);
		}
		return;
	}

	// Check normal spawn functions.
	for(table = spawnLists; *table; table++)
	{
		for (s=*table; s->name ; s++)
		{
			if (!strcmp(s->name, ent->classname))
			{	
				// Found it.
				s->spawn (ent);
#if TIME_ENTS
				ent->owner_spawn = s;
				s->count++;
#endif	
				return;
			}
		}
	}

	// No spawn func == no exist.
	gi.dprintf ("%s doesn't have a spawn function\n", ent->classname);
	G_FreeEdict(ent);
}

/*
=============
ED_NewString
=============
*/
char *ED_NewString (char *string)
{
	char	*newb, *new_p;
	int		i,l;
	
	l = strlen(string) + 1;

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

	new_p = newb;

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

/*
===============
ED_ParseField

Takes a key/value pair and sets the binary values
in an edict
===============
*/
void ED_ParseField (char *key, char *value, edict_t *ent)
{
	field_t	*f;
	byte	*b;
	float	v;
	vec3_t	vec;

	for (f=fields ; f->name ; f++)
	{
		if (!stricmp(f->name, key))
		{	// found it
			if (f->flags & FFL_SPAWNTEMP)
				b = (byte *)&st;
			else
				b = (byte *)ent;

			switch (f->type)
			{
			case F_STRING:
				*(char **)(b+f->ofs) = ED_NewString (value);
				break;
			case F_VECTOR:
				sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]);
				((float *)(b+f->ofs))[0] = vec[0];
				((float *)(b+f->ofs))[1] = vec[1];
				((float *)(b+f->ofs))[2] = vec[2];
				break;
			case F_INT:
				*(int *)(b+f->ofs) = atoi(value);
				break;
			case F_SHORT:
				*(short *)(b+f->ofs) = (short)atoi(value);
				break;
			case F_FLOAT:
				*(float *)(b+f->ofs) = atof(value);
				break;
			case F_ANGLEHACK:
				v = atof(value);
				((float *)(b+f->ofs))[0] = 0;
				((float *)(b+f->ofs))[1] = v;
				((float *)(b+f->ofs))[2] = 0;
				break;
			case F_IGNORE:
				break;
			}
			return;
		}
	}
	gi.dprintf ("%s is not a field\n", key);
}

/*
====================
ED_ParseEdict

Parses an edict out of the given string, returning the new position
ed should be a properly initialized empty edict.
====================
*/
char *ED_ParseEdict (char *data, edict_t *ent)
{
	qboolean	init;
	char		keyname[256];
	char		*com_token;

	init = false;
	memset (&st, 0, sizeof(st));

// go through all the dictionary pairs
	while (1)
	{	
	// parse key
		com_token = COM_Parse (&data);
		if (com_token[0] == '}')
			break;
		if (!data)
			gi.error ("ED_ParseEntity: EOF without closing brace");

		strncpy (keyname, com_token, sizeof(keyname)-1);
		
	// parse value	
		com_token = COM_Parse (&data);
		if (!data)
			gi.error ("ED_ParseEntity: EOF without closing brace");

		if (com_token[0] == '}')
			gi.error ("ED_ParseEntity: closing brace without data");

		init = true;	

	// keynames with a leading underscore are used for utility comments,
	// and are immediately discarded by quake
		if (keyname[0] == '_')
			continue;

		ED_ParseField (keyname, com_token, ent);
	}

	if (!init)
		memset (ent, 0, sizeof(*ent));

	return data;
}


/*
================
G_FindTeams

Chain together all entities with a matching team field.

All but the first will have the FL_TEAMSLAVE flag set.
All but the last will have the teamchain field set to the next one
================
*/
void G_FindTeams (void)
{
	edict_t	*e, *e2, *chain;
	int		i, j;
	int		c, c2;

	c = 0;
	c2 = 0;
	for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
	{
		if (!e->inuse)
			continue;
		if (!e->team)
			continue;
		if (e->flags & FL_TEAMSLAVE)
			continue;
		chain = e;
		e->teammaster = e;
		c++;
		c2++;
		for (j=i+1, e2=e+1 ; j < globals.num_edicts ; j++,e2++)
		{
			if (!e2->inuse)
				continue;
			if (!e2->team)
				continue;
			if (e2->flags & FL_TEAMSLAVE)
				continue;
			if (!strcmp(e->team, e2->team))
			{
				c2++;
				chain->teamchain = e2;
				e2->teammaster = e;
				chain = e2;
				e2->flags |= FL_TEAMSLAVE;
			}
		}
	}

	gi.dprintf ("%i teams with %i entities\n", c, c2);
}

void HackPrecacheLevelSounds(void);


void RegisterCommonStringPackets(void)
{
	gi.SP_Register("items");
	gi.SP_Register("weapons");
	gi.SP_Register("general");
}


/*
==============
SpawnEntities

Creates a server's entity / program execution context by
parsing textual entity definitions out of an ent file.
==============
*/

edict_t*	WorldSpawnScriptRunner;
extern float walldamagetime;		// rjr: yeah, yucky extern here, but reserving real words for what I feel...

void SpawnEntities (char *mapname, char *entities, char *spawnpoint)
{
	edict_t		*ent;
	int			inhibit;
	char		*com_token;
	int			i;

	WorldSpawnScriptRunner = NULL;
	
	PreserveClientData ();

	ShutdownScripts();
	gi.FreeTags (TAG_LEVEL);

	memset (&level, 0, sizeof(level));
	G_FreeAllEdicts();

	gmonster.Init();

	strncpy (level.mapname, mapname, sizeof(level.mapname)-1);
	strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1);

	// set client fields on player ents
	for (i=0 ; i<game.maxclients ; i++)
	{
		g_edicts[i+1].health = 100;
		g_edicts[i+1].client = game.clients + i;
	}

	ent = NULL;
	inhibit = 0;
	game.cinematicfreeze = 0;

	gi.Update(0.05f, true);
	//cache all the ghoul stuff we're going to need for this level
	game_ghoul.LevelPrecache();

	gi.Update(0.075f, true);
	W_InitWorldWeaponModels();
	
	// init list of client sfx and debris models needed for entities
	InitEntSoundCaching();

	InitEntMetalSoundCaching(); // this is necessary because the routine that is supposed to read in textures isn't 
								// properly reading them in on ladders.  Since I doubt that routine is going to be 
								// fixed (which is the proper solution), I poop this here.

	InitEntDebrisCaching();

	FX_Init();
	InitDeathmatchSystem();

	gi.Update(0.1f, true);
	RegisterCommonStringPackets();

	aiPoints.load();

	gi.Update(0.15f, true);
	PB_PrecacheAllPlayerModels();

	// 12/27/99 kef -- just need to call this sometime before we start making calls to ED_CallSpawn. it inits
	//our global list of pickup items. see CWeaponInfo.h.
	thePickupList.PreSpawnInit();

#if TIME_ENTS
	ClearTimings(true);
#endif	// _DEBUG

	fs_createpak = gi.cvar("fs_createpak", "0", 0);
	if (fs_createpak && fs_createpak->value)
	{
		ai_loadAllBolts=1;
	}
// parse ents
	while (1)
	{
		// parse the opening brace	
		com_token = COM_Parse (&entities);
		if (!entities)
			break;
		if (com_token[0] != '{')
			gi.error ("ED_LoadFromFile: found %s when expecting {",com_token);

		if (!ent)
			ent = g_edicts;
		else
			ent = G_Spawn ();
		entities = ED_ParseEdict (entities, ent);

		// remove things (except the world) from different skill levels or deathmatch
		if (ent != g_edicts)
		{
			if (dm->isDM())
			{
				if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH )
				{
					G_FreeEdict (ent);	
					inhibit++;
					continue;
				}
			}

			else
			{

				if ((((game.playerSkills.getEnemyValue() == 0) || (game.playerSkills.getEnemyValue() == 1)) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
					(((game.playerSkills.getEnemyValue() == 2)) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
					(((game.playerSkills.getEnemyValue() == 3) || (game.playerSkills.getEnemyValue() == 4)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)))
				{
						G_FreeEdict (ent);	
						inhibit++;
						continue;
				}
				// 12/27/99 kef -- here is where we should remove fluff objects...currently done in ED_CallSpawn, I think
			}


			ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_DEATHMATCH);
//			ent->spawnflags &= ~(SPAWNFLAG_NOT_DEATHMATCH);
		}

		// put skill level item check here -- dk wubba

		ED_CallSpawn (ent);
		// cache necessary debris models based on ent's material
		CacheEntDebrisAndSounds(ent);
	}
	
	// 1/31/00 kef -- just need to call this sometime after spawning all pickups. see CWeaponInfo.h.
	thePickupList.PostSpawnInit();


	ai_loadAllBolts=0;

	// Gametype specific initialisation.

	dm->levelInit();

	gi.Update(0.25f, true);
	CacheAllDebris();
	CacheAllSounds();
	CacheAllWallEffects();

	gi.dprintf ("%i entities inhibited\n", inhibit);

	G_FindTeams ();

	PlayerTrail_Init ();

	gi.Update(0.28f, true);
	SndInitWorldSounds();

	CalculatePlayerEntryWeapons(&level.weaponsAvailable);

	gi.Update(0.30f, true);
	
	// Clear out all the (now invalid) pointers to the weapon ghoul objects.
	pe->UncacheViewWeaponModels();

	// level.weaponsAvailable - this will take care of everything enemies are 
	// carrying / items lying around - players come later.
	pe->PrecacheViewWeaponModels(level.weaponsAvailable);

	gi.CreateGhoulConfigStrings();
	gi.Update(0.33f, true);

	walldamagetime = -999999.0;
}


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

void PrecacheSurfaceTypes(void)
{
	byte List[256], count;

	count = gi.SurfaceTypeList(List, sizeof(List));

	while(count)
	{
		count--;

		switch(List[count])
		{
		case SURF_DEFAULT:
			break;
		case SURF_METAL:
			break;
		case SURF_SAND_YELLOW:
			break;
		case SURF_SAND_WHITE:
			break;
		case SURF_SAND_LBROWN:
			break;
		case SURF_SAND_DBROWN:
			break;

		case SURF_GRAVEL_GREY:
			break;
		case SURF_GRAVEL_DBROWN:
			break;
		case SURF_GRAVEL_LBROWN:
			break;

		case SURF_SNOW:
			break;

		case SURF_LIQUID_BLUE:
			break;
		case SURF_LIQUID_GREEN:
			break;
		case SURF_LIQUID_ORANGE:
			break;
		case SURF_LIQUID_BROWN:
			break;

		case SURF_WOOD_LBROWN:
			break;
		case SURF_WOOD_DBROWN:
			break;
		case SURF_WOOD_LGREY:
			break;

		case SURF_STONE_LGREY:			
			break;
		case SURF_STONE_DGREY:			
			break;
		case SURF_STONE_LBROWN:			
			break;
		case SURF_STONE_DBROWN:			
			break;
		case SURF_STONE_WHITE:			
			break;
		case SURF_STONE_GREEN:			
			break;
		case SURF_STONE_RED:			
			break;
		case SURF_STONE_BLACK:			
			break;

		case SURF_GRASS_GREEN:
			break;
		case SURF_GRASS_BROWN:
			break;

		case SURF_LIQUID_RED:
			break;

		case SURF_METAL_STEAM:
			break;
		case SURF_METAL_WATERJET:
			break;
		case SURF_METAL_OILSPURT:
			break;
		case SURF_METAL_CHEMSPURT:
			break;
		case SURF_METAL_COMPUTERS:
			break;

		case SURF_SNOW_LBROWN:
			break;
		case SURF_SNOW_GREY:
			break;
		case SURF_BLOOD:
			break;
		case SURF_LIQUID_BLACK:
			break;
		case SURF_GLASS:
			break;
		case SURF_GLASS_COMPUTER:
			break;
		case SURF_SODAMACHINE:
			break;
		case SURF_PAPERWALL:
			break;
		case SURF_NEWSPAPER_DAMAGE:
			break;


		}
	}
}

void PrecacheSpawningEnemies(edict_t *ent)
{
	edict_t *newGuy;
	char *curSpawn;
	int oldLoadAllBolts=ai_loadAllBolts;

	ai_loadAllBolts = 1;

	for(int i = 0; i < 3; i++)
	{
		switch(i)
		{
		case 0:
			curSpawn = ent->spawn1;
			break;
		case 1:
			curSpawn = ent->spawn2;
			break;
		case 2:
			curSpawn = ent->spawn3;
			break;
		}

		if(!curSpawn)
		{
			continue;
		}

		if(!strcmp(curSpawn, ""))
		{
			continue;
		}

		newGuy = G_Spawn();

		newGuy->classname = curSpawn;
		ED_CallSpawn (newGuy);
		G_FreeEdict(newGuy);
	}
	ai_loadAllBolts = oldLoadAllBolts;
}


/*QUAKED worldspawn (0 0 0) ?

Only used for the world.
"sky"	environment map name
"skyaxis"	vector axis for rotating sky
"skyrotate"	speed of rotation in degrees/second
"sounds"	music cd track number
"gravity"	800 is default gravity
"message"	text to print at user logon
"musicset"		filename of the musicset for this level
"terrainset"	filename of the terrainset for this level
"distcull"      sets the maximum distance for vis (100 is good)
"chop"          sets the surface patch size for lighting
"chopsky"       sets the surface patch size for sky
"chopwarp"      sets the surface patch size for warp surfaces
"script"		filename of a script to run at map loadtime
"raffel"        set this to 1 if you want your map to run slower
"jersey"		set this to 1 to make all enemies talk funny
"screeneffect"	sets the style of general screen effect
"spawn1"
"spawn2"
"spawn3"        types of guys that will be spawned into the world from combat
"spawnfrequency"  0..1 (from no guys to very likely) - default is 0
"noweapons"		for levels like the armory where the player gets no weapons. set to 1 for no weapons.
"setweapons"		set the player's inventory. For TRN1 and TSR1 use 1. overridden by noweapons.
"maxDeadHostages"	number of dead hostages allowed before you fail this mission
"ambientset"		filename of the ambient set for this level
"startmusic"		set if you want to force a specific song at startup
"forceHUD"			set this for the tutorial level. draws HUD even with no weapon.
"weaponsAvailable"  magic number for precaching weapons. ask a programmer. (68 for tutorial level)
"ainame"            over-ride for per-level enemy set
*/

void SP_worldspawn (edict_t *ent)
{
	int		nWeapons = -1;

	ent->movetype = MOVETYPE_PUSH;
	ent->solid = SOLID_BSP;
	ent->inuse = true;			// since the world doesn't use G_Spawn()
	ent->s.modelindex = 1;		// world model is always index 1

	level.spawnFrequency = st.spawnfrequency;
	level.playerLoudness = 0;//defaults to this
	level.spawnRushTime = -999;
	level.spawnSoundTime = 0;
	level.lullTime = 0;
	level.lastSpawn = 0;
	level.weaponsAvailable = 0;
	level.lastEnemyKill = 0;
	memset(level.clearedNodes, 0, MAX_NODES);
	if (st.ai_name)
	{
		strcpy(level_ai_name, st.ai_name);
	}
	else
	{
		level_ai_name[0] = 0;
	}

	level.forceHUD = st.forceHUD;
	level.weaponsAvailable = st.weaponsAvailable;
	if (st.noweapons)
	{
		nWeapons = 0;
	}
	else if (st.setweapons)
	{
		nWeapons = st.setweapons;
	}
	if (nWeapons >= 0)
	{
		// oh	my	gosh	this	is	so	lame
		if (!ent->objInfo)
		{ // create a baseObjInfo_c for our edict
			baseObjInfo_c	*newInfo = new baseObjInfo_c(ent);
			
			// somewhere in the newInfo struct note the fact that this is a "no weapons" level
			if (ent->objInfo = newInfo)
			{
				objFlipInfo_c *pFlipInfo = (objFlipInfo_c*)newInfo->GetInfo(OIT_FLIP);
				switch(nWeapons)
				{
				case 0:
					pFlipInfo->radius = -1;		// tried to make this lamer. couldn't.
					break;
				case 1:
					pFlipInfo->radius = -2;
					break;
				default:
					break;
				}
			}
		}
	}

	if (st.maxDeadHostages)
	{	// how many hostages are allowed to die before you fail this mission
		level.maxDeadHostages = st.maxDeadHostages;
	}
	else
	{	// if there's no value here, save a good flag-value
		level.maxDeadHostages = -1;
	}

	// init the status of our countdown timer (if there isn't one in this level, who cares?)
	level.countdownEnded = 0;
	// just starting the mission...no status yet
	level.missionStatus = MISSION_NONE;

	//---------------

	// reserve some spots for dead player bodies for coop / deathmatch
	InitBodyQue ();

	// set configstrings for items
//	SetItemNames ();

	if (st.nextmap)
		strcpy (level.nextmap, st.nextmap);

	// make some data visible to the server

	if (ent->message && ent->message[0])
	{
		gi.configstring (CS_NAME, ent->message);
		strncpy (level.level_name, ent->message, sizeof(level.level_name));
	}
	else
		strncpy (level.level_name, level.mapname, sizeof(level.level_name));

	if (st.sky && st.sky[0])
		gi.configstring (CS_SKY, st.sky);
	else
		gi.configstring (CS_SKY, "none");

	if (st.terrainSet && st.terrainSet[0])
	{
		gi.configstring (CS_TERRAINNAME, va("%s", st.terrainSet));
	}

	gi.configstring (CS_SKYROTATE, va("%f", st.skyrotate) );

	gi.configstring (CS_SKYAXIS, va("%f %f %f",
		st.skyaxis[0], st.skyaxis[1], st.skyaxis[2]) );

	gi.configstring (CS_CDTRACK, va("%i", ent->sounds) );

	gi.configstring (CS_MAXCLIENTS, va("%i", (int)(maxclients->value) ) );

	gi.configstring (CS_SCREENEFFECT, va("%i", (int)(st.screeneffect) ) );

	if (st.musicSet && st.musicSet[0])
	{
		gi.configstring (CS_MUSICSET, va("%s", st.musicSet) );
	}

	// set ambient sound set name.
	// if its blank, then force it to "sounds/sound.amb"
	if (st.ambientSet && st.ambientSet[0])
	{
		gi.configstring (CS_AMBSET, va("%s", st.ambientSet));
	}
	else
	{
		gi.configstring (CS_AMBSET, "sound/sound.ams");
	}

	if(st.startmusic)
	{
		level.baseSong = st.startmusic;
	}
	else
	{
		level.baseSong = 0;
	}

	if(st.script)
	{
/*		edict_t	*runner;

		runner = 
		runner->classname = "script_runner";

		ED_CallSpawn(runner);

		runner->use(runner, runner, runner);
*/
	
		WorldSpawnScriptRunner = G_Spawn();
		WorldSpawnScriptRunner->classname = "script_runner";
		ED_CallSpawn(WorldSpawnScriptRunner);	
	}

	//---------------


	// help icon for statusbar
//	level.pic_health = gi.imageindex ("pics/status/i_health");
	level.alertedStatus = 0;

	//sniper scope images
	gi.imageindex ("pics/scope/arrows128_1");
	gi.imageindex ("pics/scope/arrows128_2");
	gi.imageindex ("pics/scope/arrows128_3");
	gi.imageindex ("pics/scope/arrows128_4");


	if (!st.gravity)
		gi.cvar_set("sv_gravity", "800");
	else
		gi.cvar_set("sv_gravity", st.gravity);

//
// Setup light animation tables. 'a' is total darkness, 'z' is doublebright.
//

	// 0 normal
	gi.configstring(CS_LIGHTS+0, "m");
	
	// 1 FLICKER (first variety)
	gi.configstring(CS_LIGHTS+1, "mmnmmommommnonmmonqnmmo");
	
	// 2 SLOW STRONG PULSE
	gi.configstring(CS_LIGHTS+2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb");
	
	// 3 CANDLE (first variety)
	gi.configstring(CS_LIGHTS+3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
	
	// 4 FAST STROBE
	gi.configstring(CS_LIGHTS+4, "mamamamamama");
	
	// 5 GENTLE PULSE 1
	gi.configstring(CS_LIGHTS+5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
	
	// 6 FLICKER (second variety)
	gi.configstring(CS_LIGHTS+6, "nmonqnmomnmomomno");
	
	// 7 CANDLE (second variety)
	gi.configstring(CS_LIGHTS+7, "mmmaaaabcdefgmmmmaaaammmaamm");
	
	// 8 CANDLE (third variety)
	gi.configstring(CS_LIGHTS+8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
	
	// 9 SLOW STROBE (fourth variety)
	gi.configstring(CS_LIGHTS+9, "aaaaaaaazzzzzzzz");
	
	// 10 FLUORESCENT FLICKER
	gi.configstring(CS_LIGHTS+10, "mmamammmmammamamaaamammma");

	// 11 SLOW PULSE NOT FADE TO BLACK
	gi.configstring(CS_LIGHTS+11, "abcdefghijklmnopqrrqponmlkjihgfedcba");

	// 12 FAST PULSE FOR JEREMY
	gi.configstring(CS_LIGHTS+12, "mkigegik");

	// 13 Special sky lightning on client - don't muck with this, por favor...
	gi.configstring(CS_LIGHTS+CL_WORLDSKYLIGHT, "ex");


	// styles 32-62 are assigned by the light program for switchable lights

	// 63 testing
	gi.configstring(CS_LIGHTS+63, "a");

	gi.cvar_set("timescale", "1.0");//in case a script was interrupted...

	// precache stuff - this should probably be handled better...
	PrecacheSurfaceTypes();

	PrecacheSpawningEnemies(ent);
}

void SetSkyColor(float red, float blue, float green)
{
	gi.configstring (CS_SKYCOLOR, va("%f %f %f", red, green, blue) );
}

void InitEntMetalSoundCaching(void)
{
	int i = 0;	

	entSoundsToCache[CLSFX_LAND_METAL] = 1;

	for (i = CLSFX_METALSTRIKE1; i <= CLSFX_METALSTRIKE3; i++)
	{
		entSoundsToCache[i] = 1;
	}

	for (i = 0; i < NUM_FOOTSTEPS_PER_GROUP; i++)
	{
		entSoundsToCache[CLSFX_FOOTSTEP_METAL + i] = 1;
	}
}

void InitEntSoundCaching(void)
{
	int i = 0, stepsnd = -1;
	byte List[256], count;

	memset(entSoundsToCache, 0, sizeof(byte)*NUM_CLSFX);

	// world geometry sounds (ricochets, wood splintering, etc)
	count = gi.SurfaceTypeList(List, sizeof(List));

	while(count)
	{
		count--;

		//	references to CLSFX_METALIMPACT have been commented out because we're forcing Metal sound Precaching anyway :/
		switch(List[count])
		{
		case SURF_DEFAULT:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_METAL:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_SAND_YELLOW:
			//CLSFX_FOOTSTEP_SAND,		CLSFX_SANDIMPACT,	CLSFX_LAND_SAND,
			stepsnd = CLSFX_FOOTSTEP_SAND;
			entSoundsToCache[CLSFX_SANDIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_SAND] = 1;
			break;
		case SURF_SAND_WHITE:
			//CLSFX_FOOTSTEP_SAND,		CLSFX_SANDIMPACT,	CLSFX_LAND_SAND,
			stepsnd = CLSFX_FOOTSTEP_SAND;
			entSoundsToCache[CLSFX_SANDIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_SAND] = 1;
			break;
		case SURF_SAND_LBROWN:
			//CLSFX_FOOTSTEP_SAND,		CLSFX_SANDIMPACT,	CLSFX_LAND_SAND,
			stepsnd = CLSFX_FOOTSTEP_SAND;
			entSoundsToCache[CLSFX_SANDIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_SAND] = 1;
			break;
		case SURF_SAND_DBROWN:
			//CLSFX_FOOTSTEP_SAND,		CLSFX_SANDIMPACT,	CLSFX_LAND_SAND,
			stepsnd = CLSFX_FOOTSTEP_SAND;
			entSoundsToCache[CLSFX_SANDIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_SAND] = 1;
			break;
		case SURF_GRAVEL_GREY:
			//CLSFX_FOOTSTEP_GRAVEL,	CLSFX_GRAVELIMPACT,	CLSFX_LAND_GRAVEL
			stepsnd = CLSFX_FOOTSTEP_GRAVEL;
			entSoundsToCache[CLSFX_GRAVELIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_GRAVEL] = 1;
			break;
		case SURF_GRAVEL_DBROWN:
			//CLSFX_FOOTSTEP_GRAVEL,	CLSFX_GRAVELIMPACT,	CLSFX_LAND_GRAVEL
			stepsnd = CLSFX_FOOTSTEP_GRAVEL;
			entSoundsToCache[CLSFX_GRAVELIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_GRAVEL] = 1;
			break;
		case SURF_GRAVEL_LBROWN:
			//CLSFX_FOOTSTEP_GRAVEL,	CLSFX_GRAVELIMPACT,	CLSFX_LAND_GRAVEL
			stepsnd = CLSFX_FOOTSTEP_GRAVEL;
			entSoundsToCache[CLSFX_GRAVELIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_GRAVEL] = 1;
			break;
		case SURF_SNOW:
			//CLSFX_FOOTSTEP_SNOW,		CLSFX_SNOWIMPACT,	CLSFX_LAND_SNOW,
			stepsnd = CLSFX_FOOTSTEP_SNOW;
			entSoundsToCache[CLSFX_SNOWIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_SNOW] = 1;
			break;
		case SURF_LIQUID_BLUE:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_LIQUID_GREEN:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_LIQUID_ORANGE:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_LIQUID_BROWN:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_WOOD_LBROWN:
			//CLSFX_FOOTSTEP_WOOD,		CLSFX_WOODIMPACT,	CLSFX_LAND_WOOD,
			stepsnd = CLSFX_FOOTSTEP_WOOD;
			entSoundsToCache[CLSFX_WOODIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WOOD] = 1;
			break;
		case SURF_WOOD_DBROWN:
			//CLSFX_FOOTSTEP_WOOD,		CLSFX_WOODIMPACT,	CLSFX_LAND_WOOD,
			stepsnd = CLSFX_FOOTSTEP_WOOD;
			entSoundsToCache[CLSFX_WOODIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WOOD] = 1;
			break;
		case SURF_WOOD_LGREY:
			//CLSFX_FOOTSTEP_WOOD,		CLSFX_WOODIMPACT,	CLSFX_LAND_WOOD,
			stepsnd = CLSFX_FOOTSTEP_WOOD;
			entSoundsToCache[CLSFX_WOODIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WOOD] = 1;
			break;
		case SURF_STONE_LGREY:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_DGREY:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_LBROWN:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_DBROWN:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_WHITE:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_GREEN:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_RED:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_STONE_BLACK:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_GRASS_GREEN:
			//CLSFX_FOOTSTEP_GRASS,	CLSFX_GRASSIMPACT,	CLSFX_LAND_GRASS,
			stepsnd = CLSFX_FOOTSTEP_GRASS;
			entSoundsToCache[CLSFX_GRASSIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_GRASS] = 1;
			break;
		case SURF_GRASS_BROWN:
			//CLSFX_FOOTSTEP_GRASS,	CLSFX_GRASSIMPACT,	CLSFX_LAND_GRASS,
			stepsnd = CLSFX_FOOTSTEP_GRASS;
			entSoundsToCache[CLSFX_GRASSIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_GRASS] = 1;
			break;
		case SURF_LIQUID_RED:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_METAL_STEAM:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_METAL_WATERJET:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_METAL_OILSPURT:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_METAL_CHEMSPURT:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_METAL_COMPUTERS:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			//CLSFX_SPARK1, CLSFX_EXP_SMALL
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			// if damaged, the surface requires certain sounds...
			entSoundsToCache[CLSFX_SPARK1] = 1;
			entSoundsToCache[CLSFX_SPARK2] = 1;
			entSoundsToCache[CLSFX_EXP_SMALL] = 1;
			break;
		case SURF_SNOW_LBROWN:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_SNOW_GREY:
			//CLSFX_FOOTSTEP_STONE,	CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_BLOOD:
			//CLSFX_STONEIMPACT,		CLSFX_STONEIMPACT,	CLSFX_LAND_STONE,
			stepsnd = CLSFX_FOOTSTEP_STONE;
			entSoundsToCache[CLSFX_STONEIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_STONE] = 1;
			break;
		case SURF_LIQUID_BLACK:
			//CLSFX_FOOTSTEP_WATER,	CLSFX_WATERIMPACT,	CLSFX_LAND_WATER,
			stepsnd = CLSFX_FOOTSTEP_WATER;
			entSoundsToCache[CLSFX_WATERIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WATER] = 1;
			break;
		case SURF_GLASS:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			//CLSFX_BREAK_GLASSSMALL
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			// if damaged, the surface requires a certain sound...
			entSoundsToCache[CLSFX_BREAK_GLASSSMALL] = 1;
			break;
		case SURF_GLASS_COMPUTER:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			//CLSFX_BREAK_GLASSSMALL
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			// if damaged, the surface requires a certain sound...
			entSoundsToCache[CLSFX_BREAK_GLASSSMALL] = 1;
			break;
		case SURF_SODAMACHINE:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		case SURF_PAPERWALL:
			//CLSFX_FOOTSTEP_WOOD,		CLSFX_WOODIMPACT,	CLSFX_LAND_WOOD,
			stepsnd = CLSFX_FOOTSTEP_WOOD;
			entSoundsToCache[CLSFX_WOODIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_WOOD] = 1;
			break;
		case SURF_NEWSPAPER_DAMAGE:
			//CLSFX_FOOTSTEP_METAL,	CLSFX_METALIMPACT,	CLSFX_LAND_METAL,
			stepsnd = CLSFX_FOOTSTEP_METAL;
//			entSoundsToCache[CLSFX_METALIMPACT] = 1;
			entSoundsToCache[CLSFX_LAND_METAL] = 1;
			break;
		}

		if (stepsnd >= 0)
		{
			for (i = 0; i < NUM_FOOTSTEPS_PER_GROUP; i++)
			{
				entSoundsToCache[stepsnd + i] = 1;
			}
		}
		stepsnd = -1;
	}

	entSoundsToCache[CLSFX_SPARK1] = 1;
	entSoundsToCache[CLSFX_SPARK2] = 1;

}

void InitEntDebrisCaching(void)
{
	int i = 0;

	memset(entDebrisToCache, 'n', sizeof(char)*NUM_CLGHL);

	// there may be some debris models that we'll always want to cache, but I
	//can't think of 'em right now

}

void CacheEntDebrisAndSounds(edict_t *ent)
{
	// cache the debris that this ent turns into when it breaks/dies/explodes
	switch(ent->material)
	{
	case MAT_BRICK_BROWN:
	case MAT_BRICK_LBROWN:
	case MAT_BRICK_LGREY:
	case MAT_BRICK_DGREY:
	case MAT_BRICK_RED:
		entDebrisToCache[CLGHL_CHUNKBRICK] = DEBRIS_YES;
		break;

	case MAT_GLASS:
		entDebrisToCache[CLGHL_CHUNKGLASSSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKGLASSMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKGLASSLRG] = DEBRIS_YES;
		break;

	case MAT_METAL_LGREY:
	case MAT_METAL_DGREY:
	case MAT_METAL_RUSTY:
	case MAT_METAL_SHINY:
		entDebrisToCache[CLGHL_CHUNKMETALSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKMETALMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKMETALLRG] = DEBRIS_YES;
		break;

	case MAT_ROCK_BROWN:
	case MAT_ROCK_LBROWN:
	case MAT_ROCK_DBROWN:
	case MAT_ROCK_LGREY:
	case MAT_ROCK_DGREY:
	case MAT_ROCK_FLESH:
		entDebrisToCache[CLGHL_CHUNKROCKSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKLRG] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKTINY] = DEBRIS_YES;
		break;

	case MAT_WOOD_LBROWN:
	case MAT_WOOD_DBROWN:
	case MAT_WOOD_DGREY:
	case MAT_WOOD_LGREY:
		entDebrisToCache[CLGHL_CHUNKWOODSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWOODMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWOODLRG] = DEBRIS_YES;
		break;

	case MAT_WALL_BLACK:
	case MAT_WALL_BROWN:
	case MAT_WALL_DARKBROWN:
	case MAT_WALL_LIGHTBROWN:
	case MAT_WALL_GREY:
	case MAT_WALL_DARKGREY:
	case MAT_WALL_LIGHTGREY:
	case MAT_WALL_GREEN:
	case MAT_WALL_ORANGE:
	case MAT_WALL_RED:
	case MAT_WALL_WHITE:
	case MAT_WALL_STRAW:
		entDebrisToCache[CLGHL_CHUNKWALLSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWALLMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWALLLRG] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWALLTINY] = DEBRIS_YES;
		break;
	case MAT_ROCK_SNOW:
		entDebrisToCache[CLGHL_CHUNKROCKSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKMED] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKLRG] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKROCKTINY] = DEBRIS_YES;
		break;
	}

// kef -- have to keep some of the precaching stuff out for now cuz it crashes final/nyc with
// an index overflow in the soundlist
#if 1
	// some surfaceTypes have various client fx associated with their death. these
	//fx tend to generate a lot of chunks. also, the sounds needed by the client function
	//HandleDebris() are precached here.
	switch(ent->surfaceType)
	{
	case SURF_DEFAULT:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_WOOD_LBROWN:
		// MakeDefaultWallEffect -- no chunks 
		entSoundsToCache[CLSFX_BREAK_WOOD] = 1;
		break;
	case SURF_WOOD_DBROWN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_WOOD] = 1;
		break;
	case SURF_WOOD_LGREY:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_WOOD] = 1;
		break;
	case SURF_STONE_LGREY:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_DGREY:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_LBROWN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_DBROWN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_WHITE:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_GREEN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_RED:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_STONE_BLACK:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GRASS_GREEN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GRASS_BROWN:
		// MakeDefaultWallEffect -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_METAL:
		// MakeSparks -- no chunks 
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		break;
	case SURF_SAND_YELLOW:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SAND_WHITE:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SAND_LBROWN:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SAND_DBROWN:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GRAVEL_GREY:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GRAVEL_DBROWN:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GRAVEL_LBROWN:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SNOW:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SNOW_LBROWN:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_SNOW_GREY:
		// MakeSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_BLUE:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_GREEN:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_ORANGE:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_BROWN:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_RED:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_LIQUID_BLACK:
		// MakeSplatWater -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_METAL_STEAM:
		// MakeShortSteam -- no chunks
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		break;
	case SURF_METAL_WATERJET:
		// MakeLiquidSpurt -- no chunks
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		break;
	case SURF_METAL_OILSPURT:
		// MakeLiquidSpurt -- no chunks
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		break;
	case SURF_METAL_CHEMSPURT:
		// MakeLiquidSpurt -- no chunks
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		break;
	case SURF_METAL_COMPUTERS:
		// MakeComputerDamage -- calls ThrowChunks with surfaceType SURF_STONE_DGREY
		//CLSFX_SPARK1, CLSFX_EXP_SMALL
		entDebrisToCache[CLGHL_CHUNKWALLSM] = DEBRIS_YES;
		entSoundsToCache[CLSFX_BREAK_METAL] = 1;
		entSoundsToCache[CLSFX_SPARK1] = 1;
		entSoundsToCache[CLSFX_EXP_SMALL] = 1;
		break;
	case SURF_BLOOD:
		// MakeBloodSplatter -- no chunks
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	case SURF_GLASS:
		// MakeSplatGlass -- throws some glass chunks
		//CLSFX_BREAK_GLASSSMALL,CLSFX_BREAK_GLASSMED,CLSFX_BREAK_GLASSBIG
		entDebrisToCache[CLGHL_CHUNKGLASSSM] = DEBRIS_YES;
		entSoundsToCache[CLSFX_BREAK_GLASSSMALL] = 1;
		entSoundsToCache[CLSFX_BREAK_GLASSMED] = 1;
		entSoundsToCache[CLSFX_BREAK_GLASSBIG] = 1;
		break;
	case SURF_GLASS_COMPUTER:
		// MakeComputerGlassDamage
		//CLSFX_BREAK_GLASSSMALL,CLSFX_BREAK_GLASSMED,CLSFX_BREAK_GLASSBIG
		entDebrisToCache[CLGHL_CHUNKGLASSSM] = DEBRIS_YES;
		entDebrisToCache[CLGHL_CHUNKWALLSM] = DEBRIS_YES;
		entSoundsToCache[CLSFX_BREAK_GLASSSMALL] = 1;
		entSoundsToCache[CLSFX_BREAK_GLASSMED] = 1;
		entSoundsToCache[CLSFX_BREAK_GLASSBIG] = 1;
		break;
	case SURF_SODAMACHINE:
		// MakeSodaDamage -- you guessed it...spews cans
		entDebrisToCache[CLGHL_CAN] = DEBRIS_YES;
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;

	case SURF_PAPERWALL:
		// MakePaperWallDamage
		//CLSFX_WOODIMPACT
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;

	case SURF_NEWSPAPER_DAMAGE:
		// MakeNewspaperDamage 
		entDebrisToCache[CLGHL_PAPER] = DEBRIS_YES;
		entSoundsToCache[CLSFX_BREAK_STONE] = 1;
		break;
	}
#endif
}

void CacheAllDebris(void)
{

	if (NUM_CLGHL > MAX_QPATH)	// Too many different types of debris
	{
		Com_Printf("ERROR: NUM_CLGHL > MAX_QPATH!\n");
		return;
	}

	gi.configstring(CS_DEBRISPRECACHE,entDebrisToCache);	// Set up string to send across 
}

void CacheAllSounds(void)
{
	int i = 0;

	// never know when we'll have water available, so make sure the 'I entered water' sound
	//is always precached
	entSoundsToCache[CLSFX_LAND_WATER] = 1;
	for (i = 0; i < NUM_CLSFX; i++)
	{
		if (entSoundsToCache[i])
		{
			gi.soundindex(cl_fxs[i].filename);
		}
	}
}

// this is less than cool
void CacheAllWallEffects(void)
{
	gi.effectindex("environ/waterspurt");
	gi.effectindex("environ/oilspurt");
	gi.effectindex("environ/chemspurt");
	gi.effectindex("environ/metal_computer");
	gi.effectindex("environ/glass_computer");
	gi.effectindex("environ/metal_steam");
	gi.effectindex("environ/bulletsplash");
	gi.effectindex("environ/splatgreen");
	gi.effectindex("environ/splatorange");
	gi.effectindex("environ/splatred");
	gi.effectindex("environ/splatbrown");
	gi.effectindex("environ/splatblack");
}

char *weaponList[] =
{	"nothing",
	"knife",
	"pistol2",
	"pistol1",
	"mpistol",
	"assault",
	"sniper",
	"slugger",
	"shotgun",
	"machinegun",
	"rocket",
	"mpg",
	"flamegun",
	0
};

void CalculatePlayerEntryWeapons(int *weaponsAvailable)
{
	char *curSpot;
	char *curTokSpot;
	char token[256];
	char temp[256];
	int  value;

	//assume curSpot is somewhere decent

	curSpot = gi.cvar_info(CVAR_WEAPON | CVAR_ITEM | CVAR_AMMO);

	if(!curSpot || (!strcmp(curSpot, "")))
	{	// if there is nothing, plug in the defaults
		return;
	}

	while(*curSpot)
	{
		curTokSpot = token;

		if(*curSpot == '\\')
		{
			curSpot++;
		}

		while(*curSpot && (*curSpot != '\\'))
		{
			*curTokSpot++ = *curSpot++;
		}
		*curTokSpot = 0;

		assert(*curSpot);

		if(*curSpot == '\\')
		{
			curSpot++;
		}

		curTokSpot = temp;
		while((*curSpot) && (*curSpot != '\\'))
		{
			*curTokSpot++ = *curSpot++;
		}
		*curTokSpot = 0;
		value = atoi(temp);

		// okay, we have the two tokens... let's do stuff with them now...


		char *testString;
		int curCheck;
		int found = 0;



		// look for weapons

		curCheck = 0;

		do
		{
			testString = weaponList[curCheck];

			if(!stricmp(token, testString))
			{	// hey, I have one of these!  Cool!
				if(value)
				{// add the weapon
					*weaponsAvailable |= (1<<curCheck);
				}
				found = 1;
				break;
			}

			curCheck++;
		}while(weaponList[curCheck]);

		if(found)
		{
			continue;
		}
	}

	return;
}
