///////////////////////////////////////////////////////////////////////////////
//	viewthing.cpp
//
//	for viewing models and their animations
//
//	FIXME:	remove or disable for final release
///////////////////////////////////////////////////////////////////////////////

#include	"world.h"
#include	"hierarchy.h"
#include	"ai_frames.h"

///////////////////////////////////////////////////////////////////////////////
//	exports
///////////////////////////////////////////////////////////////////////////////

DllExport	void	viewthing (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////
//	defines
///////////////////////////////////////////////////////////////////////////////

#define	VT_MAX_SEQUENCES	64

///////////////////////////////////////////////////////////////////////////////
//	typedefs
///////////////////////////////////////////////////////////////////////////////

typedef	struct
{
	char	seqName [16];		//	name of sequence
	
	int		seqStart;			//	starting frame of sequence
	int		seqLength;			//	# of frames in sequence
} seqList_t;

typedef	struct
{
	seqList_t	*list;

	//	list of sequences to play... will loop through them in order
	int			PlayList [VT_MAX_SEQUENCES];

	int			PlayListLength;
	int			CurrentSequence;

	int			startFrame;
	int			endFrame;
} seqHeader_t;

///////////////////////////////////////////////////////////////////////////////
//	globals
///////////////////////////////////////////////////////////////////////////////

//static	int				vt_startFrame = 0;
//static	int				vt_endFrame = 0;

static	float			vt_thinkTime = 0.05;
static	int				vt_IsHierarchical = 0;

static	userEntity_t	*vt;		
/*
static	userEntity_t	*vt_Weapon;
static	userEntity_t	*vt_Head;
static	userEntity_t	*vt_Torso;
static	userEntity_t	*vt_Legs;
*/
static	seqList_t		vt_List [VT_MAX_SEQUENCES];
static	seqList_t		vt_WeaponList [VT_MAX_SEQUENCES];
static	seqList_t		vt_HeadList [VT_MAX_SEQUENCES];
static	seqList_t		vt_TorsoList [VT_MAX_SEQUENCES];
static	seqList_t		vt_LegsList [VT_MAX_SEQUENCES];

static	seqHeader_t		vt_Header;
static	seqHeader_t		vt_WeaponHeader;
static	seqHeader_t		vt_HeadHeader;
static	seqHeader_t		vt_TorsoHeader;
static	seqHeader_t		vt_LegsHeader;

//	for printing out the name of the model when a surface doesn't exist
static	char	vt_WeaponName [256];
static	char	vt_HeadName [256];
static	char	vt_TorsoName [256];
static	char	vt_LegsName [256];

/*
static	seqList_t		vt_List [VT_MAX_SEQUENCES];

//	list of sequences to play... will loop through them in order
static	int				vt_PlayList [VT_MAX_SEQUENCES];
//	# of sequences in vt_PlayList
static	int				vt_PlayListLength = 0;
//	current sequence in vt_PlayList
static	int				vt_CurrentSequence = 0;
*/

///////////////////////////////////////////////////////////////////////////////
//	vt_InitList
//
///////////////////////////////////////////////////////////////////////////////

void	vt_InitList (seqHeader_t *header, seqList_t *list)
{
	memset (header->PlayList, 0x00, sizeof (header->PlayList));
	memset (list, 0x00, sizeof (seqList_t) * VT_MAX_SEQUENCES);
	header->list = list;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_InList
//
//	returns FALSE if sequence name is not already in list
///////////////////////////////////////////////////////////////////////////////

int	vt_InList (seqList_t *list, char *name)
{
	int		i;

	for (i = 0; list [i].seqName [0] != 0x00; i++)
	{
		if (!stricmp (name, list [i].seqName))
			return	TRUE;
	}

	return	FALSE;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ListIndex
//
//	returns the index in vt_List of the specified seqeunce name
//	or -1 if name was not in vt_List
///////////////////////////////////////////////////////////////////////////////

int	vt_ListIndex (seqList_t *list, char *name)
{
	int		i;

	for (i = 0; list [i].seqName [0] != 0x00; i++)
	{
		if (!stricmp (name, list [i].seqName))
			return	i;
	}

	return	-1;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_AddToList
//
//	adds a sequence at the end of vt_List
///////////////////////////////////////////////////////////////////////////////

void	vt_AddToList (seqList_t *list, char *name, int start, int len)
{
	int		i;

	for (i = 0; list [i].seqName [0] != 0x00; i++);

	if (i < VT_MAX_SEQUENCES)
	{
		strcpy (list [i].seqName, name);
		list [i].seqStart = start;
		list [i].seqLength = len;
	}
	else
	{
		gstate->Con_Printf ("Too many unique sequences in model!\n");
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_isAlpha
//
//	returns true if character is an upper or lowercase letter
///////////////////////////////////////////////////////////////////////////////

int	vt_isAlpha (char c)
{
	if (c >= 0x41 && c <= 0x5A)
		return	true;
	if (c >= 0x61 && c <= 0x7A)
		return  true;

	return	false;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_GetSequences
//
//	shows all sequences and the number of frames in each
//
//	goes through all frames and saves each unique name
///////////////////////////////////////////////////////////////////////////////

void	vt_GetSequences (userEntity_t *self, seqHeader_t *header)
{
	char	frameName [16];
	int		frame = 0;
	int		startFrame, endFrame, seqLength, inList, i;

	//	init list
	memset (header->list, 0x00, sizeof (seqList_t) * VT_MAX_SEQUENCES);

	while (1)
	{
		gstate->GetFrameName (self->s.modelindex, frame, frameName);

		if (frameName [0] == 0x00 || !vt_isAlpha (frameName [0]))
			break;

		//	strip frame number from end of name
		for (i = 0; frameName [i] < 0x30 || frameName [i] > 0x39; i++);

		frameName [i] = 0x00;

		if (vt_ListIndex (header->list, frameName) < 0)
		{
			com->GetFrames (self, frameName, &startFrame, &endFrame);
			vt_AddToList (header->list, frameName, startFrame, endFrame - startFrame);
		}

		frame++;
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ListSequences
//
//	shows all sequences and the number of frames in each
//
//	goes through all frames and saves each unique name
//	only lists the sequences available to the legs for hierarhical models
///////////////////////////////////////////////////////////////////////////////

void	vt_ListSequences (userEntity_t *self)
{
	int				i = 0, frameTotal = 0;
	char			name [16], start [6], len [7];
	seqHeader_t		*header;
	playerHook_t	*hook = (playerHook_t *) vt->userHook;

	if (vt_IsHierarchical)
	{
		header = &vt_LegsHeader;
	
		vt_GetSequences (hook->weapon, &vt_WeaponHeader);
		vt_GetSequences (hook->head, &vt_HeadHeader);
		vt_GetSequences (hook->torso, &vt_TorsoHeader);
		vt_GetSequences (hook->legs, header);
	}
	else
	{
		header = &vt_Header;
		vt_GetSequences (vt, header);
	}

	gstate->Con_Printf ("\nname            start length\n");
	gstate->Con_Printf ("----            ----- ------\n");

	while (1)
	{
		if (header->list [i].seqName [0] == 0x00)
			break;

		//	format output
		memset (name, 0x20, sizeof (name));
		strcpy (name, header->list [i].seqName);
		name [strlen (header->list [i].seqName)] = 0x20;
		name [15] = 0x00;

		sprintf (start, "%5i", header->list [i].seqStart);
		sprintf (len, "%6i", header->list [i].seqLength + 1);

		gstate->Con_Printf ("%s %s %s\n", name, start, len);

		frameTotal += header->list [i].seqLength;

		i++;
	}

	if (!frameTotal)
		gstate->Con_Printf ("No frames in model.\n");
	else
	{
		gstate->Con_Printf ("----------------------------\n");
		gstate->Con_Printf ("%i total frames\n\n", frameTotal);
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_HierarchyStop
//
//	scales all of the hierarchical bits
///////////////////////////////////////////////////////////////////////////////

void	vt_HierarchyStop (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;	

	hook->weapon->nextthink = -1;
	hook->head->nextthink = -1;
	hook->torso->nextthink = -1;
	hook->legs->nextthink = -1;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_HierarchyScale
//
//	scales all of the hierarchical bits
///////////////////////////////////////////////////////////////////////////////

void	vt_HierarchyScale (userEntity_t *self, float xscale, float yscale, float zscale)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;	

	com->SetVector (hook->weapon->s.render_scale, xscale, yscale, zscale);
	com->SetVector (hook->head->s.render_scale, xscale, yscale, zscale);
	com->SetVector (hook->torso->s.render_scale, xscale, yscale, zscale);
	com->SetVector (hook->legs->s.render_scale, xscale, yscale, zscale);
}

///////////////////////////////////////////////////////////////////////////////
//	vt_HierarchyOff
//
//	sets nodraw flags on hierarchical bits
///////////////////////////////////////////////////////////////////////////////

void	vt_HierarchyOff (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;	

	hook->weapon->s.renderfx |= RF_NODRAW;
	hook->head->s.renderfx |= RF_NODRAW;
	hook->torso->s.renderfx |= RF_NODRAW;
	hook->legs->s.renderfx |= RF_NODRAW;

	self->s.renderfx -= (self->s.renderfx & RF_NODRAW);
}

///////////////////////////////////////////////////////////////////////////////
//	vt_HierarchyOn
//
//	clears nodraw flags on hierarchical bits
///////////////////////////////////////////////////////////////////////////////

void	vt_HierarchyOn (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;	

	//	Nelno:	don't draw weapon for now
	//	hook->weapon->s.renderfx -= (hook->weapon->s.renderfx & RF_NODRAW);
	hook->head->s.renderfx -= (hook->head->s.renderfx & RF_NODRAW);
	hook->torso->s.renderfx -= (hook->torso->s.renderfx & RF_NODRAW);
	hook->legs->s.renderfx -= (hook->legs->s.renderfx & RF_NODRAW);

	self->s.renderfx |= RF_NODRAW;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_Stop
//
//	stops animation of all viewthings
//	stops animation of all hierarchical bits
///////////////////////////////////////////////////////////////////////////////

void	vt_Stop (userEntity_t *self)
{
	userEntity_t	*ent;
	int				count = 0;

	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className)
			continue;

		if (!stricmp (ent->className, "viewthing"))
		{
			ent->nextthink = -1;

			vt_HierarchyStop (ent);

			count++;
		}
	}

	if (!count)
		gstate->Con_Printf ("No viewthing on map.\n");
}

///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

void	vt_ViewFrame (userEntity_t *self)
{
	userEntity_t	*ent;
	int				count = 0;
	int				frame;
	playerHook_t	*hook;	

	if (gstate->GetArgc () != 2)
	{
		gstate->Con_Printf ("  USAGE: vf [frame]\n");
		return;
	}
	
	vt_Stop (self);

	frame = atoi (gstate->GetArgv (1));

	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className)
			continue;

		if (!stricmp (ent->className, "viewthing"))
		{
			hook = (playerHook_t *) ent->userHook;

			if (vt_IsHierarchical)
				hook->legs->s.frame = frame;
			else
				ent->s.frame = frame;
			count++;
		}
	}

	if (!count)
		gstate->Con_Printf ("No viewthing on map.\n");
}

///////////////////////////////////////////////////////////////////////////////
//	vt_IncFrames
//
//	changes self->s.frame
//	doesn't frame skip, so percieved playback can drop below desired rate
///////////////////////////////////////////////////////////////////////////////

void	vt_IncFrames (userEntity_t *self, seqHeader_t *header)
{
	self->s.frame++;
	if (self->s.frame > header->endFrame)
	{
		if (header->PlayListLength > 1)
		{
			header->CurrentSequence++;
			if (header->CurrentSequence >= header->PlayListLength)
				header->CurrentSequence = 0;

			header->startFrame = header->list [header->PlayList [header->CurrentSequence]].seqStart;
			header->endFrame = header->startFrame + header->list [header->PlayList [header->CurrentSequence]].seqLength;
		}

		self->s.frame = header->startFrame;
	}
}


///////////////////////////////////////////////////////////////////////////////
//	vt_Animate
//
//	think that changes self->s.frame
//	doesn't frame skip, so percieved playback can drop below desired rate
///////////////////////////////////////////////////////////////////////////////

void	vt_Animate (userEntity_t *self)
{
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	if (vt_IsHierarchical)
	{
		vt_IncFrames (hook->weapon, &vt_WeaponHeader);
		vt_IncFrames (hook->head, &vt_HeadHeader);
		vt_IncFrames (hook->torso, &vt_TorsoHeader);
		vt_IncFrames (hook->legs, &vt_LegsHeader);
	}
	else
		vt_IncFrames (self, &vt_Header);

	self->nextthink = gstate->time + vt_thinkTime;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_GetPlayList
//
//	gets play list a model from the current console line
///////////////////////////////////////////////////////////////////////////////

void	vt_GetPlayList (seqHeader_t *header)
{
	int		i, index;

	//	get number of each sequence specified
	memset (header->PlayList, 0x00, sizeof (header->PlayList));

	for (i = 1; i < gstate->GetArgc (); i++)
	{
		index = vt_ListIndex (header->list, gstate->GetArgv (i));
		if (index < 0)
		{
			gstate->Con_Printf ("Sequence %s is not in model.\n", gstate->GetArgv (i));
			return;
		}

		header->PlayList [i - 1] = index;
	}

	header->PlayListLength = i - 1;
	header->CurrentSequence = 0;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_SetFrames
//
///////////////////////////////////////////////////////////////////////////////

void	vt_SetFrames (userEntity_t *ent, seqHeader_t *header)
{
	header->startFrame = header->list [header->PlayList [0]].seqStart;
	header->endFrame = header->startFrame + header->list [header->PlayList [0]].seqLength;
	ent->s.frame = header->startFrame;
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ViewSequence
//
//	starts a sequence animating
//	handles multiple viewthings -- who knows why Nelno bothered...
//
//	also handles looping of multiple sequences like:
//		tranza, ataka, tranzb, tranza, ataka, tranzb, ...
///////////////////////////////////////////////////////////////////////////////

void	vt_ViewSequence (userEntity_t *self)
{
	userEntity_t	*ent;
	int				i, index;
	seqHeader_t		*header;
	playerHook_t	*hook;

	if (gstate->GetArgc () < 2)
	{
		gstate->Con_Printf ("  USAGE: vs [sequence name]\n");
		return;
	}

	if (vt_IsHierarchical)
		header = &vt_LegsHeader;
	else
		header = &vt_Header;
/*
	//	get number of each sequence specified
	memset (header->PlayList, 0x00, sizeof (header->PlayList));

	for (i = 1; i < gstate->GetArgc (); i++)
	{
		index = vt_ListIndex (header->list, gstate->GetArgv (i));
		if (index < 0)
		{
			gstate->Con_Printf ("Sequence %i is not in model.\n", i);
			return;
		}

		header->PlayList [i - 1] = index;
	}

	header->PlayListLength = i - 1;
	header->CurrentSequence = 0;

	//	just get frames once since all viewthings use same frame #s
//	com->GetFrames (vt, gstate->GetArgv (1), &vt_startFrame, &vt_endFrame);

	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className)
			continue;

		if (!stricmp (ent->className, "viewthing"))
		{
			ent->nextthink = gstate->time + vt_thinkTime;
			ent->think = vt_Animate;

			header->startFrame = header->list [header->PlayList [0]].seqStart;
			header->endFrame = header->startFrame + header->list [header->PlayList [0]].seqLength;
			ent->s.frame = header->startFrame;
		}
	}
*/
	if (vt_IsHierarchical)
	{
		vt_GetPlayList (&vt_WeaponHeader);
		vt_GetPlayList (&vt_HeadHeader);
		vt_GetPlayList (&vt_TorsoHeader);
		vt_GetPlayList (&vt_LegsHeader);
	}
	else
		vt_GetPlayList (&vt_Header);

	//	do for all viewthings
	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className || !ent->inuse)
			continue;

		if (!stricmp (ent->className, "viewthing"))
		{
			//	start think for playing frames
			ent->nextthink = gstate->time + vt_thinkTime;
			ent->think = vt_Animate;

			if (vt_IsHierarchical)
			{
				hook = (playerHook_t *) ent->userHook;

				vt_SetFrames (hook->weapon, &vt_WeaponHeader);
				vt_SetFrames (hook->head, &vt_HeadHeader);
				vt_SetFrames (hook->torso, &vt_TorsoHeader);
				vt_SetFrames (hook->legs, &vt_LegsHeader);
			}
			else
				vt_SetFrames (ent, &vt_Header);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_Rotate
//
//	rotate the viewmodel x degrees
///////////////////////////////////////////////////////////////////////////////

void	vt_Rotate (userEntity_t *self)
{
	userEntity_t	*ent;
	int				i, angle_add;

	if (gstate->GetArgc () != 2)
	{
		gstate->Con_Printf ("  USAGE: vr [degrees]\n");
		return;
	}

	angle_add = atof (gstate->GetArgv (1));

	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className)
			continue;

		if (!stricmp (ent->className, "viewthing"))
			ent->s.angles [1] = com->AngleMod (ent->s.angles [1] + angle_add);
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ScaleModel
///////////////////////////////////////////////////////////////////////////////

void	vt_ScaleModel (userEntity_t *ent)
{
	int			count = gstate->GetArgc ();
	float		xscale, yscale, zscale;

	if (count != 2 && count != 4)
	{
		gstate->Con_Printf ("  USAGE: vscale [scale factor] or vscale [scale x] [scale y] [scale z]\n");
		return;
	}

	if (count == 2)
		xscale = yscale = zscale = atof (gstate->GetArgv (1));
	else
	{
		xscale = atof (gstate->GetArgv (1));
		yscale = atof (gstate->GetArgv (2));
		zscale = atof (gstate->GetArgv (3));
	}

	for (ent = gstate->FirstEntity (); ent; ent = gstate->NextEntity (ent))
	{
		if (!ent->className)
			continue;

		if (!stricmp (ent->className, "viewthing"))
		{
			ent->s.render_scale [0] = xscale;
			ent->s.render_scale [1] = yscale;
			ent->s.render_scale [2] = zscale;

			vt_HierarchyScale (ent, xscale, yscale, zscale);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ViewModel
///////////////////////////////////////////////////////////////////////////////

void	vt_ViewModel (userEntity_t *ent)
{
	userEntity_t	*vm;
	char			name [256];

	if (gstate->GetArgc () != 2)
	{
		gstate->Con_Printf ("  USAGE: vm [modelname]\n");
		return;
	}

	vt_Stop (ent);

	//	find viewthing entity
	for (vm = gstate->FirstEntity (); vm && stricmp (vm->className, "viewthing"); vm = gstate->NextEntity (vm));

	if (!vm)
	{
		gstate->Con_Printf ("No viewthing on map.\n");
		return;
	}

	sprintf (name, "models/%s/%s.md2", gstate->GetArgv (1), gstate->GetArgv (1));

	gstate->ForceModelLoad (vt->s.modelindex, name);

	//	set all viewthing frames to 0
	for (vm = gstate->FirstEntity (); vm; vm = gstate->NextEntity (vm))
	{
		if (!stricmp (vm->className, "viewthing"))
		{
			//	turn off all hierarchical models
			vt_HierarchyOff (vm);
			vm->s.frame = 0;
		}
	}

	vt_GetSequences (vt, &vt_Header);
}

///////////////////////////////////////////////////////////////////////////////
//	viewthing_init
//
//	checks to see if a client has connected and if so, it sets all the model
//	indexes for the viewthing's hierarchical parts to those of the client
//	
//	if no client has connected, the viewthing's think is set up to try again
//	on the next frame
///////////////////////////////////////////////////////////////////////////////

void	viewthing_init (userEntity_t *self)
{
	userEntity_t	*first_client;
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	playerHook_t	*phook;

	//	check the first client to see if he is here...
	first_client = gstate->g_edicts;
	first_client++;

	if (first_client->inuse)
	{
		phook = (playerHook_t *) first_client->userHook;

		if (phook)
		{
			gstate->Con_Printf ("Set up hierarchical viewthing.\n");
			hook->weapon->s.modelindex = phook->weapon->s.modelindex;
			hook->weapon->modelName = phook->weapon->modelName;

			hook->head->s.modelindex = phook->head->s.modelindex;
			hook->head->modelName = phook->head->modelName;

			hook->torso->s.modelindex = phook->torso->s.modelindex;
			hook->torso->modelName = phook->torso->modelName;

			hook->legs->s.modelindex = phook->legs->s.modelindex;
			hook->legs->modelName = phook->legs->modelName;

			self->nextthink = -1;

			return;
		}
	}

	self->nextthink = gstate->time + 0.1;
}

///////////////////////////////////////////////////////////////////////////////
//	viewthing
//
//	spawn function
///////////////////////////////////////////////////////////////////////////////

void	viewthing (userEntity_t *self)
{
	userEntity_t	*head;
	int				i;
	playerHook_t	*hook;

	self->userHook = com->malloc_hook (sizeof (playerHook_t), MEM_MALLOC);
	hook = (playerHook_t *) self->userHook;

	//	set up all structures
	vt_InitList (&vt_Header, vt_List);
	vt_InitList (&vt_WeaponHeader, vt_WeaponList);
	vt_InitList (&vt_HeadHeader, vt_HeadList);
	vt_InitList (&vt_TorsoHeader, vt_TorsoList);
	vt_InitList (&vt_LegsHeader, vt_LegsList);

	//	load viewmodel based on episode
	if (strstr (gstate->mapName, "e1"))
		self->s.modelindex = gstate->ModelIndex ("models/ragemaster/ragemaster.md2");
	else if (strstr (gstate->mapName, "e2"))
		self->s.modelindex = gstate->ModelIndex ("models/satyr/satyr.md2");
	else if (strstr (gstate->mapName, "e3"))
		self->s.modelindex = gstate->ModelIndex ("models/lycanthir/lycanthir.md2");
	else if (strstr (gstate->mapName, "e4"))
		self->s.modelindex = gstate->ModelIndex ("models/rocketdude/rocketdude.md2");
	else 
		self->s.modelindex = gstate->ModelIndex ("models/debug/goal/goal.md2");
	
	gstate->LinkEntity (self);

	//	spawn hierarchical parts
	hook->weapon = gstate->SpawnEntity ();
	hook->weapon->className = "vt_weapon";
	hook->weapon->s.renderfx = RF_NODRAW;
	VectorCopy (self->s.origin, hook->weapon->s.origin);
	hook->weapon->s.modelindex = gstate->ModelIndex ("models/katana/katana.md2");
	gstate->LinkEntity (hook->weapon);
	
	hook->head = gstate->SpawnEntity ();
	hook->head->className = "vt_head";
	hook->head->s.renderfx = RF_NODRAW;
	VectorCopy (self->s.origin, hook->head->s.origin);
	hook->head->s.modelindex = gstate->ModelIndex ("models/h_head/h_head.md2");
	gstate->LinkEntity (hook->head);
	
	hook->torso = gstate->SpawnEntity ();
	hook->torso->className = "vt_torso";
	hook->torso->s.renderfx = RF_NODRAW;
	VectorCopy (self->s.origin, hook->torso->s.origin);
	hook->torso->s.modelindex = gstate->ModelIndex ("models/h_torso/h_torso.md2");
	gstate->LinkEntity (hook->torso);	

	hook->legs = gstate->SpawnEntity ();
	hook->legs->className = "vt_legs";
	hook->legs->s.renderfx = RF_NODRAW;
	VectorCopy (self->s.origin, hook->legs->s.origin);
	hook->legs->s.modelindex = gstate->ModelIndex ("models/h_legs/h_legs.md2");
	gstate->LinkEntity (hook->legs);

	self->think = viewthing_init;
	self->nextthink = gstate->time + 1.0;

	vt = self;

	//	get sequences here so that a vt_ViewSequences will work right off
	vt_GetSequences (self, &vt_Header);
}

///////////////////////////////////////////////////////////////////////////////
//	vt_ViewHierarchical
//
//	if vt_IsHierarchical == 0 then new entities are spawned for the legs, 
//	torso, head and weapon.
//	the current models for the player legs, torso, head and weapon are changed
//	reloaded as the new models.
//
//	does not handle multiple viewthings.  Suck Nelno down.
///////////////////////////////////////////////////////////////////////////////

void	vt_ViewHierarchical (userEntity_t *self)
{
	playerHook_t	*hook;
	char			name [256];
	userEntity_t	*vm;

	if (gstate->deathmatch)
	{
		gstate->RemoveEntity (self);
		return;
	}

	//	print usage for numbnuts
	if (gstate->GetArgc () != 2)
	{
		gstate->Con_Printf ("  Views a hierarchical model.\n");
		gstate->Con_Printf ("  USAGE: vh [model prefix]\n");
		return;
	}

	vt_Stop (self);

	//	find viewthing entity
	for (vm = gstate->FirstEntity (); vm && stricmp (vm->className, "viewthing"); vm = gstate->NextEntity (vm));

	if (!vm)
	{
		gstate->Con_Printf ("No viewthing on map.\n");
		return;
	}

	//	Nelno:	do this for each viewthing...
	//	Nelno:	set up hook for each viewthing...

	for (vm = gstate->FirstEntity (); vm; vm = gstate->NextEntity (vm))
	{
		if (!stricmp (vm->className, "viewthing"))
		{
			hook = (playerHook_t *) vm->userHook;
	
			//	spawn entities if not already viewing a 
			if (vt_IsHierarchical)
			{
				//	unhook all parts
				gstate->hr_RemoveChildren (vm, false);
				gstate->hr_RemoveChildren (hook->legs, false);
				gstate->hr_RemoveChildren (hook->torso, false);
			}

			vt_HierarchyOn (vm);

			if (!stricmp (gstate->GetArgv (1), "h"))
				hook->type = TYPE_CLIENT;
			else if (!stricmp (gstate->GetArgv (1), "mk"))
				hook->type = TYPE_MIKIKO;
			else if (!stricmp (gstate->GetArgv (1), "sf"))
				hook->type = TYPE_SUPERFLY;
			else
			{
				gstate->Con_Printf ("Unknown prefix, assuming TYPE_CLIENT.\nHead height may be off.");
				hook->type = TYPE_CLIENT;
			}

			//	Nelno:	skip drawing weapon for now...

			//	force loading of all parts
			sprintf (vt_HeadName, "models/%s_head/%s_head.md2", gstate->GetArgv (1), gstate->GetArgv (1));
			gstate->ForceModelLoad (hook->head->s.modelindex, vt_HeadName);
			hook->head->modelName = vt_HeadName;

			sprintf (vt_TorsoName, "models/%s_torso/%s_torso.md2", gstate->GetArgv (1), gstate->GetArgv (1));
			gstate->ForceModelLoad (hook->torso->s.modelindex, vt_TorsoName);
			hook->torso->modelName = vt_TorsoName;

			sprintf (vt_LegsName, "models/%s_legs/%s_legs.md2", gstate->GetArgv (1), gstate->GetArgv (1));
			gstate->ForceModelLoad (hook->legs->s.modelindex, vt_LegsName);
			hook->legs->modelName = vt_LegsName;

			hr_init_model_offsets (vm, hook->legs, hook->torso, hook->head, hook->weapon);
		}
	}

	hook = (playerHook_t *) vt->userHook;

	vt_GetSequences (hook->weapon, &vt_WeaponHeader);
	vt_GetSequences (hook->head, &vt_HeadHeader);
	vt_GetSequences (hook->torso, &vt_TorsoHeader);
	vt_GetSequences (hook->legs, &vt_LegsHeader);

	vt_IsHierarchical = true;
}