/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// client.h

#include "particles.h"

enum
{
	SKIN_NOTLOADED,	//not trying to load it. shouldn't really happen, but can.
	SKIN_LOADING,	//still loading. just do something else for now...
	SKIN_LOADED,
	SKIN_FAILED
};
typedef struct qwskin_s
{
	char		name[64];
	int			loadstate;		// the name isn't a valid skin

	//qw skin info
	int			width;
	int			height;
	void		*skindata;

	//for hardware 32bit texture overrides
	texnums_t	textures;
} qwskin_t;

// player_state_t is the information needed by a player entity
// to do move prediction and to generate a drawable entity
typedef struct
{
	int			messagenum;		// all player's won't be updated each frame

	double		state_time;		// not the same as the packet time,
								// because player commands come asyncronously
	usercmd_t	command;		// last command for prediction

	vec3_t		origin;
	vec3_t		predorigin;		// pre-predicted pos
	vec3_t		viewangles;		// only for demos, not from server
	vec3_t		velocity;
	int			weaponframe;

	unsigned int			modelindex;
	int			frame;
	int			skinnum;
	int			effects;

#ifdef PEXT_SCALE
	float scale;
#endif
	qbyte colourmod[3];
	qbyte alpha;
#ifdef PEXT_FATNESS
	float fatness;
#endif

	int			flags;			// dead, gib, etc

	int			pm_type;
	float		waterjumptime;
	qboolean	onground;
	qboolean	jump_held;
	int			jump_msec;		// hack for fixing bunny-hop flickering on non-ZQuake servers
	vec3_t		szmins, szmaxs;
	vec3_t		gravitydir;

	float lerpstarttime;
	int oldframe;
} player_state_t;


#if defined(Q2CLIENT) || defined(Q2SERVER)
typedef enum
{
	// can accelerate and turn
	Q2PM_NORMAL,
	Q2PM_SPECTATOR,
	// no acceleration or turning
	Q2PM_DEAD,
	Q2PM_GIB,		// different bounding box
	Q2PM_FREEZE
} q2pmtype_t;
typedef struct
{	//shared with q2 dll

	q2pmtype_t	pm_type;

#if 0
	int		origin[3];		// 12.3
#else
	short		origin[3];		// 20.3
#endif
	short		velocity[3];	// 12.3
	qbyte		pm_flags;		// ducked, jump_held, etc
	qbyte		pm_time;		// each unit = 8 ms
	short		gravity;
	short		delta_angles[3];	// add to command angles to get view direction
									// changed by spawns, rotating objects, and teleporters
//	short		pad;
} q2pmove_state_t;

typedef struct
{	//shared with q2 dll so cannot be changed

	q2pmove_state_t	pmove;		// for prediction

	// these fields do not need to be communicated bit-precise

	vec3_t		viewangles;		// for fixed views
	vec3_t		viewoffset;		// add to pmovestate->origin
	vec3_t		kick_angles;	// add to view direction to get render angles
								// set by weapon kicks, pain effects, etc

	vec3_t		gunangles;
	vec3_t		gunoffset;
	int			gunindex;
	int			gunframe;

	float		blend[4];		// rgba full screen effect

	float		fov;			// horizontal field of view

	int			rdflags;		// refdef flags

	short		stats[Q2MAX_STATS];		// fast status bar updates
} q2player_state_t;
#endif

typedef struct colourised_s {
	char name[64];
	unsigned int topcolour;
	unsigned int bottomcolour;
	char skin[64];
	struct colourised_s *next;
} colourised_t;

#define	MAX_SCOREBOARDNAME	64
#define MAX_DISPLAYEDNAME	16
typedef struct player_info_s
{
	int		userid;
	char	userinfo[EXTENDED_INFO_STRING];
	char	teamstatus[128];
	float	teamstatustime;

	// scoreboard information
	char	name[MAX_SCOREBOARDNAME];
	char	team[MAX_INFO_KEY];
	float	realentertime;	//pegged against realtime, to cope with potentially not knowing the server's time when we first receive this message
	int		frags;
	int		ping;
	qbyte	pl;

	struct
	{
		float time;	//invalid if too old.
		int health;
		int armour;
		unsigned int items;
		vec3_t org;
		char nick[8];	//kinda short, yes.
	} tinfo;

	qboolean ignored;
	qboolean vignored;

	colourised_t *colourised;

	// skin information
	unsigned int		rtopcolor;	//real, according to their userinfo
	unsigned int		rbottomcolor;

	unsigned int		ttopcolor;	//team, according to colour forcing
	unsigned int		tbottomcolor;

	int			spectator;
	qwskin_t	*qwskin;
	qwskin_t	*lastskin;	//last-known-good skin
	skinid_t	skinid;

	struct model_s	*model;

//	unsigned short vweapindex;
#ifdef HEXEN2
	unsigned char h2playerclass;
#endif

	int prevcount;

#ifdef QUAKEHUD	
	struct wstats_s 
	{
		char wname[16];
		unsigned int hit;
		unsigned int total;
	} weaponstats[16];
#endif

	int stats[MAX_CL_STATS];
	float statsf[MAX_CL_STATS];
} player_info_t;


typedef struct
{
	double		senttime;			// time cmd was sent off
	float		latency;			// the time the packet was acked. -1=choked, -2=dropped, -3=never sent, -4=reply came back invalid

	// generated on client side
	usercmd_t	cmd[MAX_SPLITS];	// cmd that generated the frame
	int			cmd_sequence;		//the outgoing move sequence. if not equal to expected, that index was stale and is no longer valid
	int			server_message_num;	//the inbound frame that was valid when this command was generated

	int server_time;
	int client_time;
} outframe_t;

typedef struct
{
	//this is the sequence we requested for this frame.
	int			delta_sequence;		// sequence number to delta from, -1 = full update

	// received from server
	int			frameid;		//the sequence number of the frame, so we can easily detect which frames are valid without poking all in advance, etc
	int			ackframe;		//the outgoing sequence this frame acked (for prediction backlerping).
	double		receivedtime;	// time message was received, or -1
	player_state_t	playerstate[MAX_CLIENTS+MAX_SPLITS];	// message received that reflects performing
								// the usercmd
	packet_entities_t	packet_entities;
	qboolean	invalid;		// true if the packet_entities delta was invalid
} inframe_t;

#ifdef Q2CLIENT
typedef struct
{
	qboolean		valid;			// cleared if delta parsing was invalid
	int				serverframe;
	int				servertime;		// server time the message is valid for (in msec)
	int				deltaframe;
	qbyte			areabits[MAX_Q2MAP_AREAS/8];		// portalarea visibility bits
	q2player_state_t	playerstate[MAX_SPLITS];
	int				clientnum[MAX_SPLITS];
	int				num_entities;
	int				parse_entities;	// non-masked index into cl_parse_entities array
} q2frame_t;
#endif

typedef struct
{
	int		destcolor[3];
	float		percent;		// 0-256
} cshift_t;

#define	CSHIFT_CONTENTS	0
#define	CSHIFT_DAMAGE	1
#define	CSHIFT_BONUS	2
#define	CSHIFT_POWERUP	3
#define CSHIFT_SERVER	4
#define	NUM_CSHIFTS		5


//
// client_state_t should hold all pieces of the client state
//
//the light array works thusly:
//dlights are allocated DL_LAST downwards to 0, static wlights are allocated DL_LAST+1 to MAX_RTLIGHTS.
//thus to clear the dlights but not rtlights, set the first light to RTL_FIRST
#define DL_LAST				(sizeof(unsigned int)*8-1)
#define RTL_FIRST			(sizeof(unsigned int)*8)

#define LFLAG_NORMALMODE	(1<<0) /*ppl with r_shadow_realtime_dlight*/
#define LFLAG_REALTIMEMODE	(1<<1) /*ppl with r_shadow_realtime_world*/
#define LFLAG_LIGHTMAP		(1<<2)
#define LFLAG_FLASHBLEND	(1<<3)

#define LFLAG_NOSHADOWS		(1<<8)
#define LFLAG_SHADOWMAP		(1<<9)
#define LFLAG_CREPUSCULAR	(1<<10)

#define LFLAG_DYNAMIC (LFLAG_LIGHTMAP | LFLAG_FLASHBLEND | LFLAG_NORMALMODE | LFLAG_REALTIMEMODE)

typedef struct dlight_s
{
	int		key;				// so entities can reuse same entry
	vec3_t	origin;
	vec3_t	axis[3];
	vec3_t	rotation;			//cubemap/spotlight rotation
	float	radius;
	float	die;				// stop lighting after this time
	float	decay;				// drop this each second
	float	minlight;			// don't add when contributing less
	float   color[3];
	float	channelfade[3];
#ifdef RTLIGHTS
	vec3_t lightcolourscales; //ambient, diffuse, specular
#endif
	float	corona;
	float	coronascale;

	unsigned int flags;
	char	cubemapname[64];

	int coronaocclusionquery;
	unsigned int coronaocclusionresult;

	//the following are used for rendering (client code should clear on create)
	qboolean rebuildcache;
	struct	shadowmesh_s *worldshadowmesh;
	texid_t cubetexture;
	struct {
		float updatetime;
	} face [6];
	int style;	//multiply by style values if > 0
	float	fov; //spotlight
	struct dlight_s *next;
} dlight_t;

typedef struct
{
	int		length;
	char	map[MAX_STYLESTRING];
	vec3_t	colours;
	int		colourkey;
} lightstyle_t;



#define	MAX_EFRAGS		512

#define	MAX_DEMOS		8
#define	MAX_DEMONAME	16

typedef enum {
ca_disconnected, 	// full screen console with no connection
ca_demostart,		// starting up a demo
ca_connected,		// netchan_t established, waiting for svc_serverdata
ca_onserver,		// processing data lists, donwloading, etc
ca_active			// everything is in, so frames can be rendered
} cactive_t;

typedef enum {
	dl_none,
	dl_model,
	dl_sound,
	dl_skin,
	dl_wad,
	dl_single,
	dl_singlestuffed
} dltype_t;		// download type

typedef struct qdownload_s
{
	enum {DL_QW, DL_QWCHUNKS, DL_Q3, DL_DARKPLACES, DL_QWPENDING, DL_HTTP, DL_FTP} method;
	vfsfile_t		*file;		// file transfer from server
	char			dclname[MAX_OSPATH];	//file to read/write the chunklist from, for download resumption.
	char			tempname[MAX_OSPATH];	//file its currently writing to.
	char			localname[MAX_OSPATH];	//file its going to be renamed to.
	int				prefixbytes;			//number of bytes that prefix the above names (ie: package/ or nothing).
	char			remotename[MAX_OSPATH];	//file its coming from.
	float			percent;				//for progress indicator.
	float			starttime;				//for speed info
	qofs_t			completedbytes;			//number of bytes downloaded, for progress/speed info
	qofs_t			size;					//total size (may be a guess)
	qboolean		sizeunknown;			//says that size is a guess
	unsigned int	filesequence;			//unique file id.
	enum fs_relative fsroot;				//where the local+temp file is meant to be relative to.

	double			ratetime;
	int				rate;
	int				ratebytes;
	unsigned int	flags;

	//chunked downloads uses this
	struct dlblock_s
	{
		qofs_t start;
		qofs_t end;
		enum
		{
			DLB_MISSING,
			DLB_PENDING,
			DLB_RECEIVED
		} state:16;
		unsigned int sequence;	//sequence is only valid on pending blocks.

		struct dlblock_s *next;
	} *dlblocks;
} qdownload_t;
enum qdlabort
{
	QDL_FAILED,		//delete file, tell server.
	QDL_DISCONNECT,	//delete file, don't tell server.
	QDL_COMPLETED,	//rename file, tell server.
};
qboolean DL_Begun(qdownload_t *dl);
void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end);	//notifies the download logic that a chunk of the file is no longer needed.
void DL_Abort(qdownload_t *dl, enum qdlabort aborttype);		//just frees the download's resources. does not delete the temp file.

//chunked downloads
void DLC_Poll(qdownload_t *dl);


//
// the client_static_t structure is persistant through an arbitrary number
// of server connections
//
typedef struct
{
// connection information
	cactive_t	state;

	/*Specifies which protocol family we're speaking*/
	enum {
		CP_UNKNOWN,
		CP_QUAKEWORLD,
		CP_NETQUAKE,
		CP_QUAKE2,
		CP_QUAKE3,
		CP_PLUGIN
	} protocol;

	/*QuakeWorld protocol flags*/
#ifdef PROTOCOLEXTENSIONS
	unsigned long fteprotocolextensions;
	unsigned long fteprotocolextensions2;
#endif
	unsigned long z_ext;

	/*NQ Protocol flags*/
	enum
	{
		CPNQ_ID,
		CPNQ_BJP1,	//16bit models, strict 8bit sounds
		CPNQ_BJP2,	//16bit models, strict 16bit sounds
		CPNQ_BJP3,	//16bit models, flagged 16bit sounds
		CPNQ_FITZ666, /*and rmqe999 protocol*/
		CPNQ_DP5,
		CPNQ_DP6,
		CPNQ_DP7
	} protocol_nq;
	#define CPNQ_IS_DP (cls.protocol_nq >= CPNQ_DP5)
	#define CPNQ_IS_BJP (cls.protocol_nq >= CPNQ_BJP1 && cls.protocol_nq <= CPNQ_BJP3)
	qboolean proquake_angles_hack;	//angles are always 16bit

	int protocol_q2;


	qboolean resendinfo;
	qboolean findtrack;

	int framecount;

	int realip_ident;
	netadr_t realserverip;

// network stuff
	netchan_t	netchan;
	float lastarbiatarypackettime;	//used to mark when packets were sent to prevent mvdsv servers from causing us to disconnect.

// private userinfo for sending to masterless servers
	char		userinfo[MAX_SPLITS][EXTENDED_INFO_STRING];

	char		servername[MAX_OSPATH];	// name of server from original connect

	struct ftenet_connections_s *sockets;

	qdownload_t *download;

// demo loop control
	int			demonum;		// -1 = don't play demos
	char		demos[MAX_DEMOS][MAX_DEMONAME];		// when not playing

// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
	vfsfile_t	*demooutfile;

	enum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD,DPB_EZTV,
#ifdef NQPROT
		DPB_NETQUAKE,
#endif
#ifdef Q2CLIENT
		DPB_QUAKE2
#endif
	}	demoplayback, demorecording;
	qboolean	demohadkeyframe;	//q2 needs to wait for a packet with a key frame, supposedly.
	qboolean	demoseeking;
	float		demoseektime;
	qboolean	timedemo;
	vfsfile_t	*demoinfile;
	struct dl_download *demoindownload; 
	float		td_lastframe;		// to meter out one message a frame
	int			td_startframe;		// host_framecount at start
	float		td_starttime;		// realtime at second frame of timedemo
	float		demostarttime;		// the time of the first frame, so we don't get weird results with qw demos

	int			challenge;

	float		latency;		// rolling average

	qboolean	allow_anyparticles;
	qboolean	allow_skyboxes;
	qboolean	allow_watervis;	//fixme: not checked any more
	float		allow_fbskins;	//fraction of allowance
	qboolean	allow_cheats;
	qboolean	allow_semicheats;	//defaults to true, but this allows a server to enforce a strict ruleset (smackdown type rules).
	qboolean	allow_csqc;			//disables some legacy/compat things, like proquake parsing.
	float		maxfps;	//server capped
	int			deathmatch;

#ifdef NQPROT
	int signon;
#endif
	int language;

	colourised_t *colourised;
} client_static_t;

extern client_static_t	cls;

typedef struct downloadlist_s {
	char rname[128];
	char localname[128];
	unsigned int size;
	unsigned int flags;
#define DLLF_VERBOSE		(1u<<0)		//tell the user that its downloading
#define DLLF_REQUIRED		(1u<<1)		//means that it won't load models etc until its downloaded (ie: requiredownloads 0 makes no difference)
#define DLLF_OVERWRITE		(1u<<2)		//overwrite it even if it already exists
#define DLLF_SIZEUNKNOWN	(1u<<3)		//download's size isn't known
#define DLLF_IGNOREFAILED	(1u<<4)		//
#define DLLF_NONGAME		(1u<<5)		//means the requested download filename+localname is gamedir explicit (so id1/foo.txt is distinct from qw/foo.txt)
#define DLLF_TEMPORARY		(1u<<6)		//download it, but don't actually save it (DLLF_OVERWRITE doesn't actually overwrite, but does ignore any local files)
#define DLLF_USEREXPLICIT	(1u<<7)		//use explicitly requested it, ignore the cl_downloads cvar.

#define DLLF_BEGUN			(1u<<8)		//server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests.
	struct downloadlist_s *next;
} downloadlist_t;


typedef struct {
	//current persistant state
	trailstate_t *trailstate;	//when to next throw out a trail
	trailstate_t *emitstate;    //when to next emit

	//current origin
	vec3_t origin;	//current render position
	vec3_t angles;

	//previous rendering frame (for trails)
	vec3_t lastorigin;
	qboolean isnew;
	qboolean isplayer;

	//intermediate values for frame lerping
	//separate upper+lower lerps
	float framelerpdeltatime[FS_COUNT];
	float newframestarttime[FS_COUNT];
	int newframe[FS_COUNT];
	float oldframestarttime[FS_COUNT];
	int oldframe[FS_COUNT];
	qbyte basebone;

	//intermediate values for origin lerping of stepping things
	int newsequence;
	float orglerpdeltatime;
	float orglerpstarttime;
	vec3_t neworigin; /*origin that we're lerping towards*/
	vec3_t oldorigin; /*origin that we're lerping away from*/
	vec3_t newangle;
	vec3_t oldangle;

	//for further info
	int skeletalobject;
	int sequence;	/*so we know if the ent is still valid*/
	entity_state_t *entstate;
} lerpents_t;

//state associated with each player 'seat' (one for each splitscreen client)
//note that this doesn't include networking inputlog info.
struct playerview_s
{
	int			playernum;		//cl.players index for this player.
	qboolean	nolocalplayer;	//inhibit use of qw-style players, predict based on entities.
#ifdef PEXT_SETVIEW
	int			viewentity;		//view is attached to this entity.
#endif

	// information for local display
	int			stats[MAX_CL_STATS];	// health, etc
	float		statsf[MAX_CL_STATS];	// health, etc
	char		*statsstr[MAX_CL_STATS];	// health, etc
	float		item_gettime[32];	// cl.time of aquiring item, for blinking
	float		faceanimtime;		// use anim frame if cl.time < this

#ifdef HEXEN2
	int			sb_hexen2_cur_item;//hexen2 hud
	float		sb_hexen2_item_time;
	qboolean	sb_hexen2_extra_info;//show the extra stuff
	qboolean	sb_hexen2_infoplaque;
#endif


// the client maintains its own idea of view angles, which are
// sent to the server each frame.  And only reset at level change
// and teleport times
	vec3_t		viewangles;			//current angles
	vec3_t		viewanglechange;	//angles set by input code this frame
	vec3_t		intermissionangles;	//absolute angles for intermission
	vec3_t		gravitydir;

	// pitch drifting vars
	float		pitchvel;
	qboolean	nodrift;
	float		driftmove;
	double		laststop;

	int gamerectknown;		//equals cls.framecount if valid
	vrect_t	gamerect;		//position the player's main view was drawn at this frame.

	//prediction state
	int			pmovetype;
	float		entgravity;
	float		maxspeed;
	vec3_t		simorg;
	vec3_t		simvel;
	vec3_t		simangles;
	float		rollangle;
	float		hdr_last;

	int			chatstate;	//1=talking, 2=afk

	float		crouch;			// local amount for smoothing stepups
	vec3_t		oldorigin;		// to track step smoothing
	float		oldz, extracrouch, crouchspeed; // to track step smoothing
	qboolean	onground;
	float		viewheight;
	int			waterlevel;		//for smartjump

#ifdef Q2CLIENT
	vec3_t predicted_origin;
	vec3_t predicted_angles;
	vec3_t prediction_error;
	float predicted_step_time;
	float predicted_step;
#endif

	float		punchangle;		// temporary view kick from weapon firing

	float		v_dmg_time;		//various view knockbacks.
	float		v_dmg_roll;
	float		v_dmg_pitch;

	double		bobtime;		//sine wave
	double		bobcltime;		//for tracking time increments
	float		bob;			//bob height


	vec3_t		cam_desired_position;	// where the camera wants to be
	int			cam_oldbuttons;			//
	vec3_t		cam_viewangles;			//
	double		cam_lastviewtime;		// timer for wallcam
	float		cam_reautotrack;		// timer to throttle tracking changes.
	int			cam_spec_track;			// player# of who we are tracking / want to track / might want to track
	enum
	{
		CAM_FREECAM	= 0,		//not attached to another player. we are our own thing (or actually playing).
		CAM_PENDING = 1,		//we want to lock on to cam_spec_track, but we don't have their position / stats yet. still freecamming
		CAM_WALLCAM = 2,		//locked, cl_chasecam=0. we're watching them from a wall.
		CAM_EYECAM	= 3			//locked, cl_chasecam=1. we know where they are, we're in their eyes.

#define CAM_ISLOCKED(pv) ((pv)->cam_state > CAM_PENDING)
	} cam_state;

	cshift_t	cshifts[NUM_CSHIFTS];	// color shifts for damage, powerups and content types
	vec4_t		screentint;

	vec3_t		vw_axis[3];	//weapons should be positioned relative to this
	vec3_t		vw_origin;	//weapons should be positioned relative to this
//	entity_t	viewent;	// is this not utterly redundant yet?
	struct
	{
		struct model_s *oldmodel;
		float lerptime;
		float oldlerptime;
		float frameduration;
		int prevframe;
		int oldframe;
	} vm;

	struct
	{
		qboolean defaulted;
		int entnum;
		vec3_t origin;
		vec3_t forward;
		vec3_t right;
		vec3_t up;
		size_t reverbtype;
		vec3_t velocity;
	} audio;
};

//
// the client_state_t structure is wiped completely at every
// server signon
//
typedef struct
{
	int			fpd;
	int			servercount;	// server identification for prespawns

	float		gamespeed;
	qboolean	csqcdebug;
	qboolean	allowsendpacket;

	qboolean	stillloading;	// set when doing something slow, and the game is still loading.

	char		serverinfo[MAX_SERVERINFO_STRING];
	char		serverpaknames[1024];
	char		serverpakcrcs[1024];
	qboolean	serverpakschanged;

	int			parsecount;		// server message counter
	int			oldparsecount;
	int			oldvalidsequence;
	int			ackedmovesequence;	//in quakeworld/q2 this is always equal to validsequence. nq can differ. may only be updated when validsequence is updated.
	int			lastackedmovesequence;	//can be higher than ackedmovesequence when the received frame was unusable.
	int			validsequence;	// this is the sequence number of the last good
								// packetentity_t we got.  If this is 0, we can't
								// render a frame yet
	int			movesequence;	// client->server frames

	int			spectator;
	int			autotrack_hint;		//the latest hint from the mod, might be negative for invalid.
	int			autotrack_killer;	//if someone kills the guy we're tracking, this is the guy we should switch to.

	double		last_ping_request;	// while showing scoreboard
	double		last_servermessage;

	//list of ent frames that still need to be acked.
	int numackframes;
	int ackframes[64];

#ifdef Q2CLIENT
	q2frame_t	q2frame;
	q2frame_t	q2frames[Q2UPDATE_BACKUP];
#endif

// sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd
	outframe_t	outframes[UPDATE_BACKUP];	//user inputs (cl.ackedmovesequence+1 to cl.movesequence are still pending)
	inframe_t	inframes[UPDATE_BACKUP];	//server state (cl.validsequence is the most recent set)
	lerpents_t	*lerpents;
	int			maxlerpents;	//number of slots allocated.
	int			lerpentssequence;
	lerpents_t	lerpplayers[MAX_CLIENTS];

	//when running splitscreen, we have multiple viewports all active at once
	int			splitclients;	//we are running this many clients split screen.
	playerview_t	playerview[MAX_SPLITS];
	int			defaultnetsplit;//which multiview splitscreen to parse the message for (set by mvd playback code)

	// localized movement vars
	float		bunnyspeedcap;

// the client simulates or interpolates movement to get these values
	double		time;			// this is the time value that the client
								// is rendering at.  always <= realtime

	float servertime;	//current server time, bound between gametime and gametimemark
	float mtime;		//server time as on the server when we last received a packet. not allowed to decrease.
	float oldmtime;		//server time as on the server for the previously received packet.

	float gametime;
	float gametimemark;
	float oldgametime;		//used as the old time to lerp cl.time from.
	float oldgametimemark;	//if it's 0, cl.time will casually increase.
	float demogametimebias;	//mvd timings are weird.

	float minpitch;
	float maxpitch;

	qboolean	paused;			// send over by server

	enum
	{
		IM_NONE,		//off.
		IM_NQSCORES,	//+showscores forced, view still attached to regular view
		IM_NQFINALE,	//slow centerprint text etc, view still attached to regular view. no hud
		IM_NQCUTSCENE,	//IM_NQFINALE, but without the 'finale' header on centerprints.
		IM_H2FINALE,	//IM_NQFINALE, but with the view offset by the player's viewheight.

		IM_QWSCORES		//intermission, view locked at a specific point
	} intermissionmode;	// don't change view angle, full screen, etc
	float		completed_time;	// latched ffrom time at intermission start

#define Q2MAX_VISIBLE_WEAPONS 32 //q2 has about 20.
	int		numq2visibleweapons;	//q2 sends out visible-on-model weapons in a wierd gender-nutral way.
	char	*q2visibleweapons[Q2MAX_VISIBLE_WEAPONS];//model names beginning with a # are considered 'sexed', and are loaded on a per-client basis. yay. :(

//
// information that is static for the entire time connected to a server
//
	char		model_name_vwep[MAX_VWEP_MODELS][MAX_QPATH];
	char		model_name[MAX_PRECACHE_MODELS][MAX_QPATH];
	char		sound_name[MAX_PRECACHE_SOUNDS][MAX_QPATH];
	char		*particle_ssname[MAX_SSPARTICLESPRE];
#ifdef Q2CLIENT
	char		*configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL];
	char		*image_name[Q2MAX_IMAGES];
	char		*item_name[Q2MAX_ITEMS];
	short		inventory[MAX_SPLITS][Q2MAX_ITEMS];
#endif

	struct model_s		*model_precache_vwep[MAX_VWEP_MODELS];
	struct model_s		*model_precache[MAX_PRECACHE_MODELS];
	struct sfx_s		*sound_precache[MAX_PRECACHE_SOUNDS];
	int					particle_ssprecache[MAX_SSPARTICLESPRE];	//these are actually 1-based, so 0 can be used to lazy-init them. I cheat.

	char				model_csqcname[MAX_CSMODELS][MAX_QPATH];
	struct model_s		*model_csqcprecache[MAX_CSMODELS];
	char				*particle_csname[MAX_CSPARTICLESPRE];
	int					particle_csprecache[MAX_CSPARTICLESPRE];	//these are actually 1-based, so we can be lazy and do a simple negate.

	qboolean			model_precaches_added;
	qboolean			particle_ssprecaches;	//says to not try to do any dp-compat hacks.
	qboolean			particle_csprecaches;	//says to not try to do any dp-compat hacks.

	//used for q2 sky/configstrings
	char skyname[MAX_QPATH];
	float skyrotate;
	vec3_t skyaxis;

	qboolean	fog_locked;
	fogstate_t	fog[2];	//0 = air, 1 = water. if water has no density fall back on air.
	fogstate_t	oldfog[2];

	char		levelname[40];	// for display on solo scoreboard
	char		*windowtitle;	// fully overrides the window caption.

// refresh related state
	struct model_s	*worldmodel;	// cl_entitites[0].model
	int			num_entities;	// stored bottom up in cl_entities array
	int			num_statics;	// stored top down in cl_entitiers

// all player information
	unsigned int    allocated_client_slots;
	player_info_t	players[MAX_CLIENTS];


	downloadlist_t *downloadlist;
	downloadlist_t *faileddownloads;

	qboolean gamedirchanged;

#ifdef Q2CLIENT
	char		q2statusbar[1024];
	char		q2layout[MAX_SPLITS][1024];
	int parse_entities;
	float lerpfrac;
	float q2svnetrate; //number of frames we expect to receive per second (required to calculate the server time correctly).
#endif

	char lastcenterprint[1024];	//prevents too much spam with console centerprint logging.



	struct itemtimer_s
	{
		float end;
		int entnum;
		float start;
		float duration;
		vec3_t origin;
		float radius;
		struct itemtimer_s *next;
	} *itemtimers;

	//interpolation+snapshots
	float	packfrac;
	packet_entities_t	*currentpackentities;
	packet_entities_t	*previouspackentities;
	float				currentpacktime;
	qboolean			do_lerp_players;


	int teamplay;
	int deathmatch;

	qboolean teamfortress;	// *sigh*. This is used for teamplay stuff. This sucks.
	qboolean hexen2pickups;

	qboolean sendprespawn;
	int contentstage;

	double matchgametimestart;
	enum {
		MATCH_DONTKNOW,	//assumed to be in progress.
		MATCH_COUNTDOWN,
		MATCH_STANDBY,
		MATCH_INPROGRESS
	} matchstate;
} client_state_t;

extern unsigned int		cl_teamtopcolor;
extern unsigned int		cl_teambottomcolor;
extern unsigned int		cl_enemytopcolor;
extern unsigned int		cl_enemybottomcolor;

//FPD values
//(commented out ones are ones that we don't support)
#define FPD_NO_FORCE_SKIN	256
#define FPD_NO_FORCE_COLOR	512
#define FPD_LIMIT_PITCH		(1 << 14)	//limit scripted pitch changes
#define FPD_LIMIT_YAW		(1 << 15)	//limit scripted yaw changes

//
// cvars
//
extern  cvar_t	cl_warncmd;
extern	cvar_t	cl_upspeed;
extern	cvar_t	cl_forwardspeed;
extern	cvar_t	cl_backspeed;
extern	cvar_t	cl_sidespeed;

extern	cvar_t	cl_movespeedkey;

extern	cvar_t	cl_yawspeed;
extern	cvar_t	cl_pitchspeed;

extern	cvar_t	cl_anglespeedkey;

extern	cvar_t	cl_shownet;
extern	cvar_t	cl_sbar;
extern	cvar_t	cl_hudswap;

extern	cvar_t	cl_pitchdriftspeed;
extern	cvar_t	lookspring;
extern	cvar_t	lookstrafe;
extern	cvar_t	sensitivity;

extern	cvar_t	m_pitch;
extern	cvar_t	m_yaw;
extern	cvar_t	m_forward;
extern	cvar_t	m_side;

extern cvar_t		_windowed_mouse;

#ifndef SERVERONLY
extern	cvar_t	name;
#endif


extern cvar_t ruleset_allow_playercount;
extern cvar_t ruleset_allow_frj;
extern cvar_t ruleset_allow_semicheats;
extern cvar_t ruleset_allow_packet;
extern cvar_t ruleset_allow_particle_lightning;
extern cvar_t ruleset_allow_overlongsounds;
extern cvar_t ruleset_allow_larger_models;
extern cvar_t ruleset_allow_modified_eyes;
extern cvar_t ruleset_allow_sensitive_texture_replacements;
extern cvar_t ruleset_allow_localvolume;
extern cvar_t ruleset_allow_shaders;
extern cvar_t ruleset_allow_watervis;

#ifndef SERVERONLY
extern	client_state_t	cl;
#endif

typedef struct
{
	entity_t		ent;
	entity_state_t	state;
	trailstate_t   *emit;
	int	mdlidx;	/*negative are csqc indexes*/
	pvscache_t		pvscache;
} static_entity_t;

// FIXME, allocate dynamically
extern	entity_state_t *cl_baselines;
extern	static_entity_t		*cl_static_entities;
extern	unsigned int	cl_max_static_entities;
extern	lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
extern	dlight_t		*cl_dlights;
extern	unsigned int	cl_maxdlights;

extern int rtlights_first, rtlights_max;
extern int cl_baselines_count;

extern	qboolean	nomaster;
extern float	server_version;	// version of server we connected to

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


//
// cl_main
//
void CL_InitDlights(void);
void CL_FreeDlights(void);
dlight_t *CL_AllocDlight (int key);
dlight_t *CL_AllocSlight (void);	//allocates a static light
dlight_t *CL_NewDlight (int key, const vec3_t origin, float radius, float time, float r, float g, float b);
dlight_t *CL_NewDlightCube (int key, const vec3_t origin, vec3_t angles, float radius, float time, vec3_t colours);
void	CL_DecayLights (void);

void CLQW_ParseDelta (struct entity_state_s *from, struct entity_state_s *to, int bits);

void CL_Init (void);
void Host_WriteConfiguration (void);
void CL_CheckServerInfo(void);
void CL_CheckServerPacks(void);

void CL_EstablishConnection (char *host);

void CL_Disconnect (void);
void CL_Disconnect_f (void);
void CL_Reconnect_f (void);
void CL_NextDemo (void);
void CL_Startdemos_f (void);
void CL_Demos_f (void);
void CL_Stopdemo_f (void);
void CL_Changing_f (void);
void CL_Reconnect_f (void);
void CL_ConnectionlessPacket (void);
qboolean CL_DemoBehind(void);
void CL_SaveInfo(vfsfile_t *f);
void CL_SetInfo (int pnum, char *key, char *value);

void CL_BeginServerConnect(const char *host, int port, qboolean noproxy);
char *CL_TryingToConnect(void);

void CL_ExecInitialConfigs(char *defaultexec);

extern	int				cl_framecount;	//number of times the entity lists have been cleared+reset.
extern	int				cl_numvisedicts;
extern	int				cl_maxvisedicts;
extern	entity_t		*cl_visedicts;

/*these are for q3 really*/
typedef struct {
	struct shader_s *shader;
	int firstvert;
	int firstidx;
	int numvert;
	int numidx;
	unsigned int flags;
} scenetris_t;
extern scenetris_t		*cl_stris;
extern vecV_t			*fte_restrict cl_strisvertv;
extern vec4_t			*fte_restrict cl_strisvertc;
extern vec2_t			*fte_restrict cl_strisvertt;
extern index_t			*fte_restrict cl_strisidx;
extern unsigned int cl_numstrisidx;
extern unsigned int cl_maxstrisidx;
extern unsigned int cl_numstrisvert;
extern unsigned int cl_maxstrisvert;
extern unsigned int cl_numstris;
extern unsigned int cl_maxstris;

extern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[];

//CL_TraceLine traces against network(positive)+csqc(negative) ents. returns frac(1 on failure), and impact, normal, ent values
float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *ent);
entity_t *TraceLineR (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);

//
// cl_input
//
typedef struct
{
	int		down[MAX_SPLITS][2];		// key nums holding it down
	int		state[MAX_SPLITS];			// low bit is down state
} kbutton_t;

extern	kbutton_t	in_mlook, in_klook;
extern 	kbutton_t 	in_strafe;
extern 	kbutton_t 	in_speed;

extern	float in_sensitivityscale;

void CL_MakeActive(char *gamename);
void CL_UpdateWindowTitle(void);

void CL_InitInput (void);
void CL_SendCmd (double frametime, qboolean mainloop);
void CL_SendMove (usercmd_t *cmd);
#ifdef NQPROT
void CL_ParseTEnt (qboolean nqprot);
#else
void CL_ParseTEnt (void);
#endif
void CL_UpdateTEnts (void);

enum beamtype_e
{	//these are internal ids, matching the beam table
	BT_Q1LIGHTNING1,
	BT_Q1LIGHTNING2,
	BT_Q1LIGHTNING3,
	BT_Q1BEAM,

	BT_Q2PARASITE,
	BT_Q2GRAPPLE,
	BT_Q2HEATBEAM,
	BT_Q2LIGHTNING,

	BT_H2LIGHTNING_SMALL,
	BT_H2CHAIN,
	BT_H2SUNSTAFF1,
	BT_H2SUNSTAFF1_SUB,	//inner beam hack
	BT_H2SUNSTAFF2,		//same model as 1, but different particle effect
	BT_H2LIGHTNING,
	BT_H2COLORBEAM,
	BT_H2ICECHUNKS,
	BT_H2GAZE,
	BT_H2FAMINE,
};
typedef struct beam_s beam_t;
beam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end);

void CL_ClearState (void);
void CLQ2_ClearState(void);

void CL_ReadPackets (void);
void CL_ClampPitch (int pnum);

int  CL_ReadFromServer (void);
void CL_WriteToServer (usercmd_t *cmd);
void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps);

int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost);

float CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart);
char *Key_KeynumToString (int keynum, int modifier);
int Key_StringToKeynum (const char *str, int *modifier);
char *Key_GetBinding(int keynum, int bindmap, int modifier);
void Key_GetBindMap(int *bindmaps);
void Key_SetBindMap(int *bindmaps);

void CL_UseIndepPhysics(qboolean allow);

void CL_FlushClientCommands(void);
void VARGS CL_SendClientCommand(qboolean reliable, char *format, ...) LIKEPRINTF(2);
float CL_FilterTime (double time, float wantfps, qboolean ignoreserver);
int CL_RemoveClientCommands(char *command);
void CL_AllowIndependantSendCmd(qboolean allow);

//
// cl_demo.c
//
void CL_StopPlayback (void);
qboolean CL_GetMessage (void);
void CL_WriteDemoCmd (usercmd_t *pcmd);
void CL_Demo_ClientCommand(char *commandtext);	//for QTV.

void CL_WriteSetDemoMessage (void);	//'restarts' a qwd, when we have reloads/map changes in them
void CL_WriteRecordQ2DemoMessage(sizebuf_t *msg);
void CL_Stop_f (void);
void CL_Record_f (void);
void CL_ReRecord_f (void);
void CL_PlayDemo_f (void);
void CL_QTVPlay_f (void);
void CL_QTVPoll (void);
void CL_QTVList_f (void);
void CL_QTVDemos_f (void);
void CL_DemoJump_f(void);
void CL_ProgressDemoTime(void);
void CL_TimeDemo_f (void);
typedef struct 
{
	enum
	{
		QTVCT_NONE,
		QTVCT_STREAM,
		QTVCT_CONNECT,
		QTVCT_JOIN,
		QTVCT_OBSERVE,
		QTVCT_MAP
	} connectiontype;
	enum
	{
		QTVCT_NETQUAKE,
		QTVCT_QUAKEWORLD,
		QTVCT_QUAKE2,
		QTVCT_QUAKE3
	} protocol;
	char server[256];
	char splashscreen[256];
	//char *datafiles;
} qtvfile_t;
void CL_ParseQTVFile(vfsfile_t *f, const char *fname, qtvfile_t *result);

//
// cl_parse.c
//
#define NET_TIMINGS 256
#define NET_TIMINGSMASK 255
extern int	packet_latency[NET_TIMINGS];
int CL_CalcNet (float scale);
void CL_CalcNet2 (float *pings, float *pings_min, float *pings_max, float *pingms_stddev, float *pingfr, int *pingfr_min, int *pingfr_max, float *dropped, float *choked, float *invalid);
void CL_ClearParseState(void);
void CL_Parse_Disconnected(void);
void CL_DumpPacket(void);
void CL_ParseEstablished(void);
void CLQW_ParseServerMessage (void);
void CLNQ_ParseServerMessage (void);
#ifdef Q2CLIENT
void CLQ2_ParseServerMessage (void);
#endif
void CL_NewTranslation (int slot);

int CL_IsDownloading(const char *localname);
qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags);
qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags);
downloadlist_t *CL_DownloadFailed(const char *name, qdownload_t *qdl);
int CL_DownloadRate(void);
void CL_GetDownloadSizes(unsigned int *filecount, qofs_t *totalsize, qboolean *somesizesunknown);
qboolean CL_ParseOOBDownload(void);
void CL_DownloadFinished(qdownload_t *dl);
void CL_RequestNextDownload (void);
void CL_SendDownloadReq(sizebuf_t *msg);
void Sound_CheckDownload(const char *s); /*checkorenqueue a sound file*/

qboolean CL_IsUploading(void);
void CL_NextUpload(void);
void CL_StartUpload (qbyte *data, int size);
void CL_StopUpload(void);

qboolean CL_CheckBaselines (int size);

//
// view.c
//
void V_StartPitchDrift (playerview_t *pv);
void V_StopPitchDrift (playerview_t *pv);

void V_RenderView (void);
void V_Register (void);
void V_ParseDamage (playerview_t *pv);
void V_SetContentsColor (int contents);

//used directly by csqc
void V_CalcRefdef (playerview_t *pv);
void V_ClearRefdef(playerview_t *pv);
void V_ApplyRefdef(void);
void V_CalcGunPositionAngle (playerview_t *pv, float bob);
float V_CalcBob (playerview_t *pv, qboolean queryold);
void DropPunchAngle (playerview_t *pv);

int V_EditExternalModels(int newviewentity, entity_t *viewentities, int maxviewenties);
void V_DepthSortEntities(float *vieworg);


//
// cl_tent
//
void CL_RegisterParticles(void);
void CL_InitTEnts (void);
void CL_InitTEntSounds (void);
void CL_ClearTEnts (void);
void CL_ClearTEntParticleState (void);
void CL_ClearCustomTEnts(void);
void CL_ParseCustomTEnt(void);
qboolean CL_WriteCustomTEnt(sizebuf_t *buf, int id);
void CL_ParseEffect (qboolean effect2);

void CLNQ_ParseParticleEffect (void);
void CL_ParseParticleEffect2 (void);
void CL_ParseParticleEffect3 (void);
void CL_ParseParticleEffect4 (void);

int CL_TranslateParticleFromServer(int sveffect);
void CL_ParseTrailParticles(void);
void CL_ParsePointParticles(qboolean compact);
void CL_SpawnSpriteEffect(vec3_t org, vec3_t dir, vec3_t orientationup, struct model_s *model, int startframe, int framecount, float framerate, float alpha, float scale, float randspin, float gravity, int traileffect, unsigned int renderflags, int skinnum);	/*called from the particlesystem*/

//
// cl_ents.c
//
void CL_SetSolidPlayers (void);
void CL_SetUpPlayerPrediction(qboolean dopred);
void CL_LinkStaticEntities(void *pvs);
void CL_TransitionEntities (void); /*call at the start of the frame*/
void CL_EmitEntities (void);
void CL_ClearProjectiles (void);
void CL_ParseProjectiles (int modelindex, qboolean nails2);
void CLQW_ParsePacketEntities (qboolean delta);
void CLFTE_ParseEntities (void);
void CLFTE_ParseBaseline(entity_state_t *es, qboolean numberisimportant);
void CL_SetSolidEntities (void);
void CL_ParsePlayerinfo (void);
void CL_ParseClientPersist(void);
//these last ones are needed for csqc handling of engine-bound ents.
void CL_ClearEntityLists(void);
void CL_FreeVisEdicts(void);
void CL_LinkViewModel(void);
void CL_LinkPlayers (void);
void CL_LinkPacketEntities (void);
void CL_LinkProjectiles (void);
void CL_ClearLerpEntsParticleState (void);
qboolean CL_MayLerp(void);

//
//clq3_parse.c
//
#ifdef Q3CLIENT
void VARGS CLQ3_SendClientCommand(const char *fmt, ...) LIKEPRINTF(1);
void CLQ3_SendAuthPacket(netadr_t *gameserver);
void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport);
void CLQ3_SendCmd(usercmd_t *cmd);
qboolean CLQ3_Netchan_Process(void);
void CLQ3_ParseServerMessage (void);
struct snapshot_s;
qboolean CG_FillQ3Snapshot(int snapnum, struct snapshot_s *snapshot);

void CG_InsertIntoGameState(int num, char *str);
void CG_Restart_f(void);

char *CG_GetConfigString(int num);
#endif

//
//pr_csqc.c
//
#ifdef CSQC_DAT
qboolean CSQC_Inited(void);
void CSQC_RendererRestarted(void);
qboolean CSQC_UnconnectedOkay(qboolean inprinciple);
qboolean CSQC_UnconnectedInit(void);
qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checksum);
qboolean CSQC_ConsoleLink(char *text, char *info);
void CSQC_RegisterCvarsAndThings(void);
qboolean CSQC_SetupToRenderPortal(int entnum);
qboolean CSQC_DrawView(void);
qboolean CSQC_UseGamecodeLoadingScreen(void);
void CSQC_Shutdown(void);
qboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend);
qboolean CSQC_LoadResource(char *resname, char *restype);
qboolean CSQC_ParsePrint(char *message, int printlevel);
qboolean CSQC_ParseGamePacket(void);
qboolean CSQC_CenterPrint(int lplayernum, char *cmd);
qboolean CSQC_Parse_Damage(float save, float take, vec3_t source);
void CSQC_Input_Frame(int lplayernum, usercmd_t *cmd);
void CSQC_WorldLoaded(void);
qboolean CSQC_ParseTempEntity(void);
qboolean CSQC_ConsoleCommand(char *cmd);
qboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid);
qboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid);
qboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid);
qboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid);
qboolean CSQC_Accelerometer(float x, float y, float z);
qboolean CSQC_Gyroscope(float x, float y, float z);
int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs, unsigned int flags);
void CSQC_ParseEntities(void);
void CSQC_ResetTrails(void);

qboolean CSQC_DeltaPlayer(int playernum, player_state_t *state);
void CSQC_DeltaStart(float time);
qboolean CSQC_DeltaUpdate(entity_state_t *src);
void CSQC_DeltaEnd(void);

void CSQC_CvarChanged(cvar_t *var);
#else
#define CSQC_UnconnectedOkay(inprinciple) false
#define CSQC_UnconnectedInit() false
#define CSQC_UseGamecodeLoadingScreen() false
#endif

//
// cl_pred.c
//
void CL_InitPrediction (void);
void CL_PredictMove (void);
void CL_PredictUsercmd (int seat, int entnum, player_state_t *from, player_state_t *to, usercmd_t *u);
#ifdef Q2CLIENT
void CLQ2_CheckPredictionError (void);
#endif
void CL_CalcClientTime(void);

//
// cl_cam.c
//
qboolean Cam_DrawViewModel(playerview_t *pv);
int Cam_TrackNum(playerview_t *pv);
void Cam_Unlock(playerview_t *pv);				//revert to freecam or so, because that entity failed.
void Cam_Lock(playerview_t *pv, int playernum);	//attempt to lock on to the given player.
void Cam_NowLocked(playerview_t *pv);						//player was located, track them now
void Cam_SelfTrack(playerview_t *pv);
void Cam_Track(playerview_t *pv, usercmd_t *cmd);
void Cam_TrackCrosshairedPlayer(playerview_t *pv);
void Cam_SetModAutoTrack(int userid);
void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd);
void Cam_Reset(void);
void Cam_TrackPlayer(int seat, char *cmdname, char *plrarg);
void CL_InitCam(void);
void Cam_AutoTrack_Update(const char *mode);	//reset autotrack setting (because we started a new map or whatever)

void QDECL vectoangles(vec3_t fwd, vec3_t ang);

//
//zqtp.c
//
#define TPM_UNKNOWN    0
#define TPM_NORMAL     1
#define TPM_TEAM       2
#define TPM_SPECTATOR  4
#define TPM_FAKED     16
#define TPM_OBSERVEDTEAM  32
#define TPM_QTV       64		//should only be qtv_say_game/qtv_say_team_game

void		CL_Say (qboolean team, char *extra);
int			TP_CategorizeMessage (char *s, int *offset, player_info_t **plr);
void		TP_CheckPickupSound(char *s, vec3_t org, int seat);
qboolean	TP_CheckSoundTrigger (char *str);
int			TP_CountPlayers (void);
char*		TP_EnemyName (void);
char*		TP_EnemyTeam (void);
void		TP_ExecTrigger (char *s, qboolean indemos);
qboolean	TP_FilterMessage (char *s);
void		TP_Init(void);
char*		TP_LocationName (vec3_t location);
char*		TP_MapName (void);
void		TP_NewMap (void);
void		TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_info_t *info);
qboolean	TP_IsPlayerVisible(vec3_t origin);
char*		TP_PlayerName (void);
char*		TP_PlayerTeam (void);
void		TP_SearchForMsgTriggers (char *s, int level);
qboolean	TP_SoundTrigger(char *message);
void		TP_StatChanged (int stat, int value);
qboolean	TP_SuppressMessage(char *buf);
colourised_t *TP_FindColours(char *name);
void		TP_UpdateAutoStatus(void);

//
// skin.c
//

typedef struct
{
    char	manufacturer;
    char	version;
    char	encoding;
    char	bits_per_pixel;
    unsigned short	xmin,ymin,xmax,ymax;
    unsigned short	hres,vres;
    unsigned char	palette[48];
    char	reserved;
    char	color_planes;
    unsigned short	bytes_per_line;
    unsigned short	palette_type;
    char	filler[58];
//    unsigned char	data;			// unbounded
} pcx_t;
qbyte *ReadPCXData(qbyte *buf, int length, int width, int height, qbyte *result);


qwskin_t *Skin_Lookup (char *fullname);
char *Skin_FindName (player_info_t *sc);
void	Skin_Find (player_info_t *sc);
qbyte	*Skin_Cache8 (qwskin_t *skin);
void	Skin_Skins_f (void);
void	Skin_FlushSkin(char *name);
void	Skin_AllSkins_f (void);
void	Skin_NextDownload (void);
void Skin_FlushPlayers(void);
void Skin_FlushAll(void);

#define RSSHOT_WIDTH 320
#define RSSHOT_HEIGHT 200





//valid.c
void	RulesetLatch(cvar_t *cvar);
void	Validation_Apply_Ruleset(void);
void	Validation_FlushFileList(void);
void	Validation_CheckIfResponse(char *text);
void	Validation_DelatchRulesets(void);
void	InitValidation(void);
void	Validation_IncludeFile(char *filename, char *file, int filelen);
void	Validation_Auto_Response(int playernum, char *s);

extern	qboolean f_modified_particles;
extern	qboolean care_f_modified;


//random files (fixme: clean up)

#ifdef Q2CLIENT
unsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max);
void CLQ2_ParseTEnt (void);
void CLQ2_AddEntities (void);
void CLQ2_ParseBaseline (void);
void CLQ2_ClearParticleState(void);
void CLR1Q2_ParsePlayerUpdate(void);
void CLQ2_ParseFrame (int extrabits);
void CLQ2_RunMuzzleFlash2 (int ent, int flash_number);
int CLQ2_RegisterTEntModels (void);
void CLQ2_WriteDemoBaselines(sizebuf_t *buf);
#endif

#ifdef HLCLIENT
//networking
void CLHL_LoadClientGame(void);
int CLHL_ParseGamePacket(void);
int CLHL_AnimateViewEntity(entity_t *ent);
//screen
int CLHL_DrawHud(void);
//inputs
int CLHL_GamecodeDoesMouse(void);
int CLHL_MouseEvent(unsigned int buttonmask);
void CLHL_SetMouseActive(int activate);
int CLHL_BuildUserInput(int msecs, usercmd_t *cmd);
#endif

#ifdef NQPROT
void CLNQ_ParseEntity(unsigned int bits);
void NQ_P_ParseParticleEffect (void);
void CLNQ_SignonReply (void);
void NQ_BeginConnect(char *to);
void NQ_ContinueConnect(char *to);
int CLNQ_GetMessage (void);
#endif

void CL_BeginServerReconnect(void);

void SV_User_f (void);	//called by client version of the function
void SV_Serverinfo_f (void);
void SV_ConSay_f(void);



#ifdef TEXTEDITOR
extern console_t *editormodal;
void Editor_Draw(void);
void Editor_Init(void);
struct pubprogfuncs_s;
void Editor_ProgsKilled(struct pubprogfuncs_s *dead);
#endif

void SCR_StringToRGB (char *rgbstring, float *rgb, float rgbinputscale);

struct model_s;
void CL_AddVWeapModel(entity_t *player, struct model_s *model);

/*q2 cinematics*/
struct cinematics_s;
void CIN_StopCinematic (struct cinematics_s *cin);
struct cinematics_s *CIN_PlayCinematic (char *arg);
int CIN_RunCinematic (struct cinematics_s *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette);
void CIN_Rewind(struct cinematics_s *cin);

typedef enum
{
	CINSTATE_INVALID,	//also reported for not playing
	CINSTATE_PLAY,
	CINSTATE_LOOP,
	CINSTATE_PAUSE,
	CINSTATE_ENDED,
	CINSTATE_FLUSHED,	//video will restart from beginning
} cinstates_t;
typedef struct cin_s cin_t;
#ifdef NOMEDIA
#define Media_Playing() false
#define Media_Init() (void)0
#define Media_PlayingFullScreen() false
#define Media_PlayFilm(n,e) false
#define Media_StopFilm(a) (void)true
#else
/*media playing system*/
qboolean Media_PlayingFullScreen(void);
void Media_Init(void);
qboolean Media_PlayFilm(char *name, qboolean enqueue);
qboolean Media_StopFilm(qboolean all);
struct cin_s *Media_StartCin(char *name);
texid_tf Media_UpdateForShader(cin_t *cin);
void Media_ShutdownCin(cin_t *cin);
#endif
qboolean Media_NamedTrack(const char *initialtrack, const char *looptrack);	//new background music interface
void Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack);				//legacy cd interface for protocols that only support numbered tracks.
void Media_EndedTrack(void);	//cd is no longer running, media code needs to pick a new track (cd track or faketrack)

//these accept NULL for cin to mean the current fullscreen video
void Media_Send_Command(cin_t *cin, const char *command);
void Media_Send_MouseMove(cin_t *cin, float x, float y);
void Media_Send_Resize(cin_t *cin, int x, int y);
void Media_Send_GetSize(cin_t *cin, int *x, int *y, float *aspect);
void Media_Send_KeyEvent(cin_t *cin, int button, int unicode, int event);
void Media_Send_Reset(cin_t *cin);
void Media_SetState(cin_t *cin, cinstates_t newstate);
cinstates_t Media_GetState(cin_t *cin);
const char *Media_Send_GetProperty(cin_t *cin, const char *key);

void MVD_Interpolate(void);

int Stats_GetKills(int playernum);
int Stats_GetTKills(int playernum);
int Stats_GetDeaths(int playernum);
int Stats_GetTouches(int playernum);
int Stats_GetCaptures(int playernum);
qboolean Stats_HaveFlags(int mode);
qboolean Stats_HaveKills(void);
float Stats_GetLastOwnFrag(int seat, char *res, int reslen);
void VARGS Stats_Message(char *msg, ...) LIKEPRINTF(1);
qboolean Stats_ParsePrintLine(const char *line);
qboolean Stats_ParsePickups(const char *line);
void Stats_NewMap(void);
void Stats_Clear(void);
void Stats_Init(void);

enum uploadfmt;
typedef struct
{
	size_t structsize;
	const char *drivername;
	void *(VARGS *createdecoder)(const char *name);
	qboolean (VARGS *decodeframe)(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx);
	void (VARGS *shutdown)(void *ctx);
	void (VARGS *rewind)(void *ctx);

	//these are any interactivity functions you might want...
	void (VARGS *cursormove) (void *ctx, float posx, float posy);	//pos is 0-1
	void (VARGS *key) (void *ctx, int code, int unicode, int event);
	qboolean (VARGS *setsize) (void *ctx, int width, int height);
	void (VARGS *getsize) (void *ctx, int *width, int *height);
	void (VARGS *changestream) (void *ctx, const char *streamname);

	qboolean (VARGS *getproperty) (void *ctx, const char *field, char *out, size_t *outsize);	//if out is null, returns required buffer size. returns 0 on failure / buffer too small
} media_decoder_funcs_t;
typedef struct
{
	size_t structsize;
	const char *drivername;
	const char *description;
	const char *defaultextension;
	void *(VARGS *capture_begin) (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits);
	void (VARGS *capture_video) (void *ctx, void *data, int frame, int width, int height, enum uploadfmt fmt);
	void (VARGS *capture_audio) (void *ctx, void *data, int bytes);
	void (VARGS *capture_end) (void *ctx);
} media_encoder_funcs_t;
extern struct plugin_s *currentplug;
qboolean Media_RegisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs);
qboolean Media_UnregisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs);
qboolean Media_RegisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs);
qboolean Media_UnregisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs);