#include	<time.h>

#include	"world.h"

#include	"ai.h"
#include	"ai_utils.h"
#include	"ai_move.h"
#include	"ai_weapons.h"
#include	"ai_frames.h"

#include	"skat.h"
#include	"nodelist.h"
#include	"client.h"
#include	"bodylist.h"
#include	"actorlist.h"
#include	"light.h"
#include	"chasecam.h"
#include	"bot.h"
#include	"thinkFuncs.h"
#include	"viewthing.h"

#include	"epairs.h"

//	dll interface crap
#include	"dk_dll.h"

//	just for seeding random number generator on worldspawn
#include	"time.h"

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

DllExport	void	worldspawn	(userEntity_t *self);
DllExport	void	*dll_Query	(void);
DllExport	int		dll_Entry (HINSTANCE hParent, DWORD dwReasonForCall, PVOID pvData);

///////////////////////////////////////////////////////////////////////////////
//	external globals
///////////////////////////////////////////////////////////////////////////////

char			*global_spawntarget;	// targetname to spawn at
Vector			forward, right, up;
vec3_t			zero_vector = {0, 0, 0};
userTrace_t		trace;
trace_t			tr;
serverState_t	*gstate;
common_export_t	*com;

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

static	unsigned long	world_thinks	= 0;	//	number of World_Prethinks called since level spawned
char	*dll_Description = "WORLD.DLL.  Client routines, entity routines and AI.\n";

///////////////////////////////////////////////////////////////////////////////
//	prototypes for hell
///////////////////////////////////////////////////////////////////////////////

void	drop_monster (userEntity_t *self);
void	drop_frame (userEntity_t *self);
void	drop_rotate (userEntity_t *self);
void	drop_select (userEntity_t *self);
void	drop_move (userEntity_t *self);
void	drop_remove (userEntity_t *self);
void	drop_deco (userEntity_t *self);
void	drop_scale (userEntity_t *self);
void	drop_gravity (userEntity_t *self);
void	drop_set (userEntity_t *self);
void	drop_restore (userEntity_t *self);
void	drop_sprite (userEntity_t *self);
void	drop_movez (userEntity_t *self);
void	drop_help (userEntity_t *self);

void	menu_start (userEntity_t *self);
void	menu_quit (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////
//	DllMain
//
//	standard DLL startup/exit routine.  We don't have to do anything in here
//	because Daikatana calls dll_Entry at appropriate times.
///////////////////////////////////////////////////////////////////////////////

BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////
//	various testing and development functions
/////////////////////////////////////////////////////////////////////////

void	testsprite (userEntity_t *self)
{
	userEntity_t *temp;

	temp = gstate->SpawnEntity ();

	temp->solid = SOLID_NOT;
	temp->movetype = MOVETYPE_NONE;

	gstate->SetModel (temp, "models/testfire.spr");
	gstate->SetSize (temp, 0, 0, 0, 0, 0, 0);
	gstate->SetOrigin2 (temp, self->s.origin);
}

/////////////////////////////////////////////
// stop
/////////////////////////////////////////////

void	stop_entity (userEntity_t *self)
{
	int					argc, count = 0;
	char				*name;
	char				other_name [256];
	userEntity_t		*head;
	int					freeze_all = false;

	argc = gstate->GetArgc();

	if ((argc <= 1) || (argc > 2))
	{
		gstate->bprint ("usage: freeze [classname]\n");
		return;
	}

	name = gstate->GetArgv (1);

	if (!stricmp (gstate->GetArgv (1), "all"))
	{
		freeze_all = true;
		strcpy (other_name, "ass");
	}
	else
	{
		strcpy (other_name, "monster_");
		strcat (other_name, name);
	}

	head = gstate->FirstEntity ();

	while (head)
	{
		if (head->className)
		{
			if ( (freeze_all && head->flags & (FL_MONSTER | FL_BOT)) || 
				(!stricmp (head->className, name) || !stricmp (head->className, other_name)) )
			{
				head->prethink = NULL;
				head->think = NULL;
				head->postthink = NULL;
				head->pain = NULL;
				head->die = NULL;
				head->use = NULL;

				head->movetype = MOVETYPE_NONE;

				com->ZeroVector (head->velocity);

				count++;
			}
		}

		head = gstate->NextEntity (head);
	}

	if (freeze_all)
		gstate->Con_Printf ("Froze %i entities.\n", count);
	else if (count == 1)
		gstate->Con_Printf ("Froze %i %s.\n", count, name);
	else if (count > 1)
		gstate->Con_Printf ("Froze %i %ss.\n", count, name);
	else
		gstate->Con_Printf ("No %s or %s entities to freeze.\n", name, other_name);
}

////////////////////////////////////////////
// massacre
////////////////////////////////////////////

void	massacre (userEntity_t *self)
{
	userEntity_t *head;

	head = gstate->FirstEntity ();

	while (head)
	{
		if (head->flags & FL_MONSTER || head->flags & FL_BOT)
			gstate->RemoveEntity (head);

		head = gstate->NextEntity (head);
	}
}

////////////////////////////////////////////
// node_move
////////////////////////////////////////////

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

	com->CopyVector (hook->node_list->cur_node->origin, self->s.origin);
	if (hook->node_list->cur_node->marker)
		com->CopyVector (hook->node_list->cur_node->marker->s.origin, self->s.origin);
}

////////////////////////////////////////////
// node_save
////////////////////////////////////////////

void	node_save (userEntity_t *self)
{
	if (client_nodes)
		node_writelist (client_nodes);
	else
		gstate->bprint ("No nodes to write.\n");
}

////////////////////////////////////////////
// node_load
////////////////////////////////////////////

void	node_load (userEntity_t *self)
{
	userEntity_t	*head;
	playerHook_t	*hook;

//	hook = (playerHook_t *) self->userHook;
	
	if (client_nodes)
	{
		// remove the current nodelist and header
		node_delete_nodelist (&client_nodes);
	}

	client_nodes = node_create_header ();

	// create and load in the new list
	node_readlist (&client_nodes);

	// reassign each entity's node_list to the newly created list
	head = gstate->FirstEntity ();
	while (head)
	{
		if (head->flags & (FL_CLIENT + FL_BOT + FL_MONSTER))
		{
//			gstate->Con_Printf ("------------------------------------\nresetting nodes for %s\n", head->className);

			hook = (playerHook_t *) head->userHook;

			//	free the old node list for this entity
			if (hook->node_list)
			{
				com->free (hook->node_list);
			}

			hook->node_list = node_create_nodelist (client_nodes);
			//	make sure cur_node and last_node get reset!
			node_place_spawn_node (head, hook->node_list);
		}
		head = gstate->NextEntity (head);
	}

//	hook = (playerHook_t *) self->userHook;
//	gstate->Con_Printf ("self at node #%i\n", hook->node_list->cur_node->number);
}

////////////////////////////////////////////
// pathto
////////////////////////////////////////////

void	pathto (userEntity_t *self)
{
	mapNode_t		*node;
	playerHook_t	*hook = (playerHook_t *) self->userHook;
	int				len, argc;
	char			*name;

	argc = gstate->GetArgc();

	if ((argc <= 1) || (argc > 2))
	{
		gstate->bprint ("usage: pathto [classname]\n");
		return;
	}

	name = gstate->GetArgv (1);

	node = skat_find_item (self, name);

	if (node)
		path_shortest (self, hook->node_list, hook->node_list->cur_node, node, len);
	else
		gstate->bprint ("could not find %s\n", name);
}

////////////////////////////////////////////
//	bot_spawn_bot
////////////////////////////////////////////

void	bot_spawn_bot (userEntity_t *self)
{
	bot_spawn (self);
}

////////////////////////////////////////////
//	next_frame
////////////////////////////////////////////

void	next_frame (userEntity_t *self)
{
	userEntity_t *torso;

	torso = self->hchild->hchild;
	torso->s.frame++;
	if (torso->s.frame > 19)
		torso->s.frame = 0;
}

////////////////////////////////////////////
//	next_frame_legs
////////////////////////////////////////////

void	next_frame_legs (userEntity_t *self)
{
	userEntity_t *legs;

	legs = self->hchild;
	legs->s.frame++;
	if (legs->s.frame > 2)
		legs->s.frame = 0;
}

////////////////////////////////////////////
//	next_weapon
////////////////////////////////////////////

void	next_weapon (userEntity_t *self)
{
	userEntity_t *weapon;

	weapon = self->hchild->hchild->hchild->hchild;
	if (!stricmp (weapon->modelName, "models/katana.mdl"))
		gstate->SetModel (weapon, "models/pistol.mdl");
	else
		gstate->SetModel (weapon, "models/katana.mdl");
}

////////////////////////////////////////////
//	ledge
////////////////////////////////////////////

void	ledge (userEntity_t *self)
{
	Vector			temp;
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	temp = Vector (self->s.angles);
	temp.x = 0;
	temp.AngleVectors (forward, right, up);
	hook->max_jump_dist = 256;

//	ai_find_ledge_list	(self, 32.0);

	ai_jump_obstructed (self, 240, 270.0);
}

////////////////////////////////////////////
//	show_terrain
////////////////////////////////////////////

void	show_terrain (userEntity_t *self)
{
	Vector			temp;
	playerHook_t	*hook = (playerHook_t *) self->userHook;

	temp = Vector (self->client->v_angle);
	temp.x = temp.z = 0;

	temp.AngleVectors (forward, right, up);

	ai_terrain_type (self, forward, 32.0);
}

////////////////////////////////////////////
//	cam_force_attack
////////////////////////////////////////////

void	cam_force_attack (userEntity_t *self)
{
	playerHook_t	*hook;
	
	if (!cam_debug_target)
		return;

	hook = (playerHook_t *) cam_debug_target->userHook;

	ai_set_think (cam_debug_target, hook->begin_attack, "cam_force_attack");
}

////////////////////////////////////////////
//	cam_force_ambient
////////////////////////////////////////////

void	cam_force_ambient (userEntity_t *self)
{
	playerHook_t	*hook;
	
	if (!cam_debug_target)
		return;

	hook = (playerHook_t *) cam_debug_target->userHook;

	if (hook->th_state != TH_STAND)
		return;

	gstate->Con_Printf ("forcing ambient\n");
	ai_begin_transition (cam_debug_target, hook->frames->stand [1], SEQUENCE_STAND, 1, hook->begin_stand);
//	ai_init_sequence (cam_debug_target, hook->frames->stand [1], SEQUENCE_STAND, 1);
//	hook->frame_flags = FRAME_ONCE;
}

///////////////////////////////////////////////////////////////////////////////
//	fov_change
//
///////////////////////////////////////////////////////////////////////////////

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

	if (hook->fov_desired == 30)
		hook->fov_desired = 170;
	else
		hook->fov_desired = 30;

	hook->fov_rate = 90.0;
	hook->fov_last_change = gstate->Sys_Milliseconds ();
}

///////////////////////////////////////////////////////////////////////////////
//	test_camera
//
///////////////////////////////////////////////////////////////////////////////

void	test_camera (userEntity_t *self)
{
	if (self->flags & FL_CAMERAMODE)
	{
		self->flags -= FL_CAMERAMODE;
		gstate->Con_Printf ("FL_CAMERAMODE OFF.\n");
	}
	else
	{
		self->flags |= FL_CAMERAMODE;
		gstate->Con_Printf ("FL_CAMERAMODE ON.\n");
	}
}

/*-----------------------------------------------------------------------------
	DLL initialization/deinitialization functions
-------------------------------------------------------------------------------*/

///////////////////////////////////////////////////////////////////////////////
//	dll_ServerLoad
//
//	called when Daikatana first loads this DLL
//
//	NO ROUTINES FROM COMMON ARE AVAILABLE HERE!!
///////////////////////////////////////////////////////////////////////////////

void	dll_ServerLoad (serverState_t *state)
{
	gstate = state;

	gstate->Con_Printf ("* Loaded WORLD.DLL, %i.\n", sizeof (playerHook_t));

	// add spawn command
	gstate->AddCommand ("llama", Client_LlamaCheat);
	gstate->AddCommand ("rampage", Client_RampageCheat);
	gstate->AddCommand ("health", Client_HealthCheat);
	gstate->AddCommand ("showstats", Client_ShowStats);
	gstate->AddCommand ("notarget", Client_NoTarget);
	gstate->AddCommand ("use", Client_Use);

	gstate->AddCommand ("freeze", stop_entity);
	gstate->AddCommand ("massacre", massacre);

	gstate->AddCommand ("cam_prevmon", camera_prev_monster);
	gstate->AddCommand ("cam_nextmon", camera_next_monster);
	gstate->AddCommand ("cam_toggle", camera_toggle);
	gstate->AddCommand ("cam_debug", camera_debug);
	gstate->AddCommand ("cam_toggle_possession", camera_toggle_possession);
	gstate->AddCommand ("cam_toggle_gravity", camera_toggle_gravity);
	gstate->AddCommand ("cam_toggle_debug", camera_toggle_debug_target);
	gstate->AddCommand ("cam_set_debug", camera_set_debug_target);
	gstate->AddCommand ("cam_kill", camera_kill_target);
	gstate->AddCommand ("cam_lock", camera_lock);
	gstate->AddCommand ("cam_force_enemy", camera_force_enemy);

	gstate->AddCommand ("cam_force_attack", cam_force_attack);
	gstate->AddCommand ("cam_force_ambient", cam_force_ambient);

	gstate->AddCommand ("skat_show", skat_show_list);

	gstate->AddCommand ("node_links", node_show_links);
	gstate->AddCommand ("node_toggle", node_toggle_nodes);
	gstate->AddCommand ("node_save", node_save);
	gstate->AddCommand ("node_load", node_load);
	gstate->AddCommand ("node_move", node_move);

	gstate->AddCommand ("next_frame", next_frame);
	gstate->AddCommand ("next_frame_legs", next_frame_legs);
	gstate->AddCommand ("next_weapon", next_weapon);

	gstate->AddCommand ("ai_bot", bot_spawn_bot);
	gstate->AddCommand ("ai_ledge", ledge);
	gstate->AddCommand ("ai_terrain_type", show_terrain);

	//	Viewthing commands, some duplicated to remain compatible with Q1 viewthing commands
	gstate->AddCommand ("vs", vt_ViewSequence);
	gstate->AddCommand ("viewseq", vt_ViewSequence);
	gstate->AddCommand ("vstop", vt_Stop);
	gstate->AddCommand ("vf", vt_ViewFrame);
	gstate->AddCommand ("viewframe", vt_ViewFrame);
	gstate->AddCommand ("vl", vt_ListSequences);
	gstate->AddCommand ("listseq", vt_ListSequences);
	gstate->AddCommand ("vm", vt_ViewModel);
	gstate->AddCommand ("viewmodel", vt_ViewModel);
	gstate->AddCommand ("vr", vt_Rotate);
	gstate->AddCommand ("vscale", vt_ScaleModel);
	gstate->AddCommand ("vh", vt_ViewHierarchical);

	gstate->AddCommand ("fov_change", fov_change);

	gstate->AddCommand ("test_camera", test_camera);

	gstate->AddCommand ("drop_monster", drop_monster);
	gstate->AddCommand ("drop_rotate", drop_rotate);
	gstate->AddCommand ("drop_select", drop_select);
	gstate->AddCommand ("drop_move", drop_move);
	gstate->AddCommand ("drop_frame", drop_frame);
	gstate->AddCommand ("drop_remove", drop_remove);
	gstate->AddCommand ("drop_deco", drop_deco);
	gstate->AddCommand ("drop_scale", drop_scale);
	gstate->AddCommand ("drop_gravity", drop_gravity);
	gstate->AddCommand ("drop_set", drop_set);
	gstate->AddCommand ("drop_restore", drop_restore);
	gstate->AddCommand ("drop_sprite", drop_sprite);
	gstate->AddCommand ("drop_movez", drop_movez);
	gstate->AddCommand ("drop_help", drop_help);

	gstate->AddCommand ("menu_quit", menu_quit);
	gstate->AddCommand ("menu_start", menu_start);

	ai_init_weapons ();
}

///////////////////////////////////////////////////////////////////////////////
// dll_Version
//
// returns TRUE if size == IONSTORM_DLL_INTERFACE_VERSION
///////////////////////////////////////////////////////////////////////////////

int dll_Version	(int size)
{
	if (size == IONSTORM_DLL_INTERFACE_VERSION)
		return	TRUE;
	else
		return	FALSE;
}

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

void dll_LevelLoad (void)
{
	gstate->Con_Printf ("WORLD.DLL: dll_LevelLoad\n");

	//	init actor lists
	actor_list = alist_init ();
	client_list = alist_init ();
	monster_list = alist_init ();
	bot_list = alist_init ();
	client_bot_list = alist_init ();
	monster_bot_list = alist_init ();
}

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

void	dll_ServerInit (void)
{
	com = (common_export_t *) gstate->common_exports;	

	ai_init_weapons ();
}

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

void	dll_ServerKill (void)
{
}

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

void	dll_LevelExit (void)
{
	thinkFunc_remove_thinks ();
}

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

void *dll_Query(void)
{
	return(eclassStr);
}

///////////////////////////////////////////////////////////////////////////////
//	dll_Entry
//
//
///////////////////////////////////////////////////////////////////////////////

int	dll_Entry (HINSTANCE hParent, DWORD dwReasonForCall, PVOID pvData)
{
	int			size;

	switch (dwReasonForCall)
	{
		case	QDLL_VERSION:
			size = *(int *) pvData;

			return	dll_Version (size);
			break;
		
		case 	QDLL_QUERY:
			* (userEpair_t **) pvData = eclassStr;

			return	TRUE;
			break;

		case	QDLL_DESCRIPTION:
			* (char **) pvData = dll_Description;

			return	TRUE;
			break;

		case	QDLL_SERVER_INIT:
			dll_ServerInit ();

			return	TRUE;
			break;

		case	QDLL_SERVER_KILL:
			dll_ServerKill ();

			return	TRUE;
			break;

		case	QDLL_SERVER_LOAD:
			dll_ServerLoad ((serverState_t *) pvData);

			return	TRUE;
			break;

		case	QDLL_LEVEL_LOAD:
			dll_LevelLoad ();

			return	TRUE;
			break;

		case	QDLL_LEVEL_EXIT:
			dll_LevelExit ();

			return	TRUE;
			break;
	}

	return	FALSE;
}

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

static	unsigned long	basetime = 0;
static	unsigned long	asstime = 0;
static	unsigned long	newtime = 0;
/*
int Sys_Milliseconds (void)
{
	if (!basetime)
		basetime = timeGetTime ();
	
	newtime = timeGetTime ();

	asstime = newtime - basetime;
	basetime = newtime;

	return asstime;
}
*/
///////////////////////////////////////////////////////////////////////////////
//	worldspawn_prethink
//
//	this is the first think function called after everything has been spawned
//	it is used to build the actorlist for clients, bots and monsters and
//	to run thinkFuncs, which are basically fake entities whose sole purpose
//	is to call a function at specific intervals
///////////////////////////////////////////////////////////////////////////////

void worldspawn_prethink (userEntity_t *self)
{
	if (world_thinks == 0)
	{
		//	do startup stuff for a level
//		gstate->Con_Printf ("-----------------------\nworldspawn_prethink called!\n-----------------------\n");

		//	build actor lists now, except for client_list, because clients
		//	haven't been connected yet
		alist_build (actor_list, FL_CLIENT + FL_BOT + FL_MONSTER);
		alist_build (monster_list, FL_MONSTER);
		alist_build (bot_list, FL_BOT);
		alist_build (monster_bot_list, FL_BOT + FL_MONSTER);
		alist_build (client_bot_list, FL_CLIENT + FL_BOT);

		//	load up node files if they exist
		//	FIXME:	only for demo, 5/21/98
		if (!nodes_in_map)
			node_load (self);

//		node_restore_links (self);

		thinkFunc_init ();

		//	seed random number generator
		srand ((unsigned)time (NULL));
	}

	/////////////////////////////////////////////////////////
	//	handle think_funcs which is a list of pointers
	//	to functions that need to be called each frame
	//
	//	this allows us to have thinks without actually
	//	spawning a new entity
	/////////////////////////////////////////////////////////

	thinkFunc_run_thinks ();

	world_thinks++;
}

///////////////////////////////////////////////////////////////////////////////
//	WorldSpam
//
//	Spams necessary world objects and caches models, sounds, etc.
///////////////////////////////////////////////////////////////////////////////

void worldspawn (userEntity_t *self)
{
	int	i;

	//	this crap needs to be done in the fucking physics, you moron fucking id fucks!!!!!!
	self->movetype = MOVETYPE_PUSH;
	self->solid = SOLID_BSP;
	self->inuse = true;			// since the world doesn't use G_Spawn()
	self->s.modelindex = 1;		// world model is always index 1

	//	seed random number generator with time
	srand	((unsigned)time (NULL));

	gstate->foglevel = 0;

	i = 0;

	while (self->epair [i].key != NULL)
	{
		if (!stricmp (self->epair [i].key, "fog_value"))
		{
			gstate->foglevel = atoi (self->epair [i].value);
		}
		else if (!stricmp (self->epair [i].key, "cdtrack"))
		{
			if (!gstate->GetCvar ("developer"))
			{
				self->sounds = atof (self->epair [i].value);
				gstate->cdtrack = self->sounds;

				gstate->ConfigString (CS_CDTRACK, self->epair [i].value);
			}
		}
		else if (!stricmp (self->epair [i].key, "sky"))
			gstate->ConfigString (CS_SKY, self->epair [i].value);
		else if (!stricmp (self->epair [i].key, "skyaxis"))
			gstate->ConfigString (CS_SKYAXIS, self->epair [i].value);
		else if (!stricmp (self->epair [i].key, "skyrotate"))
			gstate->ConfigString (CS_SKYROTATE, self->epair [i].value);
		else if (!stricmp (self->epair [i].key, "palette"))
			gstate->ConfigString (CS_PALETTE, self->epair [i].value);

		i++;
	}

	self->className = "worldspawn";

	//	don't let mappers accidentally put an angle in the world spawn...
	//	causes world clipping to be totally screwed (in Q1, at least).
	com->ZeroVector (self->s.angles);

	///////////////////////////////////////////////////
	//	hiro models
	///////////////////////////////////////////////////
	
	gstate->ModelIndex ("models/h_head/h_head.md2");
	gstate->ModelIndex ("models/h_torso/h_torso.md2");
	gstate->ModelIndex ("models/h_legs/h_legs.md2");

	///////////////////////////////////////////////////
	//	mikiko models
	///////////////////////////////////////////////////
	
	gstate->ModelIndex ("models/mk_head/mk_head.md2");
	gstate->ModelIndex ("models/mk_torso/mk_torso.md2");
	gstate->ModelIndex ("models/mk_legs/mk_legs.md2");

	///////////////////////////////////////////////////
	//	Superfly models
	///////////////////////////////////////////////////
	
	gstate->ModelIndex ("models/sf_head/sf_head.md2");
	gstate->ModelIndex ("models/sf_torso/sf_torso.md2");
	gstate->ModelIndex ("models/sf_legs/sf_legs.md2");

	///////////////////////////////////////////////////
	//	Hand-held weapon models
	///////////////////////////////////////////////////

	gstate->ModelIndex ("models/katana/katana.md2");
	gstate->ModelIndex ("models/pistol/pistol.md2");
	gstate->ModelIndex ("models/shotgun/shotgun.md2");

	///////////////////////////////////////////////////
	// monster weapons
	///////////////////////////////////////////////////

	gstate->ModelIndex ("models/rocket/rocket.md2");
//	gstate->Precache_Model ("models/muzflash.mdl");

	///////////////////////////////////////////////////
	// gibs
	///////////////////////////////////////////////////

	gstate->ModelIndex (gib_names [0]);
	gstate->ModelIndex (gib_names [1]);
	gstate->ModelIndex (gib_names [2]);
	gstate->ModelIndex (gib_names [3]);
	gstate->ModelIndex (gib_names [4]);

	gstate->ModelIndex ("models/gibs/glass1/glass1.md2");
	gstate->ModelIndex ("models/gibs/glass2/glass2.md2");
	gstate->ModelIndex ("models/gibs/wood1/wood1.md2");
	gstate->ModelIndex ("models/gibs/wood2/wood2.md2");
	gstate->ModelIndex ("models/gibs/metal1/metal1.md2");
	gstate->ModelIndex ("models/gibs/metal2/metal2.md2");

	gstate->ModelIndex ("models/gibs/rock1/rock1.md2");
	gstate->ModelIndex ("models/gibs/rock2/rock2.md2");
	gstate->ModelIndex ("models/gibs/rock3/rock3.md2");

	gstate->ModelIndex ("models/satyr/satyr.md2");

	///////////////////////////////////////////////////
	// artifacts/items
	///////////////////////////////////////////////////

//	gstate->Precache_Model("models/armor.mdl");	

	// sprites
	gstate->Precache_Model("models/explo.spr");

	// temp models
	gstate->ModelIndex ("models/testbutton/testbutton.md2");

	/////////////////////////////////////////////////////////
	// ai and node system models
	/////////////////////////////////////////////////////////
	gstate->ModelIndex ("models/debug/goal/goal.md2");
	gstate->ModelIndex ("models/debug/lastnode/lastnode.md2");
	gstate->ModelIndex ("models/debug/curnode/curnode.md2");
	gstate->ModelIndex ("models/debug/node/node.md2");
	gstate->ModelIndex ("models/debug/marker/marker.md2");
/*
	gstate->Precache_Model ("models/debug/node.mdl");
	gstate->Precache_Model ("models/debug/curnode.mdl");
	gstate->Precache_Model ("models/debug/lastnode.mdl");
	gstate->Precache_Model ("models/debug/pathnode.mdl");
	gstate->Precache_Model ("models/debug/jumptarg.mdl");
	gstate->Precache_Model ("models/debug/linkto.mdl");
	gstate->Precache_Model ("models/debug/linkfrom.mdl");
*/
	////////////////////////////////////////////////////////
	// sound precaches
	////////////////////////////////////////////////////////

	// warping sounds
	gstate->Precache_Sound ("misc/warpstart.wav");
	gstate->Precache_Sound ("misc/warpend2.wav");

	// precache sounds played in Daikatana engine
	gstate->Precache_Sound ("engine/waterhit.wav");
	gstate->Precache_Sound ("engine/land.wav");
	gstate->Precache_Sound ("explosions/explode1.wav");
	gstate->Precache_Sound ("explosions/explode2.wav");

	// Daikatana client sounds
	gstate->Precache_Sound ("player/playerexitwater.wav");
	gstate->Precache_Sound ("player/playerjump.wav");
	gstate->Precache_Sound ("player/playerlandinwater.wav");
	gstate->Precache_Sound ("player/playerswim1.wav");
	gstate->Precache_Sound ("player/playerbreathe.wav");
	gstate->Precache_Sound ("player/playerbreathe2.wav");
	gstate->Precache_Sound ("player/playerchoke1.wav");
	gstate->Precache_Sound ("player/playerchoke3.wav");
	gstate->Precache_Sound ("player/playerjump5.wav");
	gstate->Precache_Sound ("player/playerjump6.wav");
	gstate->Precache_Sound ("player/playerjump7.wav");
	gstate->Precache_Sound ("player/playerland1.wav");
	gstate->Precache_Sound ("player/playerland2.wav");
	gstate->Precache_Sound ("player/playerland3.wav");
	gstate->Precache_Sound ("player/playerland4.wav");
	gstate->Precache_Sound ("player/playerlandhurt.wav");
	gstate->Precache_Sound ("player/playerlandhurt2.wav");
	gstate->Precache_Sound ("player/playerdeath1.wav");
	gstate->Precache_Sound ("player/playerdeath2.wav");
	gstate->Precache_Sound ("player/playerdeath3.wav");
	gstate->Precache_Sound ("player/playerdeath4.wav");
	gstate->Precache_Sound ("player/playerpain1.wav");
	gstate->Precache_Sound ("player/playerpain2.wav");
	gstate->Precache_Sound ("player/playerpain3.wav");
	gstate->Precache_Sound ("player/playerpain4.wav");
	gstate->Precache_Sound ("player/playerwaterdeath.wav");
	gstate->Precache_Sound ("player/playerwaterchoke1.wav");
	gstate->Precache_Sound ("player/playerland1.wav");

	// Superfly sounds
	gstate->Precache_Sound ("superfly/superflypain1.wav");
	gstate->Precache_Sound ("superfly/superflypain2.wav");
	gstate->Precache_Sound ("superfly/superflypain3.wav");
	gstate->Precache_Sound ("superfly/superflypain4.wav");
	gstate->Precache_Sound ("superfly/superflypain5.wav");
	gstate->Precache_Sound ("superfly/superflypain6.wav");
	gstate->Precache_Sound ("superfly/superflypain7.wav");
	gstate->Precache_Sound ("superfly/superflypain8.wav");
	gstate->Precache_Sound ("superfly/superflypain9.wav");
	gstate->Precache_Sound ("superfly/superflydeath1.wav");
	gstate->Precache_Sound ("superfly/superflydeath2.wav");
	gstate->Precache_Sound ("superfly/superflydeath3.wav");
	gstate->Precache_Sound ("superfly/superflydeath4.wav");
	gstate->Precache_Sound ("superfly/superflydrown.wav");
	gstate->Precache_Sound ("superfly/superflygasp1.wav");
	gstate->Precache_Sound ("superfly/superflygasp2.wav");
	gstate->Precache_Sound ("superfly/superflykill1.wav");
	gstate->Precache_Sound ("superfly/superflykill2.wav");
	gstate->Precache_Sound ("superfly/superflychoke.wav");
	gstate->Precache_Sound ("superfly/superflyfire.wav");

	// Mikiko sounds
	gstate->Precache_Sound ("mikiko/mikikochoke.wav");
	gstate->Precache_Sound ("mikiko/mikikodeath1.wav");
	gstate->Precache_Sound ("mikiko/mikikodeath2.wav");
	gstate->Precache_Sound ("mikiko/mikikodeath3.wav");
	gstate->Precache_Sound ("mikiko/mikikodeath4.wav");
	gstate->Precache_Sound ("mikiko/mikikodeath5.wav");
	gstate->Precache_Sound ("mikiko/mikikodrown.wav");
	gstate->Precache_Sound ("mikiko/mikikogasp1.wav");
	gstate->Precache_Sound ("mikiko/mikikogasp2.wav");
	gstate->Precache_Sound ("mikiko/mikikokill1.wav");
	gstate->Precache_Sound ("mikiko/mikikokill2.wav");
	gstate->Precache_Sound ("mikiko/mikikokill3.wav");	
	gstate->Precache_Sound ("mikiko/mikikopain1.wav");
	gstate->Precache_Sound ("mikiko/mikikopain2.wav");
	gstate->Precache_Sound ("mikiko/mikikopain3.wav");
	gstate->Precache_Sound ("mikiko/mikikopain4.wav");
	gstate->Precache_Sound ("mikiko/mikikopain5.wav");
	gstate->Precache_Sound ("mikiko/mikikopain6.wav");

	// item pickup sounds
	gstate->Precache_Sound ("items/ammopickup.wav");
	gstate->Precache_Sound ("items/weaponpickup.wav");
	gstate->Precache_Sound ("items/itempickup1.wav");

	gstate->SoundIndex ("misc/debris1.wav");
	gstate->SoundIndex ("misc/debris2.wav");

	// artifact sounds
/*
	gstate->Precache_Sound ("artifacts/megawait.wav");
	gstate->Precache_Sound ("artifacts/megapick.wav");
	gstate->Precache_Sound ("artifacts/megafade.wav");
*/
	///////////////////////////////////////////////////////
	//	set up light styles
	///////////////////////////////////////////////////////

	light_setstyles ();
	// set up node mapping for clients/bots/monsters
	client_nodes = node_create_header ();

	// set up circular body list
	bodylist_init (self);

	// init client count
	cur_clients = 0;
	world_thinks = 0;
	nodes_in_map = 0;

	self->prethink = worldspawn_prethink;
}

