/*
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

#ifndef CLIENT_H
#define CLIENT_H

#include "matrixlib.h"
#include "snd_main.h"
#include "view.h"
#include "cap.h"
#include "cl_parse.h"
#include "cl_particles.h"
#include "r_stats.h"

// flags for rtlight rendering
#define LIGHTFLAG_NORMALMODE 1
#define LIGHTFLAG_REALTIMEMODE 2

typedef struct tridecal_s
{
	// color and initial alpha value
	float			texcoord2f[3][2];
	float			vertex3f[3][3];
	float			color4f[3][4];
	float			plane[4]; // backface culling
	// how long this decal has lived so far (the actual fade begins at cl_decals_time)
	float			lived;
	// if >= 0 this indicates the decal should follow an animated triangle
	int				triangleindex;
	// for visibility culling
	int				surfaceindex;
	// old decals are killed to obey cl_decals_max
	unsigned int	decalsequence;
}
tridecal_t;

typedef struct decalsystem_s
{
	model_t *model;
	double lastupdatetime;
	int maxdecals;
	int freedecal;
	int numdecals;
	tridecal_t *decals;
	float *vertex3f;
	float *texcoord2f;
	float *color4f;
	int *element3i;
	unsigned short *element3s;
}
decalsystem_t;

typedef struct effect_s
{
	int active;
	vec3_t origin;
	double starttime;
	float framerate;
	model_t *model;
	int startframe;
	int endframe;
	// these are for interpolation
	int frame;
	double frame1time;
	double frame2time;
}
cl_effect_t;

typedef struct beam_s
{
	int		entity;
	// draw this as lightning polygons, or a model?
	int		lightning;
	struct model_s	*model;
	float	endtime;
	vec3_t	start, end;
}
beam_t;

typedef struct rtlight_particle_s
{
	float origin[3];
	float color[3];
}
rtlight_particle_t;

typedef struct rtlight_s
{
	// note that the world to light matrices are inversely scaled (divided) by lightradius

	// core properties
	/// matrix for transforming light filter coordinates to world coordinates
	matrix4x4_t matrix_lighttoworld;
	/// matrix for transforming world coordinates to light filter coordinates
	matrix4x4_t matrix_worldtolight;
	/// typically 1 1 1, can be lower (dim) or higher (overbright)
	vec3_t color;
	/// size of the light (remove?)
	vec_t radius;
	/// light filter
	char cubemapname[64];
	/// light style to monitor for brightness
	int style;
	/// whether light should render shadows (see castshadows for whether it actually does this frame)
	int shadow;
	/// intensity of corona to render
	vec_t corona;
	/// radius scale of corona to render (1.0 means same as light radius)
	vec_t coronasizescale;
	/// ambient intensity to render
	vec_t ambientscale;
	/// diffuse intensity to render
	vec_t diffusescale;
	/// specular intensity to render
	vec_t specularscale;
	/// LIGHTFLAG_* flags
	int flags;

	// generated properties
	/// used only for casting shadows
	vec3_t shadoworigin;
	/// culling
	vec3_t cullmins;
	vec3_t cullmaxs;
	/// when r_shadow_culllights_trace is set, this is refreshed by each successful trace.
	double trace_timer;

	// rendering properties, updated each time a light is rendered
	// this is rtlight->color * d_lightstylevalue
	vec3_t currentcolor;
	/// used by corona updates, due to occlusion query
	float corona_visibility;
	unsigned int corona_queryindex_visiblepixels;
	unsigned int corona_queryindex_allpixels;
	/// this is R_GetCubemap(rtlight->cubemapname)
	rtexture_t *currentcubemap;
	/// set by R_Shadow_PrepareLight to decide whether R_Shadow_DrawLight should draw it
	qbool draw;
	/// set by R_Shadow_PrepareLight to indicate whether R_Shadow_DrawShadowMaps should do anything
	qbool castshadows;
	/// these fields are set by R_Shadow_PrepareLight for later drawing
	int cached_numlightentities;
	int cached_numlightentities_noselfshadow;
	int cached_numshadowentities;
	int cached_numshadowentities_noselfshadow;
	int cached_numsurfaces;
	struct entity_render_s **cached_lightentities;
	struct entity_render_s **cached_lightentities_noselfshadow;
	struct entity_render_s **cached_shadowentities;
	struct entity_render_s **cached_shadowentities_noselfshadow;
	unsigned char *cached_shadowtrispvs;
	unsigned char *cached_lighttrispvs;
	int *cached_surfacelist;
	// reduced light cullbox from GetLightInfo
	vec3_t cached_cullmins;
	vec3_t cached_cullmaxs;
	// current shadow-caster culling planes based on view
	// (any geometry outside these planes can not contribute to the visible
	//  shadows in any way, and thus can be culled safely)
	int cached_numfrustumplanes;
	mplane_t cached_frustumplanes[5]; // see R_Shadow_ComputeShadowCasterCullingPlanes

	/// static light info
	/// true if this light should be compiled as a static light
	int isstatic;
	/// true if this is a compiled world light, cleared if the light changes
	int compiled;
	/// the size that this light should have (assuming no scene LOD kicking in to reduce it)
	int shadowmapsidesize;
	/// position of this light in the shadowmap atlas
	int shadowmapatlasposition[2];
	/// size of one side of this light in the shadowmap atlas (for omnidirectional shadowmaps this is the min corner of a 2x3 arrangement, or a 4x3 arrangement in the case of noselfshadow entities being present)
	int shadowmapatlassidesize;
	/// optimized and culled mesh to render for world entity shadows
	shadowmesh_t *static_meshchain_shadow_shadowmap;
	/// used for visibility testing (more exact than bbox)
	int static_numleafs;
	int static_numleafpvsbytes;
	int *static_leaflist;
	unsigned char *static_leafpvs;
	/// surfaces seen by light
	int static_numsurfaces;
	int *static_surfacelist;
	/// flag bits indicating which triangles of the world model should cast
	/// shadows, and which ones should be lit
	///
	/// this avoids redundantly scanning the triangles in each surface twice
	/// for whether they should cast shadows, once in culling and once in the
	/// actual shadowmarklist production.
	int static_numshadowtrispvsbytes;
	unsigned char *static_shadowtrispvs;
	/// this allows the lighting batch code to skip backfaces andother culled
	/// triangles not relevant for lighting
	/// (important on big surfaces such as terrain)
	int static_numlighttrispvsbytes;
	unsigned char *static_lighttrispvs;
	/// masks of all shadowmap sides that have any potential static receivers or casters
	int static_shadowmap_receivers;
	int static_shadowmap_casters;
	/// particle-tracing cache for global illumination
	int particlecache_numparticles;
	int particlecache_maxparticles;
	int particlecache_updateparticle;
	rtlight_particle_t *particlecache_particles;

	/// bouncegrid light info
	float bouncegrid_photoncolor[3];
	float bouncegrid_photons;
	int bouncegrid_hits;
	int bouncegrid_traces;
	float bouncegrid_effectiveradius;
}
rtlight_t;

typedef struct dlight_s
{
	// destroy light after this time
	// (dlight only)
	vec_t die;
	// the entity that owns this light (can be NULL)
	// (dlight only)
	struct entity_render_s *ent;
	// location
	// (worldlight: saved to .rtlights file)
	vec3_t origin;
	// worldlight orientation
	// (worldlight only)
	// (worldlight: saved to .rtlights file)
	vec3_t angles;
	// dlight orientation/scaling/location
	// (dlight only)
	matrix4x4_t matrix;
	// color of light
	// (worldlight: saved to .rtlights file)
	vec3_t color;
	// cubemap name to use on this light
	// (worldlight: saved to .rtlights file)
	char cubemapname[64];
	// make light flash while selected
	// (worldlight only)
	int selected;
	// brightness (not really radius anymore)
	// (worldlight: saved to .rtlights file)
	vec_t radius;
	// drop intensity this much each second
	// (dlight only)
	vec_t decay;
	// intensity value which is dropped over time
	// (dlight only)
	vec_t intensity;
	// initial values for intensity to modify
	// (dlight only)
	vec_t initialradius;
	vec3_t initialcolor;
	// light style which controls intensity of this light
	// (worldlight: saved to .rtlights file)
	int style;
	// cast shadows
	// (worldlight: saved to .rtlights file)
	int shadow;
	// corona intensity
	// (worldlight: saved to .rtlights file)
	vec_t corona;
	// radius scale of corona to render (1.0 means same as light radius)
	// (worldlight: saved to .rtlights file)
	vec_t coronasizescale;
	// ambient intensity to render
	// (worldlight: saved to .rtlights file)
	vec_t ambientscale;
	// diffuse intensity to render
	// (worldlight: saved to .rtlights file)
	vec_t diffusescale;
	// specular intensity to render
	// (worldlight: saved to .rtlights file)
	vec_t specularscale;
	// LIGHTFLAG_* flags
	// (worldlight: saved to .rtlights file)
	int flags;
	// linked list of world lights
	// (worldlight only)
	struct dlight_s *next;
	// embedded rtlight struct for renderer
	// (worldlight only)
	rtlight_t rtlight;
}
dlight_t;

// this is derived from processing of the framegroupblend array
// note: technically each framegroupblend can produce two of these, but that
// never happens in practice because no one blends between more than 2
// framegroups at once
#define MAX_FRAMEBLENDS (MAX_FRAMEGROUPBLENDS * 2)
typedef struct frameblend_s
{
	int subframe;
	float lerp;
}
frameblend_t;

// LadyHavoc: this struct is intended for the renderer but some fields are
// used by the client.
//
// The renderer should not rely on any changes to this struct to be persistent
// across multiple frames because temp entities are wiped every frame, but it
// is acceptable to cache things in this struct that are not critical.
//
// For example the r_cullentities_trace code does such caching.
typedef struct entity_render_s
{
	// location
	//vec3_t origin;
	// orientation
	//vec3_t angles;
	// transform matrix for model to world
	matrix4x4_t matrix;
	// transform matrix for world to model
	matrix4x4_t inversematrix;
	// opacity (alpha) of the model
	float alpha;
	// size the model is shown
	float scale;
	// transparent sorting offset
	float transparent_offset;

	// NULL = no model
	model_t *model;
	// number of the entity represents, or 0 for non-network entities
	int entitynumber;
	// literal colormap colors for renderer, if both are 0 0 0 it is not colormapped
	vec3_t colormap_pantscolor;
	vec3_t colormap_shirtcolor;
	// light, particles, etc
	int effects;
	// qw CTF flags and other internal-use-only effect bits
	int internaleffects;
	// for Alias models
	int skinnum;
	// render flags
	int flags;

	// colormod tinting of models
	float colormod[3];
	float glowmod[3];

	// interpolated animation - active framegroups and blend factors
	framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];

	// time of last model change (for shader animations)
	double shadertime;

	// calculated by the renderer (but not persistent)

	// calculated during R_AddModelEntities
	vec3_t mins, maxs;
	// subframe numbers (-1 if not used) and their blending scalers (0-1), if interpolation is not desired, use subframeblend[0].subframe
	frameblend_t frameblend[MAX_FRAMEBLENDS];
	// skeletal animation data (if skeleton.relativetransforms is not NULL, it overrides frameblend)
	skeleton_t *skeleton;

	// animation cache (pointers allocated using R_FrameData_Alloc)
	// ONLY valid during R_RenderView!  may be NULL (not cached)
	float          *animcache_vertex3f;
	r_meshbuffer_t *animcache_vertex3f_vertexbuffer;
	int             animcache_vertex3f_bufferoffset;
	float          *animcache_normal3f;
	r_meshbuffer_t *animcache_normal3f_vertexbuffer;
	int             animcache_normal3f_bufferoffset;
	float          *animcache_svector3f;
	r_meshbuffer_t *animcache_svector3f_vertexbuffer;
	int             animcache_svector3f_bufferoffset;
	float          *animcache_tvector3f;
	r_meshbuffer_t *animcache_tvector3f_vertexbuffer;
	int             animcache_tvector3f_bufferoffset;
	// gpu-skinning shader needs transforms in a certain format, we have to
	// upload this to a uniform buffer for the shader to use, and also keep a
	// backup copy in system memory for the dynamic batch fallback code
	// if this is not NULL, the other animcache variables are NULL
	float *animcache_skeletaltransform3x4;
	r_meshbuffer_t *animcache_skeletaltransform3x4buffer;
	int animcache_skeletaltransform3x4offset;
	int animcache_skeletaltransform3x4size;

	// CL_UpdateEntityShading reads these fields
	// used only if RENDER_CUSTOMIZEDMODELLIGHT is set
	vec3_t custommodellight_ambient;
	vec3_t custommodellight_diffuse;
	vec3_t custommodellight_lightdir;
	// CSQC entities get their shading from the root of their attachment chain
	float custommodellight_origin[3];

	// derived lighting parameters (CL_UpdateEntityShading)

	// used by MATERIALFLAG_FULLBRIGHT which is MATERIALFLAG_MODELLIGHT with
	// this as ambient color, along with MATERIALFLAG_NORTLIGHT
	float render_fullbright[3];
	// color tint for the base pass glow textures if any
	float render_glowmod[3];
	// MATERIALFLAG_MODELLIGHT uses these parameters
	float render_modellight_ambient[3];
	float render_modellight_diffuse[3];
	float render_modellight_lightdir_world[3];
	float render_modellight_lightdir_local[3];
	float render_modellight_specular[3];
	// lightmap rendering (not MATERIALFLAG_MODELLIGHT)
	float render_lightmap_ambient[3];
	float render_lightmap_diffuse[3];
	float render_lightmap_specular[3];
	// rtlights use these colors for the materials on this entity
	float render_rtlight_diffuse[3];
	float render_rtlight_specular[3];
	// ignore lightmap and use fixed lighting settings on this entity (e.g. FULLBRIGHT)
	qbool render_modellight_forced;
	// do not process per pixel lights on this entity at all (like MATERIALFLAG_NORTLIGHT)
	qbool render_rtlight_disabled;
	// use the 3D lightmap from q3bsp on this entity
	qbool render_lightgrid;

	// storage of decals on this entity
	// (note: if allowdecals is set, be sure to call R_DecalSystem_Reset on removal!)
	int allowdecals;
	decalsystem_t decalsystem;

	// FIELDS UPDATED BY RENDERER:
	// last time visible during trace culling
	double last_trace_visibility;

	// user wavefunc parameters (from csqc)
	vec_t userwavefunc_param[Q3WAVEFUNC_USER_COUNT];
}
entity_render_t;

typedef struct entity_persistent_s
{
	vec3_t trail_origin; // previous position for particle trail spawning
	vec3_t oldorigin; // lerp
	vec3_t oldangles; // lerp
	vec3_t neworigin; // lerp
	vec3_t newangles; // lerp
	vec_t lerpstarttime; // lerp
	vec_t lerpdeltatime; // lerp
	float muzzleflash; // muzzleflash intensity, fades over time
	float trail_time; // residual error accumulation for particle trail spawning (to keep spacing across frames)
	qbool trail_allowed; // set to false by teleports, true by update code, prevents bad lerps
}
entity_persistent_t;

typedef struct entity_s
{
	// baseline state (default values)
	entity_state_t state_baseline;
	// previous state (interpolating from this)
	entity_state_t state_previous;
	// current state (interpolating to this)
	entity_state_t state_current;

	// used for regenerating parts of render
	entity_persistent_t persistent;

	// the only data the renderer should know about
	entity_render_t render;
}
entity_t;

typedef struct lightstyle_s
{
	int		length;
	char	map[MAX_STYLESTRING];
} lightstyle_t;

typedef struct scoreboard_s
{
	char	name[MAX_SCOREBOARDNAME];
	int		frags;
	int		colors; // two 4 bit fields
	// QW fields:
	int		qw_userid;
	char	qw_userinfo[MAX_USERINFO_STRING];
	float	qw_entertime;
	int		qw_ping;
	int		qw_packetloss;
	int		qw_movementloss;
	int		qw_spectator;
	char	qw_team[8];
	char	qw_skin[MAX_QPATH];
} scoreboard_t;

typedef struct cshift_s
{
	float	destcolor[3];
	float	percent;		// 0-255
	float   alphafade;      // (any speed)
} cshift_t;

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

#define	NAME_LENGTH	64


//
// client_state_t should hold all pieces of the client state
//

#define	SIGNONS		4			// signon messages to receive before connected

typedef enum cactive_e
{
	ca_uninitialized,	// during early startup
	ca_dedicated, 		// a dedicated server with no ability to start a client
	ca_disconnected, 	// full screen console with no connection
	ca_connected		// valid netcon, talking to a server
}
cactive_t;

typedef enum qw_downloadtype_e
{
	dl_none,
	dl_single,
	dl_skin,
	dl_model,
	dl_sound
}
qw_downloadtype_t;

#define CL_MAX_DOWNLOADACKS 4

typedef struct cl_downloadack_s
{
	int start, size;
}
cl_downloadack_t;

typedef struct cl_soundstats_s
{
	int mixedsounds;
	int totalsounds;
	int latency_milliseconds;
}
cl_soundstats_t;

//
// the client_static_t structure is persistent through an arbitrary number
// of server connections
//
typedef struct client_static_s
{
	cactive_t state;

	// all client memory allocations go in these pools
	mempool_t *levelmempool;
	mempool_t *permanentmempool;

// demo loop control
	// -1 = don't play demos
	int demonum;
	// list of demos in loop
	char demos[MAX_DEMOS][MAX_DEMONAME];
	// the actively playing demo (set by CL_PlayDemo)
	char demoname[MAX_QPATH];

// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
	qbool demorecording;
	fs_offset_t demo_lastcsprogssize;
	int demo_lastcsprogscrc;
	qbool demoplayback;
	qbool demostarting; // set if currently starting a demo, to stop -demo from quitting when switching to another demo
	qbool timedemo;
	// -1 = use normal cd track
	int forcetrack;
	qfile_t *demofile;
	// realtime at second frame of timedemo (LadyHavoc: changed to double)
	double td_starttime;
	int td_frames; // total frames parsed
	double td_onesecondnexttime;
	double td_onesecondframes;
	double td_onesecondrealtime;
	double td_onesecondminfps;
	double td_onesecondmaxfps;
	double td_onesecondavgfps;
	int td_onesecondavgcount;
	// LadyHavoc: pausedemo
	qbool demopaused;

	// sound mixer statistics for showsound display
	cl_soundstats_t soundstats;

	qbool connect_trying;
	int connect_remainingtries;
	double connect_nextsendtime;
	lhnetsocket_t *connect_mysocket;
	lhnetaddress_t connect_address;
	lhnetaddress_t rcon_address;
	// protocol version of the server we're connected to
	// (kept outside client_state_t because it's used between levels)
	protocolversion_t protocol;

#define MAX_RCONS 16
	int rcon_trying;
	lhnetaddress_t rcon_addresses[MAX_RCONS];
	char rcon_commands[MAX_RCONS][MAX_INPUTLINE];
	double rcon_timeout[MAX_RCONS];
	int rcon_ringpos;

// connection information
	// 0 to SIGNONS
	int signon;
	// network connection
	netconn_t *netcon;

	// download information
	// (note: qw_download variables are also used)
	cl_downloadack_t dp_downloadack[CL_MAX_DOWNLOADACKS];

	// input sequence numbers are not reset on level change, only connect
	unsigned int servermovesequence;

	// quakeworld stuff below

	// value of "qport" cvar at time of connection
	int qw_qport;
	// copied from cls.netcon->qw. variables every time they change, or set by demos (which have no cls.netcon)
	unsigned int qw_incoming_sequence;
	unsigned int qw_outgoing_sequence;

	// current file download buffer (only saved when file is completed)
	char qw_downloadname[MAX_QPATH];
	unsigned char *qw_downloadmemory;
	int qw_downloadmemorycursize;
	int qw_downloadmemorymaxsize;
	int qw_downloadnumber;
	int qw_downloadpercent;
	qw_downloadtype_t qw_downloadtype;
	// transfer rate display
	double qw_downloadspeedtime;
	int qw_downloadspeedcount;
	int qw_downloadspeedrate;
	qbool qw_download_deflate;

	// current file upload buffer (for uploading screenshots to server)
	unsigned char *qw_uploaddata;
	int qw_uploadsize;
	int qw_uploadpos;

	// user infostring
	// this normally contains the following keys in quakeworld:
	// password spectator name team skin topcolor bottomcolor rate noaim msg *ver *ip
	char userinfo[MAX_USERINFO_STRING];

	// extra user info for the "connect" command
	char connect_userinfo[MAX_USERINFO_STRING];

#ifdef CONFIG_VIDEO_CAPTURE
	// video capture stuff
	capturevideostate_t capturevideo;
#endif

	// crypto channel
	crypto_t crypto;

	// ProQuake compatibility stuff
	int proquake_servermod; // 0 = not proquake, 1 = proquake
	int proquake_serverversion; // actual proquake server version * 10 (3.40 = 34, etc)
	int proquake_serverflags; // 0 (PQF_CHEATFREE not supported)

	// don't write-then-read csprogs.dat (useful for demo playback)
	unsigned char *caughtcsprogsdata;
	fs_offset_t caughtcsprogsdatasize;

	int r_speeds_graph_length;
	int r_speeds_graph_current;
	int *r_speeds_graph_data;

	// graph scales
	int r_speeds_graph_datamin[r_stat_count];
	int r_speeds_graph_datamax[r_stat_count];
}
client_static_t;

extern client_static_t	cls;

//[515]: csqc
typedef struct
{
	qbool drawworld;
	qbool drawenginesbar;
	qbool drawcrosshair;
}csqc_vidvars_t;

typedef enum cl_parsingtextmode_e
{
	CL_PARSETEXTMODE_NONE,
	CL_PARSETEXTMODE_PING,
	CL_PARSETEXTMODE_STATUS,
	CL_PARSETEXTMODE_STATUS_PLAYERID,
	CL_PARSETEXTMODE_STATUS_PLAYERIP
}
cl_parsingtextmode_t;

typedef struct cl_locnode_s
{
	struct cl_locnode_s *next;
	char *name;
	vec3_t mins, maxs;
}
cl_locnode_t;

typedef struct showlmp_s
{
	qbool	isactive;
	float		x;
	float		y;
	char		label[32];
	char		pic[128];
}
showlmp_t;

//
// the client_state_t structure is wiped completely at every
// server signon
//
typedef struct client_state_s
{
	// true if playing in a local game and no one else is connected
	int islocalgame;

	// send a clc_nop periodically until connected
	float sendnoptime;

	// current input being accumulated by mouse/joystick/etc input
	usercmd_t cmd;
	// latest moves sent to the server that have not been confirmed yet
	usercmd_t movecmd[CL_MAX_USERCMDS];

// information for local display
	// health, etc
	int stats[MAX_CL_STATS];
	float *statsf; // points to stats[] array
	// last known inventory bit flags, for blinking
	int olditems;
	// cl.time of acquiring item, for blinking
	float item_gettime[32];
	// last known STAT_ACTIVEWEAPON
	int activeweapon;
	// cl.time of changing STAT_ACTIVEWEAPON
	float weapontime;
	// use pain anim frame if cl.time < this
	float faceanimtime;
	// for stair smoothing
	float stairsmoothz;
	double stairsmoothtime;

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

// the client maintains its own idea of view angles, which are
// sent to the server each frame.  The server sets punchangle when
// the view is temporarily offset, and an angle reset commands at the start
// of each level and after teleporting.

	// mviewangles is read from demo
	// viewangles is either client controlled or lerped from mviewangles
	vec3_t mviewangles[2], viewangles;
	// update by server, used by qc to do weapon recoil
	vec3_t mpunchangle[2], punchangle;
	// update by server, can be used by mods to kick view around
	vec3_t mpunchvector[2], punchvector;
	// update by server, used for lean+bob (0 is newest)
	vec3_t mvelocity[2], velocity;
	// update by server, can be used by mods for zooming
	vec_t mviewzoom[2], viewzoom;
	// if true interpolation the mviewangles and other interpolation of the
	// player is disabled until the next network packet
	// this is used primarily by teleporters, and when spectating players
	// special checking of the old fixangle[1] is used to differentiate
	// between teleporting and spectating
	qbool fixangle[2];

	// client movement simulation
	// these fields are only updated by CL_ClientMovement (called by CL_SendMove after parsing each network packet)
	// set by CL_ClientMovement_Replay functions
	qbool movement_predicted;
	// if true the CL_ClientMovement_Replay function will update origin, etc
	qbool movement_replay;
	// simulated data (this is valid even if cl.movement is false)
	vec3_t movement_origin;
	vec3_t movement_velocity;
	// whether the replay should allow a jump at the first sequence
	qbool movement_replay_canjump;

	// previous gun angles (for leaning effects)
	vec3_t gunangles_prev;
	vec3_t gunangles_highpass;
	vec3_t gunangles_adjustment_lowpass;
	vec3_t gunangles_adjustment_highpass;
	// previous gun angles (for leaning effects)
	vec3_t gunorg_prev;
	vec3_t gunorg_highpass;
	vec3_t gunorg_adjustment_lowpass;
	vec3_t gunorg_adjustment_highpass;

// pitch drifting vars
	float idealpitch;
	float pitchvel;
	qbool nodrift;
	float driftmove;
	double laststop;

//[515]: added for csqc purposes
	float sensitivityscale;
	csqc_vidvars_t csqc_vidvars;	//[515]: these parms must be set to true by default
	qbool csqc_wantsmousemove;
	struct model_s *csqc_model_precache[MAX_MODELS];

	// local amount for smoothing stepups
	//float crouch;

	// sent by server
	qbool paused;
	qbool onground;
	qbool inwater;

	// used by bob
	qbool oldonground;
	double lastongroundtime;
	double hitgroundtime;
	float bob2_smooth;
	float bobfall_speed;
	float bobfall_swing;
	double calcrefdef_prevtime;

	// don't change view angle, full screen, etc
	int intermission;
	// latched at intermission start
	double completed_time;

	// the timestamp of the last two messages
	double mtime[2];

	// clients view of time, time should be between mtime[0] and mtime[1] to
	// generate a lerp point for other data, oldtime is the previous frame's
	// value of time, frametime is the difference between time and oldtime
	// note: cl.time may be beyond cl.mtime[0] if packet loss is occuring, it
	// is only forcefully limited when a packet is received
	double time, oldtime;
	// how long it has been since the previous client frame in real time
	// (not game time, for that use cl.time - cl.oldtime)
	double realframetime;

	// used by cl_nettimesyncboundmode 7
#define NUM_TS_ERRORS 32 // max 256
	unsigned char ts_error_num;
	float ts_error_stor[NUM_TS_ERRORS];

	// fade var for fading while dead
	float deathfade;

	// motionblur alpha level variable
	float motionbluralpha;

	// copy of realtime from last recieved message, for net trouble icon
	float last_received_message;

// information that is static for the entire time connected to a server
	struct model_s *model_precache[MAX_MODELS];
	struct sfx_s *sound_precache[MAX_SOUNDS];

	// FIXME: this is a lot of memory to be keeping around, this really should be dynamically allocated and freed somehow
	char model_name[MAX_MODELS][MAX_QPATH];
	char sound_name[MAX_SOUNDS][MAX_QPATH];

	// for display on solo scoreboard
	char worldmessage[40]; // map title (not related to filename)
	// variants of map name
	char worldbasename[MAX_QPATH]; // %s
	char worldname[MAX_QPATH]; // maps/%s.bsp
	char worldnamenoextension[MAX_QPATH]; // maps/%s
	// cl_entitites[cl.viewentity] = player
	int viewentity;
	// the real player entity (normally same as viewentity,
	// different than viewentity if mod uses chasecam or other tricks)
	int realplayerentity;
	// this is updated to match cl.viewentity whenever it is in the clients
	// range, basically this is used in preference to cl.realplayerentity for
	// most purposes because when spectating another player it should show
	// their information rather than yours
	int playerentity;
	// max players that can be in this game
	int maxclients;
	// type of game (deathmatch, coop, singleplayer)
	int gametype;

	// models and sounds used by engine code (particularly cl_parse.c)
	model_t *model_bolt;
	model_t *model_bolt2;
	model_t *model_bolt3;
	model_t *model_beam;
	sfx_t *sfx_wizhit;
	sfx_t *sfx_knighthit;
	sfx_t *sfx_tink1;
	sfx_t *sfx_ric1;
	sfx_t *sfx_ric2;
	sfx_t *sfx_ric3;
	sfx_t *sfx_r_exp3;
	// indicates that the file "sound/misc/talk2.wav" was found (for use by team chat messages)
	qbool foundteamchatsound;

// refresh related state

	// cl_entitites[0].model
	struct model_s *worldmodel;

	// the gun model
	entity_t viewent;

	// cd audio
	int cdtrack, looptrack;

// frag scoreboard

	// [cl.maxclients]
	scoreboard_t *scores;

	// keep track of svc_print parsing state (analyzes ping reports and status reports)
	cl_parsingtextmode_t parsingtextmode;
	int parsingtextplayerindex;
	// set by scoreboard code when sending ping command, this causes the next ping results to be hidden
	// (which could eat the wrong ping report if the player issues one
	//  manually, but they would still see a ping report, just a later one
	//  caused by the scoreboard code rather than the one they intentionally
	//  issued)
	int parsingtextexpectingpingforscores;

	// entity database stuff
	// latest received entity frame numbers
#define LATESTFRAMENUMS 32
	int latestframenumsposition;
	int latestframenums[LATESTFRAMENUMS];
	unsigned int latestsendnums[LATESTFRAMENUMS];
	entityframe_database_t *entitydatabase;
	entityframe4_database_t *entitydatabase4;
	entityframeqw_database_t *entitydatabaseqw;

	// keep track of quake entities because they need to be killed if they get stale
	int lastquakeentity;
	unsigned char isquakeentity[MAX_EDICTS];

	// bounding boxes for clientside movement
	vec3_t playerstandmins;
	vec3_t playerstandmaxs;
	vec3_t playercrouchmins;
	vec3_t playercrouchmaxs;

	// old decals are killed based on this
	unsigned int decalsequence;

	int max_entities;
	int max_csqcrenderentities;
	int max_static_entities;
	int max_effects;
	int max_beams;
	int max_dlights;
	int max_lightstyle;
	int max_brushmodel_entities;
	int max_particles;
	int max_showlmps;

	entity_t *entities;
	entity_render_t *csqcrenderentities;
	unsigned char *entities_active;
	entity_t *static_entities;
	cl_effect_t *effects;
	beam_t *beams;
	dlight_t *dlights;
	lightstyle_t *lightstyle;
	int *brushmodel_entities;
	particle_t *particles;
	showlmp_t *showlmps;

	int num_entities;
	int num_static_entities;
	int num_brushmodel_entities;
	int num_effects;
	int num_beams;
	int num_dlights;
	int num_particles;
	int num_decals;
	int num_showlmps;

	double particles_updatetime;
	double decals_updatetime;
	int free_particle;
	int free_decal;

	// cl_serverextension_download feature
	int loadmodel_current;
	int downloadmodel_current;
	int loadmodel_total;
	int loadsound_current;
	int downloadsound_current;
	int loadsound_total;
	qbool downloadcsqc;
	qbool loadcsqc;
	qbool loadbegun;
	qbool loadfinished;

	// quakeworld stuff

	// local copy of the server infostring
	char qw_serverinfo[MAX_SERVERINFO_STRING];

	// time of last qw "pings" command sent to server while showing scores
	double last_ping_request;

	// used during connect
	int qw_servercount;

	// updated from serverinfo
	int qw_teamplay;

	// unused: indicates whether the player is spectating
	// use cl.scores[cl.playerentity-1].qw_spectator instead
	//qbool qw_spectator;

	// time accumulated since an input packet was sent
	float timesincepacket;

	// movement parameters for client prediction
	unsigned int moveflags;
	float movevars_wallfriction;
	float movevars_waterfriction;
	float movevars_friction;
	float movevars_timescale;
	float movevars_gravity;
	float movevars_stopspeed;
	float movevars_maxspeed;
	float movevars_spectatormaxspeed;
	float movevars_accelerate;
	float movevars_airaccelerate;
	float movevars_wateraccelerate;
	float movevars_entgravity;
	float movevars_jumpvelocity;
	float movevars_edgefriction;
	float movevars_maxairspeed;
	float movevars_stepheight;
	float movevars_airaccel_qw;
	float movevars_airaccel_qw_stretchfactor;
	float movevars_airaccel_sideways_friction;
	float movevars_airstopaccelerate;
	float movevars_airstrafeaccelerate;
	float movevars_maxairstrafespeed;
	float movevars_airstrafeaccel_qw;
	float movevars_aircontrol;
	float movevars_aircontrol_power;
	float movevars_aircontrol_penalty;
	float movevars_warsowbunny_airforwardaccel;
	float movevars_warsowbunny_accel;
	float movevars_warsowbunny_topspeed;
	float movevars_warsowbunny_turnaccel;
	float movevars_warsowbunny_backtosideratio;
	float movevars_ticrate;
	float movevars_airspeedlimit_nonqw;

	// models used by qw protocol
	int qw_modelindex_spike;
	int qw_modelindex_player;
	int qw_modelindex_flag;
	int qw_modelindex_s_explod;

	vec3_t qw_intermission_origin;
	vec3_t qw_intermission_angles;

	// 255 is the most nails the QW protocol could send
	int qw_num_nails;
	vec_t qw_nails[255][6];

	float qw_weaponkick;

	unsigned int qw_validsequence;

	unsigned int qw_deltasequence[QW_UPDATE_BACKUP];

	// csqc stuff:
	// server entity number corresponding to a clientside entity
	unsigned short csqc_server2csqcentitynumber[MAX_EDICTS];
	qbool csqc_loaded;
	vec3_t csqc_vieworigin;
	vec3_t csqc_viewangles;
	vec3_t csqc_vieworiginfromengine;
	vec3_t csqc_viewanglesfromengine;
	matrix4x4_t csqc_viewmodelmatrixfromengine;
	qbool csqc_usecsqclistener;
	matrix4x4_t csqc_listenermatrix;
	char csqc_printtextbuf[MAX_INPUTLINE];

	// collision culling data
	world_t world;

	// loc file stuff (points and boxes describing locations in the level)
	cl_locnode_t *locnodes;
	// this is updated to cl.movement_origin whenever health is < 1
	// used by %d print in say/say_team messages if cl_locs_enable is on
	vec3_t lastdeathorigin;

	// processing buffer used by R_BuildLightMap, reallocated as needed,
	// freed on each level change
	size_t buildlightmapmemorysize;
	unsigned char *buildlightmapmemory;

	// used by EntityState5_ReadUpdate
	skeleton_t *engineskeletonobjects;
}
client_state_t;

//
// cvars
//
extern cvar_t cl_name;
extern cvar_t cl_color;
extern cvar_t cl_rate;
extern cvar_t cl_rate_burstsize;
extern cvar_t cl_pmodel;
extern cvar_t cl_playermodel;
extern cvar_t cl_playerskin;

extern cvar_t rcon_password;
extern cvar_t rcon_address;

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_autofire;

extern cvar_t cl_shownet;
extern cvar_t cl_nolerp;
extern cvar_t cl_nettimesyncfactor;
extern cvar_t cl_nettimesyncboundmode;
extern cvar_t cl_nettimesyncboundtolerance;

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

extern cvar_t freelook;

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

extern cvar_t cl_autodemo;
extern cvar_t cl_autodemo_nameformat;
extern cvar_t cl_autodemo_delete;

extern cvar_t r_draweffects;

extern cvar_t cl_explosions_alpha_start;
extern cvar_t cl_explosions_alpha_end;
extern cvar_t cl_explosions_size_start;
extern cvar_t cl_explosions_size_end;
extern cvar_t cl_explosions_lifetime;
extern cvar_t cl_stainmaps;
extern cvar_t cl_stainmaps_clearonload;

extern cvar_t cl_prydoncursor;
extern cvar_t cl_prydoncursor_notrace;

extern cvar_t cl_locs_enable;

extern client_state_t cl;

extern void CL_AllocLightFlash (entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, char *cubemapname, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags);

cl_locnode_t *CL_Locs_FindNearest(const vec3_t point);
void CL_Locs_FindLocationName(char *buffer, size_t buffersize, vec3_t point);

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

//
// cl_main
//

double CL_Frame(double time);

void CL_Shutdown (void);
void CL_Init (void);

void CL_StartVideo(void);

void CL_EstablishConnection(const char *host, int firstarg);

void CL_Disconnect(void);
void CL_DisconnectEx(qbool kicked, const char *reason, ... );
void CL_Disconnect_f(cmd_state_t *cmd);

void CL_UpdateRenderEntity(entity_render_t *ent);
void CL_SetEntityColormapColors(entity_render_t *ent, int colormap);
void CL_UpdateViewEntities(void);

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

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

void CL_InitInput (void);
void CL_SendMove (void);

void CL_ValidateState(entity_state_t *s);
void CL_MoveLerpEntityStates(entity_t *ent);
void CL_LerpUpdate(entity_t *e);
void CL_ParseTEnt (void);
void CL_NewBeam (int ent, vec3_t start, vec3_t end, model_t *m, int lightning);
void CL_RelinkBeams (void);
void CL_Beam_CalculatePositions (const beam_t *b, vec3_t start, vec3_t end);
void CL_ClientMovement_Replay(void);

void CL_ClearTempEntities (void);
entity_render_t *CL_NewTempEntity (double shadertime);

void CL_Effect(vec3_t org, model_t *model, int startframe, int framecount, float framerate);

void CL_ClearState (void);
void CL_ExpandEntities(int num);
void CL_ExpandCSQCRenderEntities(int num);
void CL_SetInfo(const char *key, const char *value, qbool send, qbool allowstarkey, qbool allowmodel, qbool quiet);


void CL_UpdateWorld (void);
void CL_WriteToServer (void);
void CL_Input (void);
extern int cl_ignoremousemoves;


float CL_KeyState (kbutton_t *key);

//
// cl_cmd.c
//
/// adds the string as a clc_stringcmd to the client message.
/// (used when there is no reason to generate a local command to do it)
void CL_ForwardToServer (const char *s);

/// adds the current command line as a clc_stringcmd to the client message.
/// things like godmode, noclip, etc, are commands directed to the server,
/// so when they are typed in at the console, they will need to be forwarded.
void CL_ForwardToServer_f (cmd_state_t *cmd);
void CL_InitCommands(void);


//
// cl_demo.c
//
void CL_StopPlayback(void);
void CL_ReadDemoMessage(void);
void CL_WriteDemoMessage(sizebuf_t *mesage);

void CL_CutDemo(unsigned char **buf, fs_offset_t *filesize);
void CL_PasteDemo(unsigned char **buf, fs_offset_t *filesize);

void CL_PlayDemo(const char *demo);
void CL_NextDemo(void);
void CL_Stop_f(cmd_state_t *cmd);
void CL_Record_f(cmd_state_t *cmd);
void CL_PlayDemo_f(cmd_state_t *cmd);
void CL_TimeDemo_f(cmd_state_t *cmd);

void CL_Demo_Init(void);


#include "cl_screen.h"

extern qbool sb_showscores;

typedef enum waterlevel_e
{
	WATERLEVEL_NONE,
	WATERLEVEL_WETFEET,
	WATERLEVEL_SWIMMING,
	WATERLEVEL_SUBMERGED
}
waterlevel_t;

typedef struct cl_clientmovement_state_s
{
	// entity to be ignored for movement
	struct prvm_edict_s *self;
	// position
	vec3_t origin;
	vec3_t velocity;
	// current bounding box (different if crouched vs standing)
	vec3_t mins;
	vec3_t maxs;
	// currently on the ground
	qbool onground;
	// currently crouching
	qbool crouched;
	// what kind of water (SUPERCONTENTS_LAVA for instance)
	int watertype;
	// how deep
	waterlevel_t waterlevel;
	// weird hacks when jumping out of water
	// (this is in seconds and counts down to 0)
	float waterjumptime;

	// user command
	usercmd_t cmd;
}
cl_clientmovement_state_t;
void CL_ClientMovement_PlayerMove_Frame(cl_clientmovement_state_t *s);

// warpzone prediction hack (CSQC builtin)
void CL_RotateMoves(const matrix4x4_t *m);

typedef enum meshname_e {
	MESH_SCENE, // CSQC R_PolygonBegin, potentially also engine particles and debug stuff
	MESH_UI,
	NUM_MESHENTITIES,
} meshname_t;
extern entity_t cl_meshentities[NUM_MESHENTITIES];
extern model_t cl_meshentitymodels[NUM_MESHENTITIES];
extern const char *cl_meshentitynames[NUM_MESHENTITIES];
#define CL_Mesh_Scene() (&cl_meshentitymodels[MESH_SCENE])
#define CL_Mesh_UI() (&cl_meshentitymodels[MESH_UI])
void CL_MeshEntities_Init(void);
void CL_MeshEntities_Scene_Clear(void);
void CL_MeshEntities_Scene_AddRenderEntity(void);
void CL_MeshEntities_Scene_FinalizeRenderEntity(void);
void CL_UpdateEntityShading(void);

void CL_NewFrameReceived(int num);
void CL_ParseEntityLump(char *entitystring);
void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
void CL_RelinkLightFlashes(void);
void CL_Beam_AddPolygons(const beam_t *b);
void CL_UpdateMoveVars(void);
void CL_Locs_Reload_f(cmd_state_t *cmd);

#endif

