//NPC_stats.cpp
#include "b_local.h"
#include "b_public.h"
#include "anims.h"
#include "../ghoul2/G2.h"

extern qboolean NPCsPrecached;

#include "../namespace_begin.h"
extern qboolean WP_SaberParseParms( const char *SaberName, saberInfo_t *saber );
extern void WP_RemoveSaber( saberInfo_t *sabers, int saberNum );
#include "../namespace_end.h"

stringID_table_t TeamTable[] =
{
	ENUM2STRING(NPCTEAM_FREE),			// caution, some code checks a team_t via "if (!team_t_varname)" so I guess this should stay as entry 0, great or what? -slc
	ENUM2STRING(NPCTEAM_PLAYER),
	ENUM2STRING(NPCTEAM_ENEMY),
	ENUM2STRING(NPCTEAM_NEUTRAL),	// most droids are team_neutral, there are some exceptions like Probe,Seeker,Interrogator
	"",	-1
};

// this list was made using the model directories, this MUST be in the same order as the CLASS_ enum in teams.h
stringID_table_t ClassTable[] =
{
	ENUM2STRING(CLASS_NONE),				// hopefully this will never be used by an npc), just covering all bases
	ENUM2STRING(CLASS_ATST),				// technically droid...
	ENUM2STRING(CLASS_BARTENDER),
	ENUM2STRING(CLASS_BESPIN_COP),		
	ENUM2STRING(CLASS_CLAW),
	ENUM2STRING(CLASS_COMMANDO),
	ENUM2STRING(CLASS_DESANN),			
	ENUM2STRING(CLASS_FISH),
	ENUM2STRING(CLASS_FLIER2),
	ENUM2STRING(CLASS_GALAK),
	ENUM2STRING(CLASS_GLIDER),
	ENUM2STRING(CLASS_GONK),				// droid
	ENUM2STRING(CLASS_GRAN),
	ENUM2STRING(CLASS_HOWLER),
//	ENUM2STRING(CLASS_RANCOR),
	ENUM2STRING(CLASS_IMPERIAL),
	ENUM2STRING(CLASS_IMPWORKER),
	ENUM2STRING(CLASS_INTERROGATOR),		// droid 
	ENUM2STRING(CLASS_JAN),				
	ENUM2STRING(CLASS_JEDI),				
	ENUM2STRING(CLASS_KYLE),
	ENUM2STRING(CLASS_LANDO),			
	ENUM2STRING(CLASS_LIZARD),
	ENUM2STRING(CLASS_LUKE),				
	ENUM2STRING(CLASS_MARK1),			// droid
	ENUM2STRING(CLASS_MARK2),			// droid
	ENUM2STRING(CLASS_GALAKMECH),		// droid
	ENUM2STRING(CLASS_MINEMONSTER),
	ENUM2STRING(CLASS_MONMOTHA),			
	ENUM2STRING(CLASS_MORGANKATARN),
	ENUM2STRING(CLASS_MOUSE),			// droid
	ENUM2STRING(CLASS_MURJJ),
	ENUM2STRING(CLASS_PRISONER),
	ENUM2STRING(CLASS_PROBE),			// droid
	ENUM2STRING(CLASS_PROTOCOL),			// droid
	ENUM2STRING(CLASS_R2D2),				// droid
	ENUM2STRING(CLASS_R5D2),				// droid
	ENUM2STRING(CLASS_REBEL),
	ENUM2STRING(CLASS_REBORN),
	ENUM2STRING(CLASS_REELO),
	ENUM2STRING(CLASS_REMOTE),
	ENUM2STRING(CLASS_RODIAN),
	ENUM2STRING(CLASS_SEEKER),			// droid
	ENUM2STRING(CLASS_SENTRY),
	ENUM2STRING(CLASS_SHADOWTROOPER),
	ENUM2STRING(CLASS_STORMTROOPER),
	ENUM2STRING(CLASS_SWAMP),
	ENUM2STRING(CLASS_SWAMPTROOPER),
	ENUM2STRING(CLASS_TAVION),
	ENUM2STRING(CLASS_TRANDOSHAN),
	ENUM2STRING(CLASS_UGNAUGHT),
	ENUM2STRING(CLASS_JAWA),
	ENUM2STRING(CLASS_WEEQUAY),
	ENUM2STRING(CLASS_BOBAFETT),
	//ENUM2STRING(CLASS_ROCKETTROOPER),
	//ENUM2STRING(CLASS_PLAYER),
	ENUM2STRING(CLASS_VEHICLE),
	ENUM2STRING(CLASS_RANCOR),
	ENUM2STRING(CLASS_WAMPA),
	"",	-1
};

stringID_table_t BSTable[] =
{
	ENUM2STRING(BS_DEFAULT),//# default behavior for that NPC
	ENUM2STRING(BS_ADVANCE_FIGHT),//# Advance to captureGoal and shoot enemies if you can
	ENUM2STRING(BS_SLEEP),//# Play awake script when startled by sound
	ENUM2STRING(BS_FOLLOW_LEADER),//# Follow your leader and shoot any enemies you come across
	ENUM2STRING(BS_JUMP),//# Face navgoal and jump to it.
	ENUM2STRING(BS_SEARCH),//# Using current waypoint as a base), search the immediate branches of waypoints for enemies
	ENUM2STRING(BS_WANDER),//# Wander down random waypoint paths
	ENUM2STRING(BS_NOCLIP),//# Moves through walls), etc.
	ENUM2STRING(BS_REMOVE),//# Waits for player to leave PVS then removes itself
	ENUM2STRING(BS_CINEMATIC),//# Does nothing but face it's angles and move to a goal if it has one
	//the rest are internal only
	"",				-1,
};

#define stringIDExpand(str, strEnum)	str, strEnum, ENUM2STRING(strEnum)

stringID_table_t BSETTable[] =
{
	ENUM2STRING(BSET_SPAWN),//# script to use when first spawned
	ENUM2STRING(BSET_USE),//# script to use when used
	ENUM2STRING(BSET_AWAKE),//# script to use when awoken/startled
	ENUM2STRING(BSET_ANGER),//# script to use when aquire an enemy
	ENUM2STRING(BSET_ATTACK),//# script to run when you attack
	ENUM2STRING(BSET_VICTORY),//# script to run when you kill someone
	ENUM2STRING(BSET_LOSTENEMY),//# script to run when you can't find your enemy
	ENUM2STRING(BSET_PAIN),//# script to use when take pain
	ENUM2STRING(BSET_FLEE),//# script to use when take pain below 50% of health
	ENUM2STRING(BSET_DEATH),//# script to use when killed
	ENUM2STRING(BSET_DELAYED),//# script to run when self->delayScriptTime is reached
	ENUM2STRING(BSET_BLOCKED),//# script to run when blocked by a friendly NPC or player
	ENUM2STRING(BSET_BUMPED),//# script to run when bumped into a friendly NPC or player (can set bumpRadius)
	ENUM2STRING(BSET_STUCK),//# script to run when blocked by a wall
	ENUM2STRING(BSET_FFIRE),//# script to run when player shoots their own teammates
	ENUM2STRING(BSET_FFDEATH),//# script to run when player kills a teammate
	stringIDExpand("", BSET_INVALID),
	"",				-1,
};

#include "../namespace_begin.h"
extern stringID_table_t WPTable[];
extern stringID_table_t FPTable[];
#include "../namespace_end.h"

char	*TeamNames[TEAM_NUM_TEAMS] = 
{
	"",
//	"starfleet",
//	"borg",
//	"parasite",
//	"scavengers",
//	"klingon",
//	"malon",
//	"hirogen",
//	"imperial",
//	"stasis",
//	"species8472",
//	"dreadnought",
//	"forge",
//	"disguise",
//	"player (not valid)"
	"player",
	"enemy",
	"neutral"
};

// this list was made using the model directories, this MUST be in the same order as the CLASS_ enum in teams.h
char	*ClassNames[CLASS_NUM_CLASSES] = 
{
	"",				// class none
	"atst",
	"bartender",
	"bespin_cop",
	"claw",
	"commando",
	"desann",
	"fish",
	"flier2",
	"galak",
	"glider",
	"gonk",			
	"gran",
	"howler",
	"imperial",
	"impworker",
	"interrogator",
	"jan",
	"jedi",
	"kyle",
	"lando",
	"lizard",
	"luke",
	"mark1",
	"mark2",
	"galak_mech",
	"minemonster",
	"monmotha",
	"morgankatarn",
	"mouse",
	"murjj",
	"prisoner",
	"probe",
	"protocol",
	"r2d2",
	"r5d2",
	"rebel",
	"reborn",
	"reelo",
	"remote",
	"rodian",
	"seeker",
	"sentry",
	"shadowtrooper",
	"stormtrooper",
	"swamp",
	"swamptrooper",
	"tavion",
	"trandoshan",
	"ugnaught",
	"weequay",
	"bobafett",
	"vehicle",
	"rancor",
	"wampa",
};


/*
NPC_ReactionTime
*/
//FIXME use grandom in here
int NPC_ReactionTime ( void ) 
{
	return 200 * ( 6 - NPCInfo->stats.reactions );
}

//
// parse support routines
//

#include "../namespace_begin.h"
extern qboolean BG_ParseLiteral( const char **data, const char *string );
#include "../namespace_end.h"

//
// NPC parameters file : scripts/NPCs.cfg
//
#define MAX_NPC_DATA_SIZE 0x20000
char	NPCParms[MAX_NPC_DATA_SIZE];
char	NPCFile[MAX_QPATH];

/*
team_t TranslateTeamName( const char *name ) 
{
	int n;

	for ( n = (NPCTEAM_FREE + 1); n < NPCTEAM_NUM_TEAMS; n++ )
	{
		if ( Q_stricmp( TeamNames[n], name ) == 0 )
		{
			return ((team_t) n);
		}
	}
	
	return NPCTEAM_FREE;
}

class_t TranslateClassName( const char *name ) 
{
	int n;

	for ( n = (CLASS_NONE + 1); n < CLASS_NUM_CLASSES; n++ )
	{
		if ( Q_stricmp( ClassNames[n], name ) == 0 )
		{
			return ((class_t) n);
		}
	}
	
	return CLASS_NONE;  // I hope this never happens, maybe print a warning
}
*/

/*
static race_t TranslateRaceName( const char *name ) 
{
	if ( !Q_stricmp( name, "human" ) ) 
	{
		return RACE_HUMAN;
	}
	return RACE_NONE;
}
*/
/*
static rank_t TranslateRankName( const char *name ) 

  Should be used to determine pip bolt-ons
*/
static rank_t TranslateRankName( const char *name ) 
{
	if ( !Q_stricmp( name, "civilian" ) ) 
	{
		return RANK_CIVILIAN;
	}

	if ( !Q_stricmp( name, "crewman" ) ) 
	{
		return RANK_CREWMAN;
	}
	
	if ( !Q_stricmp( name, "ensign" ) ) 
	{
		return RANK_ENSIGN;
	}
	
	if ( !Q_stricmp( name, "ltjg" ) ) 
	{
		return RANK_LT_JG;
	}
	
	if ( !Q_stricmp( name, "lt" ) ) 
	{
		return RANK_LT;
	}
	
	if ( !Q_stricmp( name, "ltcomm" ) ) 
	{
		return RANK_LT_COMM;
	}
	
	if ( !Q_stricmp( name, "commander" ) ) 
	{
		return RANK_COMMANDER;
	}
	
	if ( !Q_stricmp( name, "captain" ) ) 
	{
		return RANK_CAPTAIN;
	}

	return RANK_CIVILIAN;
}

#include "../namespace_begin.h"
extern saber_colors_t TranslateSaberColor( const char *name );
#include "../namespace_end.h"

/* static int MethodNameToNumber( const char *name ) {
	if ( !Q_stricmp( name, "EXPONENTIAL" ) ) {
		return METHOD_EXPONENTIAL;
	}
	if ( !Q_stricmp( name, "LINEAR" ) ) {
		return METHOD_LINEAR;
	}
	if ( !Q_stricmp( name, "LOGRITHMIC" ) ) {
		return METHOD_LOGRITHMIC;
	}
	if ( !Q_stricmp( name, "ALWAYS" ) ) {
		return METHOD_ALWAYS;
	}
	if ( !Q_stricmp( name, "NEVER" ) ) {
		return METHOD_NEVER;
	}
	return -1;
}

static int ItemNameToNumber( const char *name, int itemType ) {
//	int		n;

	for ( n = 0; n < bg_numItems; n++ ) {
		if ( bg_itemlist[n].type != itemType ) {
			continue;
		}
		if ( Q_stricmp( bg_itemlist[n].classname, name ) == 0 ) {
			return bg_itemlist[n].tag;
		}
	}
	return -1;
}
*/

//rwwFIXMEFIXME: movetypes
/*
static int MoveTypeNameToEnum( const char *name ) 
{
	if(!Q_stricmp("runjump", name))
	{
		return MT_RUNJUMP;
	}
	else if(!Q_stricmp("walk", name))
	{
		return MT_WALK;
	}
	else if(!Q_stricmp("flyswim", name))
	{
		return MT_FLYSWIM;
	}
	else if(!Q_stricmp("static", name))
	{
		return MT_STATIC;
	}

	return MT_STATIC;
}
*/

//#define CONVENIENT_ANIMATION_FILE_DEBUG_THING

#ifdef CONVENIENT_ANIMATION_FILE_DEBUG_THING
void SpewDebugStuffToFile(animation_t *anims)
{
	char BGPAFtext[40000];
	fileHandle_t f;
	int i = 0;

	trap_FS_FOpenFile("file_of_debug_stuff_SP.txt", &f, FS_WRITE);

	if (!f)
	{
		return;
	}

	BGPAFtext[0] = 0;

	while (i < MAX_ANIMATIONS)
	{
		strcat(BGPAFtext, va("%i %i\n", i, anims[i].frameLerp));
		i++;
	}

	trap_FS_Write(BGPAFtext, strlen(BGPAFtext), f);
	trap_FS_FCloseFile(f);
}
#endif

qboolean G_ParseAnimFileSet( const char *filename, const char *animCFG, int *animFileIndex )
{
	*animFileIndex = BG_ParseAnimationFile(filename, NULL, qfalse);
	//if it's humanoid we should have it cached and return it, if it is not it will be loaded (unless it's also cached already)

	if (*animFileIndex == -1)
	{
		return qfalse;
	}

	//I guess this isn't really even needed game-side.
	//BG_ParseAnimationSndFile(filename, *animFileIndex);
	return qtrue;
}

void NPC_PrecacheAnimationCFG( const char *NPC_type )
{
#if 0 //rwwFIXMEFIXME: Actually precache stuff here.
	char	filename[MAX_QPATH];
	const char	*token;
	const char	*value;
	const char	*p;
	int		junk;

	if ( !Q_stricmp( "random", NPC_type ) )
	{//sorry, can't precache a random just yet
		return;
	}

	p = NPCParms;
	COM_BeginParseSession(NPCFile);

	// look for the right NPC
	while ( p ) 
	{
		token = COM_ParseExt( &p, qtrue );
		if ( token[0] == 0 )
			return;

		if ( !Q_stricmp( token, NPC_type ) ) 
		{
			break;
		}

		SkipBracedSection( &p );
	}

	if ( !p ) 
	{
		return;
	}

	if ( BG_ParseLiteral( &p, "{" ) ) 
	{
		return;
	}

	// parse the NPC info block
	while ( 1 ) 
	{
		token = COM_ParseExt( &p, qtrue );
		if ( !token[0] ) 
		{
			Com_Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", NPC_type );
			return;
		}

		if ( !Q_stricmp( token, "}" ) ) 
		{
			break;
		}

		// legsmodel
		if ( !Q_stricmp( token, "legsmodel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			//must copy data out of this pointer into a different part of memory because the funcs we're about to call will call COM_ParseExt
			Q_strncpyz( filename, value, sizeof( filename ) );
			G_ParseAnimFileSet( filename, filename, &junk );
			return;
		}

		// playerModel
		if ( !Q_stricmp( token, "playerModel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			/*
			char	animName[MAX_QPATH];
			char	*GLAName;
			char	*slash = NULL;
			char	*strippedName;
			
			int handle = gi.G2API_PrecacheGhoul2Model( va( "models/players/%s/model.glm", value ) );
			if ( handle > 0 )//FIXME: isn't 0 a valid handle?
			{
				GLAName = gi.G2API_GetAnimFileNameIndex( handle );
				if ( GLAName )
				{
					Q_strncpyz( animName, GLAName, sizeof( animName ), qtrue );
					slash = strrchr( animName, '/' );
					if ( slash )
					{
						*slash = 0;
					}
					strippedName = COM_SkipPath( animName );

					//must copy data out of this pointer into a different part of memory because the funcs we're about to call will call COM_ParseExt
					Q_strncpyz( filename, value, sizeof( filename ), qtrue );
					G_ParseAnimFileSet( value, strippedName, &junk );//qfalse );
					//FIXME: still not precaching the animsounds.cfg?
					return;
				}
			}
			*/
			//rwwFIXMEFIXME: Do this properly.
		}
	}
#endif
}

extern int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type );
void NPC_PrecacheWeapons( team_t playerTeam, int spawnflags, char *NPCtype )
{
	int weapons = NPC_WeaponsForTeam( playerTeam, spawnflags, NPCtype );
	int curWeap;

	for ( curWeap = WP_SABER; curWeap < WP_NUM_WEAPONS; curWeap++ )
	{
		if (weapons & (1 << curWeap))
		{
			RegisterItem(BG_FindItemForWeapon((weapon_t)curWeap));
		}
	}

#if 0 //rwwFIXMEFIXME: actually precache weapons here
	int weapons = NPC_WeaponsForTeam( playerTeam, spawnflags, NPCtype );
	gitem_t	*item;
	for ( int curWeap = WP_SABER; curWeap < WP_NUM_WEAPONS; curWeap++ )
	{
		if ( (weapons & ( 1 << curWeap )) )
		{
			item = FindItemForWeapon( ((weapon_t)(curWeap)) );	//precache the weapon
			CG_RegisterItemSounds( (item-bg_itemlist) );
			CG_RegisterItemVisuals( (item-bg_itemlist) );
			//precache the in-hand/in-world ghoul2 weapon model

			char weaponModel[64];
			
			strcpy (weaponModel, weaponData[curWeap].weaponMdl);	
			if (char *spot = strstr(weaponModel, ".md3") ) {
				*spot = 0;
				spot = strstr(weaponModel, "_w");//i'm using the in view weapon array instead of scanning the item list, so put the _w back on
				if (!spot) {
					strcat (weaponModel, "_w");
				}
				strcat (weaponModel, ".glm");	//and change to ghoul2
			}
			gi.G2API_PrecacheGhoul2Model( weaponModel ); // correct way is item->world_model
		}
	}
#endif
}

/*
void NPC_Precache ( char *NPCName )

Precaches NPC skins, tgas and md3s.

*/
void NPC_Precache ( gentity_t *spawner )
{
	npcteam_t			playerTeam = NPCTEAM_FREE;
	const char	*token;
	const char	*value;
	const char	*p;
	char	*patch;
	char	sound[MAX_QPATH];
	qboolean	md3Model = qfalse;
	char	playerModel[MAX_QPATH];
	char	customSkin[MAX_QPATH];

	if ( !Q_stricmp( "random", spawner->NPC_type ) )
	{//sorry, can't precache a random just yet
		return;
	}
	strcpy(customSkin,"default");

	p = NPCParms;
	COM_BeginParseSession(NPCFile);

	// look for the right NPC
	while ( p ) 
	{
		token = COM_ParseExt( &p, qtrue );
		if ( token[0] == 0 )
			return;

		if ( !Q_stricmp( token, spawner->NPC_type ) ) 
		{
			break;
		}

		SkipBracedSection( &p );
	}

	if ( !p ) 
	{
		return;
	}

	if ( BG_ParseLiteral( &p, "{" ) ) 
	{
		return;
	}

	// parse the NPC info block
	while ( 1 ) 
	{
		token = COM_ParseExt( &p, qtrue );
		if ( !token[0] ) 
		{
			Com_Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", spawner->NPC_type );
			return;
		}

		if ( !Q_stricmp( token, "}" ) ) 
		{
			break;
		}

		// headmodel
		if ( !Q_stricmp( token, "headmodel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}

			if(!Q_stricmp("none", value))
			{
			}
			else
			{
				//Q_strncpyz( ri.headModelName, value, sizeof(ri.headModelName), qtrue);
			}
			md3Model = qtrue;
			continue;
		}
		
		// torsomodel
		if ( !Q_stricmp( token, "torsomodel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}

			if(!Q_stricmp("none", value))
			{
			}
			else
			{
				//Q_strncpyz( ri.torsoModelName, value, sizeof(ri.torsoModelName), qtrue);
			}
			md3Model = qtrue;
			continue;
		}

		// legsmodel
		if ( !Q_stricmp( token, "legsmodel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			//Q_strncpyz( ri.legsModelName, value, sizeof(ri.legsModelName), qtrue);			
			md3Model = qtrue;
			continue;
		}

		// playerModel
		if ( !Q_stricmp( token, "playerModel" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			Q_strncpyz( playerModel, value, sizeof(playerModel));			
			md3Model = qfalse;
			continue;
		}

		// customSkin
		if ( !Q_stricmp( token, "customSkin" ) ) 
		{
			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			Q_strncpyz( customSkin, value, sizeof(customSkin));			
			continue;
		}

		// playerTeam
		if ( !Q_stricmp( token, "playerTeam" ) ) 
		{
			char tk[4096]; //rww - hackilicious!

			if ( COM_ParseString( &p, &value ) ) 
			{
				continue;
			}
			//playerTeam = TranslateTeamName(value);
			Com_sprintf(tk, sizeof(tk), "NPC%s", token);
			playerTeam = (team_t)GetIDForString( TeamTable, tk );
			continue;
		}

	
		// snd
		if ( !Q_stricmp( token, "snd" ) ) {
			if ( COM_ParseString( &p, &value ) ) {
				continue;
			}
			if ( !(spawner->r.svFlags&SVF_NO_BASIC_SOUNDS) )
			{
				//FIXME: store this in some sound field or parse in the soundTable like the animTable...
				Q_strncpyz( sound, value, sizeof( sound ) );
				patch = strstr( sound, "/" );
				if ( patch ) 
				{
					*patch = 0;
				}
				spawner->s.csSounds_Std = G_SoundIndex( va("*$%s", sound) );
			}
			continue;
		}

		// sndcombat
		if ( !Q_stricmp( token, "sndcombat" ) ) {
			if ( COM_ParseString( &p, &value ) ) {
				continue;
			}
			if ( !(spawner->r.svFlags&SVF_NO_COMBAT_SOUNDS) )
			{
				//FIXME: store this in some sound field or parse in the soundTable like the animTable...
				Q_strncpyz( sound, value, sizeof( sound ) );
				patch = strstr( sound, "/" );
				if ( patch ) 
				{
					*patch = 0;
				}
				spawner->s.csSounds_Combat = G_SoundIndex( va("*$%s", sound) );
			}
			continue;
		}
		
		// sndextra
		if ( !Q_stricmp( token, "sndextra" ) ) {
			if ( COM_ParseString( &p, &value ) ) {
				continue;
			}
			if ( !(spawner->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
			{
				//FIXME: store this in some sound field or parse in the soundTable like the animTable...
				Q_strncpyz( sound, value, sizeof( sound ) );
				patch = strstr( sound, "/" );
				if ( patch ) 
				{
					*patch = 0;
				}
				spawner->s.csSounds_Extra = G_SoundIndex( va("*$%s", sound) );
			}
			continue;
		}

		// sndjedi
		if ( !Q_stricmp( token, "sndjedi" ) ) {
			if ( COM_ParseString( &p, &value ) ) {
				continue;
			}
			if ( !(spawner->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
			{
				//FIXME: store this in some sound field or parse in the soundTable like the animTable...
				Q_strncpyz( sound, value, sizeof( sound ) );
				patch = strstr( sound, "/" );
				if ( patch ) 
				{
					*patch = 0;
				}
				spawner->s.csSounds_Jedi = G_SoundIndex( va("*$%s", sound) );
			}
			continue;
		}

		if (!Q_stricmp(token, "weapon"))
		{
			int curWeap;

			if (COM_ParseString(&p, &value))
			{
				continue;
			}

			curWeap = GetIDForString( WPTable, value );

			if (curWeap > WP_NONE && curWeap < WP_NUM_WEAPONS)
			{
				RegisterItem(BG_FindItemForWeapon((weapon_t)curWeap));
			}
			continue;
		}
	}

	// If we're not a vehicle, then an error here would be valid...
	if ( !spawner->client || spawner->client->NPC_class != CLASS_VEHICLE )
	{
		if ( md3Model )
		{
			Com_Printf("MD3 model using NPCs are not supported in MP\n");
		}
		else
		{ //if we have a model/skin then index them so they'll be registered immediately
			//when the client gets a configstring update.
			char modelName[MAX_QPATH];

			Com_sprintf(modelName, sizeof(modelName), "models/players/%s/model.glm", playerModel);
			if (customSkin[0])
			{ //append it after a *
				strcat( modelName, va("*%s", customSkin) );
			}

			G_ModelIndex(modelName);
		}
	}

	//precache this NPC's possible weapons
	NPC_PrecacheWeapons( playerTeam, spawner->spawnflags, spawner->NPC_type );

//	CG_RegisterNPCCustomSounds( &ci );
//	CG_RegisterNPCEffects( playerTeam );
	//rwwFIXMEFIXME: same
	//FIXME: Look for a "sounds" directory and precache death, pain, alert sounds
}

#if 0
void NPC_BuildRandom( gentity_t *NPC )
{
	int	sex, color, head;

	sex = Q_irand(0, 2);
	color = Q_irand(0, 2);
	switch( sex )
	{
	case 0://female
		head = Q_irand(0, 2);
		switch( head )
		{
		default:
		case 0:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "garren", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 1:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "garren/salma", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 2:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "garren/mackey", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			color = Q_irand(3, 5);//torso needs to be afam
			break;
		}
		switch( color )
		{
		default:
		case 0:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale/gold", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 1:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 2:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale/blue", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 3:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale/aframG", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 4:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale/aframR", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 5:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewfemale/aframB", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		}
		Q_strncpyz( NPC->client->renderInfo.legsModelName, "crewfemale", sizeof(NPC->client->renderInfo.legsModelName), qtrue );
		break;
	default:
	case 1://male
	case 2://male
		head = Q_irand(0, 4);
		switch( head )
		{
		default:
		case 0:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "chakotay/nelson", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 1:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "paris/chase", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 2:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "doctor/pasty", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 3:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "kim/durk", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		case 4:
			Q_strncpyz( NPC->client->renderInfo.headModelName, "paris/kray", sizeof(NPC->client->renderInfo.headModelName), qtrue );
			break;
		}
		switch( color )
		{
		default:
		case 0:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewthin/red", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 1:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewthin", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
		case 2:
			Q_strncpyz( NPC->client->renderInfo.torsoModelName, "crewthin/blue", sizeof(NPC->client->renderInfo.torsoModelName), qtrue );
			break;
			//NOTE: 3 - 5 should be red, gold & blue, afram hands
		}
		Q_strncpyz( NPC->client->renderInfo.legsModelName, "crewthin", sizeof(NPC->client->renderInfo.legsModelName), qtrue );
		break;
	}

	NPC->s.modelScale[0] = NPC->s.modelScale[1] = NPC->s.modelScale[2] = Q_irand(87, 102)/100.0f;
//	NPC->client->race = RACE_HUMAN;
	NPC->NPC->rank = RANK_CREWMAN;
	NPC->client->playerTeam = NPC->s.teamowner = TEAM_PLAYER;
	NPC->client->clientInfo.customBasicSoundDir = "kyle";//FIXME: generic default?
}
#endif

extern void SetupGameGhoul2Model(gentity_t *ent, char *modelname, char *skinName);
qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) 
{
	const char	*token;
	const char	*value;
	const char	*p;
	int		n;
	float	f;
	char	*patch;
	char	sound[MAX_QPATH];
	char	playerModel[MAX_QPATH];
	char	customSkin[MAX_QPATH];
	renderInfo_t	*ri = &NPC->client->renderInfo;
	gNPCstats_t		*stats = NULL;
	qboolean	md3Model = qtrue;
	char	surfOff[1024];
	char	surfOn[1024];
	qboolean parsingPlayer = qfalse;
	vec3_t playerMins;
	vec3_t playerMaxs;
	int npcSaber1 = 0;
	int npcSaber2 = 0;

	VectorSet(playerMins, -15, -15, DEFAULT_MINS_2);
	VectorSet(playerMaxs, 15, 15, DEFAULT_MAXS_2);

	strcpy(customSkin,"default");
	if ( !NPCName || !NPCName[0]) 
	{
		NPCName = "Player";
	}

	if ( !NPC->s.number && NPC->client != NULL )
	{//player, only want certain data
		parsingPlayer = qtrue;
	}

	if ( NPC->NPC )
	{
		stats = &NPC->NPC->stats;
/*
	NPC->NPC->allWeaponOrder[0]	= WP_BRYAR_PISTOL;
	NPC->NPC->allWeaponOrder[1]	= WP_SABER;
	NPC->NPC->allWeaponOrder[2]	= WP_IMOD;
	NPC->NPC->allWeaponOrder[3]	= WP_SCAVENGER_RIFLE;
	NPC->NPC->allWeaponOrder[4]	= WP_TRICORDER;
	NPC->NPC->allWeaponOrder[6]	= WP_NONE;
	NPC->NPC->allWeaponOrder[6]	= WP_NONE;
	NPC->NPC->allWeaponOrder[7]	= WP_NONE;
*/
		// fill in defaults
		stats->aggression	= 3;
		stats->aim			= 3;
		stats->earshot		= 1024;
		stats->evasion		= 3;
		stats->hfov			= 90;
		stats->intelligence	= 3;
		stats->move			= 3;
		stats->reactions	= 3;
		stats->vfov			= 60;
		stats->vigilance	= 0.1f;
		stats->visrange		= 1024;

		stats->health		= 0;

		stats->yawSpeed		= 90;
		stats->walkSpeed	= 90;
		stats->runSpeed		= 300;
		stats->acceleration	= 15;//Increase/descrease speed this much per frame (20fps)
	}
	else
	{
		stats = NULL;
	}

	//Set defaults
	//FIXME: should probably put default torso and head models, but what about enemies
	//that don't have any- like Stasis?
	//Q_strncpyz( ri->headModelName,	DEFAULT_HEADMODEL,  sizeof(ri->headModelName),	qtrue);
	//Q_strncpyz( ri->torsoModelName, DEFAULT_TORSOMODEL, sizeof(ri->torsoModelName),	qtrue);
	//Q_strncpyz( ri->legsModelName,	DEFAULT_LEGSMODEL,  sizeof(ri->legsModelName),	qtrue);
	//FIXME: should we have one for weapon too?
	memset( (char *)surfOff, 0, sizeof(surfOff) );
	memset( (char *)surfOn, 0, sizeof(surfOn) );
	
	/*
	ri->headYawRangeLeft = 50;
	ri->headYawRangeRight = 50;
	ri->headPitchRangeUp = 40;
	ri->headPitchRangeDown = 50;
	ri->torsoYawRangeLeft = 60;
	ri->torsoYawRangeRight = 60;
	ri->torsoPitchRangeUp = 30;
	ri->torsoPitchRangeDown = 70;
	*/

	ri->headYawRangeLeft = 80;
	ri->headYawRangeRight = 80;
	ri->headPitchRangeUp = 45;
	ri->headPitchRangeDown = 45;
	ri->torsoYawRangeLeft = 60;
	ri->torsoYawRangeRight = 60;
	ri->torsoPitchRangeUp = 30;
	ri->torsoPitchRangeDown = 50;

	VectorCopy(playerMins, NPC->r.mins);
	VectorCopy(playerMaxs, NPC->r.maxs);
	NPC->client->ps.crouchheight = CROUCH_MAXS_2;
	NPC->client->ps.standheight = DEFAULT_MAXS_2;

	//rwwFIXMEFIXME: ...
	/*
	NPC->client->moveType		= MT_RUNJUMP;

	NPC->client->dismemberProbHead = 100;
	NPC->client->dismemberProbArms = 100;
	NPC->client->dismemberProbHands = 100;
	NPC->client->dismemberProbWaist = 100;
	NPC->client->dismemberProbLegs = 100;

	NPC->s.modelScale[0] = NPC->s.modelScale[1] = NPC->s.modelScale[2] = 1.0f;
	*/

	NPC->client->ps.customRGBA[0]=255;
	NPC->client->ps.customRGBA[1]=255;
	NPC->client->ps.customRGBA[2]=255;
	NPC->client->ps.customRGBA[3]=255;

	if ( !Q_stricmp( "random", NPCName ) )
	{//Randomly assemble a starfleet guy
		//NPC_BuildRandom( NPC );
		Com_Printf("RANDOM NPC NOT SUPPORTED IN MP\n");
		return qfalse;
	}
	else
	{
		int fp;

		p = NPCParms;
		COM_BeginParseSession(NPCFile);

		// look for the right NPC
		while ( p ) 
		{
			token = COM_ParseExt( &p, qtrue );
			if ( token[0] == 0 )
			{
				return qfalse;
			}

			if ( !Q_stricmp( token, NPCName ) ) 
			{
				break;
			}

			SkipBracedSection( &p );
		}
		if ( !p ) 
		{
			return qfalse;
		}

		if ( BG_ParseLiteral( &p, "{" ) ) 
		{
			return qfalse;
		}
			
		// parse the NPC info block
		while ( 1 ) 
		{
			token = COM_ParseExt( &p, qtrue );
			if ( !token[0] ) 
			{
				Com_Printf( S_COLOR_RED"ERROR: unexpected EOF while parsing '%s'\n", NPCName );
				return qfalse;
			}

			if ( !Q_stricmp( token, "}" ) ) 
			{
				break;
			}
	//===MODEL PROPERTIES===========================================================
			// custom color
			if ( !Q_stricmp( token, "customRGBA" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( !Q_stricmp( value, "random") )
				{
					NPC->client->ps.customRGBA[0]=Q_irand(0,255);
					NPC->client->ps.customRGBA[1]=Q_irand(0,255);
					NPC->client->ps.customRGBA[2]=Q_irand(0,255);
					NPC->client->ps.customRGBA[3]=255;
				} 
				else 
				{
					NPC->client->ps.customRGBA[0]=atoi(value);
					
					if ( COM_ParseInt( &p, &n ) ) 
					{
						continue;
					}
					NPC->client->ps.customRGBA[1]=n;
					
					if ( COM_ParseInt( &p, &n ) ) 
					{
						continue;
					}
					NPC->client->ps.customRGBA[2]=n;
					
					if ( COM_ParseInt( &p, &n ) ) 
					{
						continue;
					}
					NPC->client->ps.customRGBA[3]=n;
				}
				continue;
			}

			// headmodel
			if ( !Q_stricmp( token, "headmodel" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}

				if(!Q_stricmp("none", value))
				{
					//Zero the head clamp range so the torso & legs don't lag behind
					ri->headYawRangeLeft = 
					ri->headYawRangeRight = 
					ri->headPitchRangeUp = 
					ri->headPitchRangeDown = 0;
				}
				continue;
			}
			
			// torsomodel
			if ( !Q_stricmp( token, "torsomodel" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}

				if(!Q_stricmp("none", value))
				{
					//Zero the torso clamp range so the legs don't lag behind
					ri->torsoYawRangeLeft = 
					ri->torsoYawRangeRight = 
					ri->torsoPitchRangeUp = 
					ri->torsoPitchRangeDown = 0;
				}
				continue;
			}

			// legsmodel
			if ( !Q_stricmp( token, "legsmodel" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				/*
				Q_strncpyz( ri->legsModelName, value, sizeof(ri->legsModelName), qtrue);			
				//Need to do this here to get the right index
				G_ParseAnimFileSet( ri->legsModelName, ri->legsModelName, &ci->animFileIndex );
				*/
				continue;
			}

			// playerModel
			if ( !Q_stricmp( token, "playerModel" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				Q_strncpyz( playerModel, value, sizeof(playerModel));			
				md3Model = qfalse;
				continue;
			}
			
			// customSkin
			if ( !Q_stricmp( token, "customSkin" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				Q_strncpyz( customSkin, value, sizeof(customSkin));			
				continue;
			}

			// surfOff
			if ( !Q_stricmp( token, "surfOff" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( surfOff[0] )
				{
					Q_strcat( (char *)surfOff, sizeof(surfOff), "," );
					Q_strcat( (char *)surfOff, sizeof(surfOff), value );
				}
				else
				{
					Q_strncpyz( surfOff, value, sizeof(surfOff));
				}
				continue;
			}
			
			// surfOn
			if ( !Q_stricmp( token, "surfOn" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( surfOn[0] )
				{
					Q_strcat( (char *)surfOn, sizeof(surfOn), "," );
					Q_strcat( (char *)surfOn, sizeof(surfOn), value );
				}
				else
				{
					Q_strncpyz( surfOn, value, sizeof(surfOn));
				}
				continue;
			}
			
			//headYawRangeLeft
			if ( !Q_stricmp( token, "headYawRangeLeft" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->headYawRangeLeft = n;
				continue;
			}

			//headYawRangeRight
			if ( !Q_stricmp( token, "headYawRangeRight" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->headYawRangeRight = n;
				continue;
			}

			//headPitchRangeUp
			if ( !Q_stricmp( token, "headPitchRangeUp" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->headPitchRangeUp = n;
				continue;
			}
			
			//headPitchRangeDown
			if ( !Q_stricmp( token, "headPitchRangeDown" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->headPitchRangeDown = n;
				continue;
			}

			//torsoYawRangeLeft
			if ( !Q_stricmp( token, "torsoYawRangeLeft" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->torsoYawRangeLeft = n;
				continue;
			}

			//torsoYawRangeRight
			if ( !Q_stricmp( token, "torsoYawRangeRight" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->torsoYawRangeRight = n;
				continue;
			}

			//torsoPitchRangeUp
			if ( !Q_stricmp( token, "torsoPitchRangeUp" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->torsoPitchRangeUp = n;
				continue;
			}

			//torsoPitchRangeDown
			if ( !Q_stricmp( token, "torsoPitchRangeDown" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				ri->torsoPitchRangeDown = n;
				continue;
			}

			// Uniform XYZ scale
			if ( !Q_stricmp( token, "scale" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if (n != 100)
				{
					NPC->client->ps.iModelScale = n; //so the client knows
					if (n >= 1024)
					{
						Com_Printf("WARNING: MP does not support scaling up to or over 1024%\n");
						n = 1023;
					}

					NPC->modelScale[0] = NPC->modelScale[1] = NPC->modelScale[2] = n/100.0f;
				}
				continue;
			}

			//X scale
			if ( !Q_stricmp( token, "scaleX" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if (n != 100)
				{
					Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
					//NPC->s.modelScale[0] = n/100.0f;
				}
				continue;
			}

			//Y scale
			if ( !Q_stricmp( token, "scaleY" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if (n != 100)
				{
					Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
					//NPC->s.modelScale[1] = n/100.0f;
				}
				continue;
			}

			//Z scale
			if ( !Q_stricmp( token, "scaleZ" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if (n != 100)
				{
					Com_Printf("MP doesn't support xyz scaling, use 'scale'.\n");
				//	NPC->s.modelScale[2] = n/100.0f;
				}
				continue;
			}

	//===AI STATS=====================================================================
			if ( !parsingPlayer )
			{
				// aggression
				if ( !Q_stricmp( token, "aggression" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->aggression = n;
					}
					continue;
				}

				// aim
				if ( !Q_stricmp( token, "aim" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) {
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->aim = n;
					}
					continue;
				}

				// earshot
				if ( !Q_stricmp( token, "earshot" ) ) {
					if ( COM_ParseFloat( &p, &f ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( f < 0.0f ) 
					{
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->earshot = f;
					}
					continue;
				}

				// evasion
				if ( !Q_stricmp( token, "evasion" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) 
					{
						Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->evasion = n;
					}
					continue;
				}

				// hfov
				if ( !Q_stricmp( token, "hfov" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 30 || n > 180 ) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->hfov = n;// / 2;	//FIXME: Why was this being done?!
					}
					continue;
				}

				// intelligence
				if ( !Q_stricmp( token, "intelligence" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->intelligence = n;
					}
					continue;
				}
				
				// move
				if ( !Q_stricmp( token, "move" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->move = n;
					}
					continue;
				}

				// reactions
				if ( !Q_stricmp( token, "reactions" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 1 || n > 5 ) {
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->reactions = n;
					}
					continue;
				}

				// shootDistance
				if ( !Q_stricmp( token, "shootDistance" ) ) {
					if ( COM_ParseFloat( &p, &f ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( f < 0.0f ) 
					{
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->shootDistance = f;
					}
					continue;
				}

				// vfov
				if ( !Q_stricmp( token, "vfov" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 30 || n > 180 ) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->vfov = n / 2;
					}
					continue;
				}

				// vigilance
				if ( !Q_stricmp( token, "vigilance" ) ) {
					if ( COM_ParseFloat( &p, &f ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( f < 0.0f ) 
					{
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->vigilance = f;
					}
					continue;
				}

				// visrange
				if ( !Q_stricmp( token, "visrange" ) ) {
					if ( COM_ParseFloat( &p, &f ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( f < 0.0f ) 
					{
						Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->visrange = f;
					}
					continue;
				}

				// race
		//		if ( !Q_stricmp( token, "race" ) ) 
		//		{
		//			if ( COM_ParseString( &p, &value ) ) 
		//			{
		//				continue;
		//			}
		//			NPC->client->race = TranslateRaceName(value);
		//			continue;
		//		}

				// rank
				if ( !Q_stricmp( token, "rank" ) ) 
				{
					if ( COM_ParseString( &p, &value ) ) 
					{
						continue;
					}
					if ( NPC->NPC )
					{
						NPC->NPC->rank = TranslateRankName(value);
					}
					continue;
				}
			}

			// health
			if ( !Q_stricmp( token, "health" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
					stats->health = n;
				}
				else if ( parsingPlayer )
				{
					NPC->client->ps.stats[STAT_MAX_HEALTH] = NPC->client->pers.maxHealth = n;
				}
				continue;
			}

			// fullName
			if ( !Q_stricmp( token, "fullName" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				NPC->fullName = G_NewString(value);
				continue;
			}

			// playerTeam
			if ( !Q_stricmp( token, "playerTeam" ) ) 
			{
				char tk[4096]; //rww - hackilicious!

				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				Com_sprintf(tk, sizeof(tk), "NPC%s", token);
				NPC->client->playerTeam = NPC->s.teamowner = (team_t)GetIDForString( TeamTable, tk );//TranslateTeamName(value);
				continue;
			}

			// enemyTeam
			if ( !Q_stricmp( token, "enemyTeam" ) ) 
			{
				char tk[4096]; //rww - hackilicious!

				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				Com_sprintf(tk, sizeof(tk), "NPC%s", token);
				NPC->client->enemyTeam = (team_t)GetIDForString( TeamTable, tk );//TranslateTeamName(value);
				continue;
			}

			// class
			if ( !Q_stricmp( token, "class" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				NPC->client->NPC_class = (class_t)GetIDForString( ClassTable, value );
				NPC->s.NPC_class = NPC->client->NPC_class; //we actually only need this value now, but at the moment I don't feel like changing the 200+ references to client->NPC_class.

				// No md3's for vehicles.
				if ( NPC->client->NPC_class == CLASS_VEHICLE )
				{
					if ( !NPC->m_pVehicle )
					{//you didn't spawn this guy right!
						Com_Printf ( S_COLOR_RED "ERROR: Tried to spawn a vehicle NPC (%s) without using NPC_Vehicle or 'NPC spawn vehicle <vehiclename>'!!!  Bad, bad, bad!  Shame on you!\n", NPCName );
						return qfalse;
					}
					md3Model = qfalse;
				}

				continue;
			}

			// dismemberment probability for head
			if ( !Q_stricmp( token, "dismemberProbHead" ) ) {
				if ( COM_ParseInt( &p, &n ) ) {
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
				//	NPC->client->dismemberProbHead = n;
					//rwwFIXMEFIXME: support for this?
				}
				continue;
			}

			// dismemberment probability for arms
			if ( !Q_stricmp( token, "dismemberProbArms" ) ) {
				if ( COM_ParseInt( &p, &n ) ) {
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
				//	NPC->client->dismemberProbArms = n;
				}
				continue;
			}

			// dismemberment probability for hands
			if ( !Q_stricmp( token, "dismemberProbHands" ) ) {
				if ( COM_ParseInt( &p, &n ) ) {
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
				//	NPC->client->dismemberProbHands = n;
				}
				continue;
			}

			// dismemberment probability for waist
			if ( !Q_stricmp( token, "dismemberProbWaist" ) ) {
				if ( COM_ParseInt( &p, &n ) ) {
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
				//	NPC->client->dismemberProbWaist = n;
				}
				continue;
			}

			// dismemberment probability for legs
			if ( !Q_stricmp( token, "dismemberProbLegs" ) ) {
				if ( COM_ParseInt( &p, &n ) ) {
					SkipRestOfLine( &p );
					continue;
				}
				if ( n < 0 ) 
				{
					Com_Printf( "bad %s in NPC '%s'\n", token, NPCName );
					continue;
				}
				if ( NPC->NPC )
				{
				//	NPC->client->dismemberProbLegs = n;
				}
				continue;
			}

	//===MOVEMENT STATS============================================================
			
			if ( !Q_stricmp( token, "width" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					continue;
				}

				NPC->r.mins[0] = NPC->r.mins[1] = -n;
				NPC->r.maxs[0] = NPC->r.maxs[1] = n;
				continue;
			}

			if ( !Q_stricmp( token, "height" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					continue;
				}
				if ( NPC->client->NPC_class == CLASS_VEHICLE
					&& NPC->m_pVehicle
					&& NPC->m_pVehicle->m_pVehicleInfo
					&& NPC->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
				{//a flying vehicle's origin must be centered in bbox and it should spawn on the ground
					//trace_t		tr;
					//vec3_t		bottom;
					//float		adjust = 32.0f;
					NPC->r.maxs[2] = NPC->client->ps.standheight = (n/2.0f);
					NPC->r.mins[2] = -NPC->r.maxs[2];
					NPC->s.origin[2] += (DEFAULT_MINS_2-NPC->r.mins[2])+0.125f;
					VectorCopy(NPC->s.origin, NPC->client->ps.origin);
					VectorCopy(NPC->s.origin, NPC->r.currentOrigin);
					G_SetOrigin( NPC, NPC->s.origin );
					trap_LinkEntity(NPC);
					//now trace down
					/*
					VectorCopy( NPC->s.origin, bottom );
					bottom[2] -= adjust;
					trap_Trace( &tr, NPC->s.origin, NPC->r.mins, NPC->r.maxs, bottom, NPC->s.number, MASK_NPCSOLID );
					if ( !tr.allsolid && !tr.startsolid )
					{
						G_SetOrigin( NPC, tr.endpos );
						trap_LinkEntity(NPC);
					}
					*/
				}
				else
				{
					NPC->r.mins[2] = DEFAULT_MINS_2;//Cannot change
					NPC->r.maxs[2] = NPC->client->ps.standheight = n + DEFAULT_MINS_2;
				}
				NPC->radius = n;
				continue;
			}

			if ( !Q_stricmp( token, "crouchheight" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					continue;
				}

				NPC->client->ps.crouchheight = n + DEFAULT_MINS_2;
				continue;
			}

			if ( !parsingPlayer )
			{
				if ( !Q_stricmp( token, "movetype" ) ) 
				{
					if ( COM_ParseString( &p, &value ) ) 
					{
						continue;
					}
					if ( Q_stricmp( "flyswim", value ) == 0 )
					{
						NPC->client->ps.eFlags2 |= EF2_FLYING;
					}
					//NPC->client->moveType = (movetype_t)MoveTypeNameToEnum(value);
					//rwwFIXMEFIXME: support for movetypes
					continue;
				}
					
				// yawSpeed
				if ( !Q_stricmp( token, "yawSpeed" ) ) {
					if ( COM_ParseInt( &p, &n ) ) {
						SkipRestOfLine( &p );
						continue;
					}
					if ( n <= 0) {
						Com_Printf(  "bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->yawSpeed = ((float)(n));
					}
					continue;
				}

				// walkSpeed
				if ( !Q_stricmp( token, "walkSpeed" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 0 ) 
					{
						Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->walkSpeed = n;
					}
					continue;
				}
				
				//runSpeed
				if ( !Q_stricmp( token, "runSpeed" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 0 ) 
					{
						Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->runSpeed = n;
					}
					continue;
				}

				//acceleration
				if ( !Q_stricmp( token, "acceleration" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < 0 ) 
					{
						Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						stats->acceleration = n;
					}
					continue;
				}
				//sex - skip in MP
				if ( !Q_stricmp( token, "sex" ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
//===MISC===============================================================================
				// default behavior
				if ( !Q_stricmp( token, "behavior" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( n < BS_DEFAULT || n >= NUM_BSTATES ) 
					{
						Com_Printf( S_COLOR_YELLOW"WARNING: bad %s in NPC '%s'\n", token, NPCName );
						continue;
					}
					if ( NPC->NPC )
					{
						NPC->NPC->defaultBehavior = (bState_t)(n);
					}
					continue;
				}
			}

			// snd
			if ( !Q_stricmp( token, "snd" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( !(NPC->r.svFlags&SVF_NO_BASIC_SOUNDS) )
				{
					//FIXME: store this in some sound field or parse in the soundTable like the animTable...
					Q_strncpyz( sound, value, sizeof( sound ) );
					patch = strstr( sound, "/" );
					if ( patch ) 
					{
						*patch = 0;
					}
				//	ci->customBasicSoundDir = G_NewString( sound );
					//rwwFIXMEFIXME: Hooray for violating client server rules
				}
				continue;
			}

			// sndcombat
			if ( !Q_stricmp( token, "sndcombat" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( !(NPC->r.svFlags&SVF_NO_COMBAT_SOUNDS) )
				{
					//FIXME: store this in some sound field or parse in the soundTable like the animTable...
					Q_strncpyz( sound, value, sizeof( sound ) );
					patch = strstr( sound, "/" );
					if ( patch ) 
					{
						*patch = 0;
					}
				//	ci->customCombatSoundDir = G_NewString( sound );
				}
				continue;
			}
			
			// sndextra
			if ( !Q_stricmp( token, "sndextra" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( !(NPC->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
				{
					//FIXME: store this in some sound field or parse in the soundTable like the animTable...
					Q_strncpyz( sound, value, sizeof( sound ) );
					patch = strstr( sound, "/" );
					if ( patch ) 
					{
						*patch = 0;
					}
				//	ci->customExtraSoundDir = G_NewString( sound );
				}
				continue;
			}

			// sndjedi
			if ( !Q_stricmp( token, "sndjedi" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( !(NPC->r.svFlags&SVF_NO_EXTRA_SOUNDS) )
				{
					//FIXME: store this in some sound field or parse in the soundTable like the animTable...
					Q_strncpyz( sound, value, sizeof( sound ) );
					patch = strstr( sound, "/" );
					if ( patch ) 
					{
						*patch = 0;
					}
					//ci->customJediSoundDir = G_NewString( sound );
				}
				continue;
			}

			//New NPC/jedi stats:
			//starting weapon
			if ( !Q_stricmp( token, "weapon" ) ) 
			{
				int weap;

				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				//FIXME: need to precache the weapon, too?  (in above func)
				weap = GetIDForString( WPTable, value );
				if ( weap >= WP_NONE && weap <= WP_NUM_WEAPONS )///*WP_BLASTER_PISTOL*/WP_SABER ) //?!
				{
					NPC->client->ps.weapon = weap;
					NPC->client->ps.stats[STAT_WEAPONS] |= ( 1 << NPC->client->ps.weapon );
					if ( weap > WP_NONE )
					{
					//	RegisterItem( FindItemForWeapon( (weapon_t)(NPC->client->ps.weapon) ) );	//precache the weapon
						NPC->client->ps.ammo[weaponData[NPC->client->ps.weapon].ammoIndex] = 100;//FIXME: max ammo!
					}
				}
				continue;
			}
			
			if ( !parsingPlayer )
			{
				//altFire
				if ( !Q_stricmp( token, "altFire" ) ) 
				{
					if ( COM_ParseInt( &p, &n ) ) 
					{
						SkipRestOfLine( &p );
						continue;
					}
					if ( NPC->NPC )
					{
						if ( n != 0 )
						{
							NPC->NPC->scriptFlags |= SCF_ALT_FIRE;
						}
					}
					continue;
				}
				//Other unique behaviors/numbers that are currently hardcoded?
			}

			//force powers
			fp = GetIDForString( FPTable, token );
			if ( fp >= FP_FIRST && fp < NUM_FORCE_POWERS )
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//FIXME: need to precache the fx, too?  (in above func)
				//cap
				if ( n > 5 )
				{
					n = 5;
				}
				else if ( n < 0 )
				{
					n = 0;
				}
				if ( n )
				{//set
					NPC->client->ps.fd.forcePowersKnown |= ( 1 << fp );
				}
				else
				{//clear
					NPC->client->ps.fd.forcePowersKnown &= ~( 1 << fp );
				}
				NPC->client->ps.fd.forcePowerLevel[fp] = n;
				continue;
			}

			//max force power
			if ( !Q_stricmp( token, "forcePowerMax" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				NPC->client->ps.fd.forcePowerMax = n;
				continue;
			}

			//force regen rate - default is 100ms
			if ( !Q_stricmp( token, "forceRegenRate" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//NPC->client->ps.forcePowerRegenRate = n;
				//rwwFIXMEFIXME: support this?
				continue;
			}

			//force regen amount - default is 1 (points per second)
			if ( !Q_stricmp( token, "forceRegenAmount" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//NPC->client->ps.forcePowerRegenAmount = n;
				//rwwFIXMEFIXME: support this?
				continue;
			}

			//have a sabers.cfg and just name your saber in your NPCs.cfg/ICARUS script
			//saber name
			if ( !Q_stricmp( token, "saber" ) ) 
			{
				char *saberName;

				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}

				saberName = (char *)BG_TempAlloc(4096);//G_NewString( value );
				strcpy(saberName, value);

				WP_SaberParseParms( saberName, &NPC->client->saber[0] );
				npcSaber1 = G_ModelIndex(va("@%s", saberName));

				BG_TempFree(4096);
				continue;
			}
			
			//second saber name
			if ( !Q_stricmp( token, "saber2" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}

				if ( !(NPC->client->saber[0].saberFlags&SFL_TWO_HANDED) )
				{//can't use a second saber if first one is a two-handed saber...?
					char *saberName = (char *)BG_TempAlloc(4096);//G_NewString( value );
					strcpy(saberName, value);

					WP_SaberParseParms( saberName, &NPC->client->saber[1] );
					if ( (NPC->client->saber[1].saberFlags&SFL_TWO_HANDED) )
					{//tsk tsk, can't use a twoHanded saber as second saber
						WP_RemoveSaber( NPC->client->saber, 1 );
					}
					else
					{
						//NPC->client->ps.dualSabers = qtrue;
						npcSaber2 = G_ModelIndex(va("@%s", saberName));
					}
					BG_TempFree(4096);
				}
				continue;
			}

			// saberColor
			if ( !Q_stricmp( token, "saberColor" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					saber_colors_t color = TranslateSaberColor( value );
					for ( n = 0; n < MAX_BLADES; n++ )
					{
						NPC->client->saber[0].blade[n].color = color;
					}
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor2" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[1].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor3" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[2].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor4" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[3].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor5" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[4].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor6" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[5].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor7" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[6].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberColor8" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[0].blade[7].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					saber_colors_t color = TranslateSaberColor( value );
					for ( n = 0; n < MAX_BLADES; n++ )
					{
						NPC->client->saber[1].blade[n].color = color;
					}
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color2" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[1].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color3" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[2].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color4" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[3].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color5" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[4].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color6" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[5].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color7" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[6].color = TranslateSaberColor( value );
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Color8" ) ) 
			{
				if ( COM_ParseString( &p, &value ) ) 
				{
					continue;
				}
				if ( NPC->client )
				{
					NPC->client->saber[1].blade[7].color = TranslateSaberColor( value );
				}
				continue;
			}

			//saber length
			if ( !Q_stricmp( token, "saberLength" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}

				for ( n = 0; n < MAX_BLADES; n++ )
				{
					NPC->client->saber[0].blade[n].lengthMax = f;
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberLength2" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[1].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength3" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[2].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength4" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[3].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength5" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[4].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength6" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[5].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength7" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[6].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberLength8" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[0].blade[7].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				for ( n = 0; n < MAX_BLADES; n++ )
				{
					NPC->client->saber[1].blade[n].lengthMax = f;
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length2" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[1].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length3" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[2].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length4" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[3].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length5" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[4].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length6" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[5].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length7" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[6].lengthMax = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Length8" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 4.0f )
				{
					f = 4.0f;
				}
				NPC->client->saber[1].blade[7].lengthMax = f;
				continue;
			}

			//saber radius
			if ( !Q_stricmp( token, "saberRadius" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				for ( n = 0; n < MAX_BLADES; n++ )
				{
					NPC->client->saber[0].blade[n].radius = f;
				}
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius2" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[1].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius3" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[2].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius4" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[3].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius5" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[4].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius6" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[5].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius7" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[6].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saberRadius8" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[0].blade[7].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				for ( n = 0; n < MAX_BLADES; n++ )
				{
					NPC->client->saber[1].blade[n].radius = f;
				}
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius2" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[1].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius3" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[2].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius4" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[3].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius5" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[4].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius6" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[5].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius7" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[6].radius = f;
				continue;
			}

			if ( !Q_stricmp( token, "saber2Radius8" ) ) 
			{
				if ( COM_ParseFloat( &p, &f ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( f < 0.25f )
				{
					f = 0.25f;
				}
				NPC->client->saber[1].blade[7].radius = f;
				continue;
			}

			//ADD:
			//saber sounds (on, off, loop)
			//loop sound (like Vader's breathing or droid bleeps, etc.)

			//starting saber style
			if ( !Q_stricmp( token, "saberStyle" ) ) 
			{
				if ( COM_ParseInt( &p, &n ) ) 
				{
					SkipRestOfLine( &p );
					continue;
				}
				//cap
				if ( n < 0 )
				{
					n = 0;
				}
				else if ( n > 5 )
				{
					n = 5;
				}
				NPC->client->ps.fd.saberAnimLevel = n;
				/*
				if ( parsingPlayer )
				{
					cg.saberAnimLevelPending = n;
				}
				*/
				continue;
			}

			if ( !parsingPlayer )
			{
				Com_Printf( "WARNING: unknown keyword '%s' while parsing '%s'\n", token, NPCName );
			}
			SkipRestOfLine( &p );
		}
	}

/*
Ghoul2 Insert Start
*/
	if ( !md3Model )
	{
		qboolean setTypeBack = qfalse;

		if (npcSaber1 == 0)
		{ //use "kyle" for a default then
			npcSaber1 = G_ModelIndex("@Kyle");
			WP_SaberParseParms( "Kyle", &NPC->client->saber[0] );
		}

		NPC->s.npcSaber1 = npcSaber1;
		NPC->s.npcSaber2 = npcSaber2;

		if (!customSkin[0])
		{
			strcpy(customSkin, "default");
		}

		if ( NPC->client && NPC->client->NPC_class == CLASS_VEHICLE )
		{ //vehicles want their names fed in as models
			//we put the $ in front to indicate a name and not a model
			strcpy(playerModel, va("$%s", NPCName));
		}
		SetupGameGhoul2Model(NPC, playerModel, customSkin);

		if (!NPC->NPC_type)
		{ //just do this for now so NPC_Precache can see the name.
			NPC->NPC_type = (char *)NPCName;
			setTypeBack = qtrue;
		}

		NPC_Precache(NPC); //this will just soundindex some values for sounds on the client,

		if (setTypeBack)
		{ //don't want this being set if we aren't ready yet.
			NPC->NPC_type = NULL;
		}
	}
	else
	{
		Com_Printf("MD3 MODEL NPC'S ARE NOT SUPPORTED IN MP!\n");
		return qfalse;
	}
/*
Ghoul2 Insert End
*/
	/*
	if(	NPCsPrecached )
	{//Spawning in after initial precache, our models are precached, we just need to set our clientInfo
		CG_RegisterClientModels( NPC->s.number );
		CG_RegisterNPCCustomSounds( ci );
		CG_RegisterNPCEffects( NPC->client->playerTeam );
	}
	*/
	//rwwFIXMEFIXME: Do something here I guess to properly precache stuff.

	return qtrue;
}

#ifdef _XBOX
char *npcParseBuffer = NULL;
#else
char npcParseBuffer[MAX_NPC_DATA_SIZE];
#endif

void NPC_LoadParms( void ) 
{
	int			len, totallen, npcExtFNLen, mainBlockLen, fileCnt, i;
//	const char	*filename = "ext_data/NPC2.cfg";
	char		/**buffer,*/ *holdChar, *marker;
	char		npcExtensionListBuf[2048];			//	The list of file names read in
	fileHandle_t f;
	len = 0;

	//remember where to store the next one
	totallen = mainBlockLen = len;
	marker = NPCParms+totallen;
	*marker = 0;

	//now load in the extra .npc extensions
	fileCnt = trap_FS_GetFileList("ext_data/NPCs", ".npc", npcExtensionListBuf, sizeof(npcExtensionListBuf) );

#ifdef _XBOX
	npcParseBuffer = (char *) Z_Malloc(MAX_NPC_DATA_SIZE, TAG_TEMP_WORKSPACE, qfalse, 4);
#endif

	holdChar = npcExtensionListBuf;
	for ( i = 0; i < fileCnt; i++, holdChar += npcExtFNLen + 1 ) 
	{
		npcExtFNLen = strlen( holdChar );

//		Com_Printf( "Parsing %s\n", holdChar );

		len = trap_FS_FOpenFile(va( "ext_data/NPCs/%s", holdChar), &f, FS_READ);

		if ( len == -1 ) 
		{
			Com_Printf( "error reading file\n" );
		}
		else
		{
			if ( totallen + len >= MAX_NPC_DATA_SIZE ) {
				G_Error( "NPC extensions (*.npc) are too large" );
			}
			trap_FS_Read(npcParseBuffer, len, f);
			npcParseBuffer[len] = 0;

			len = COM_Compress( npcParseBuffer );

			strcat( marker, npcParseBuffer );
			strcat(marker, "\n");
			len++;
			trap_FS_FCloseFile(f);

			totallen += len;
			marker = NPCParms+totallen;
			//*marker = 0; //rww - make sure this is null or strcat will not append to the correct place
			//rww  12/19/02-actually the probelm was npcParseBuffer not being nul-term'd, which could cause issues in the strcat too
		}
	}

#ifdef _XBOX
	Z_Free(npcParseBuffer);
	npcParseBuffer = NULL;
#endif

}
