1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// cl_main.c  -- client main loop
21
22#include "quakedef.h"
23
24// we need to declare some mouse variables here, because the menu system
25// references them even when on a unix system.
26
27// these two are not intended to be set directly
28cvar_t	cl_name = CVAR3("_cl_name", "player", true);
29cvar_t	cl_color = CVAR3("_cl_color", "0", true);
30
31cvar_t	cl_shownet = CVAR2("cl_shownet","0");	// can be 0, 1, or 2
32cvar_t	cl_nolerp = CVAR2("cl_nolerp","0");
33
34cvar_t	lookspring = CVAR3("lookspring","0", true);
35cvar_t	lookstrafe = CVAR3("lookstrafe","0", true);
36cvar_t	sensitivity = CVAR3("sensitivity","3", true);
37
38cvar_t	m_pitch = CVAR3("m_pitch","0.022", true);
39cvar_t	m_yaw = CVAR3("m_yaw","0.022", true);
40cvar_t	m_forward = CVAR3("m_forward","1", true);
41cvar_t	m_side = CVAR3("m_side","0.8", true);
42
43
44client_static_t	cls;
45client_state_t	cl;
46// FIXME: put these on hunk?
47efrag_t			cl_efrags[MAX_EFRAGS];
48entity_t		cl_entities[MAX_EDICTS];
49entity_t		cl_static_entities[MAX_STATIC_ENTITIES];
50lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
51dlight_t		cl_dlights[MAX_DLIGHTS];
52
53int				cl_numvisedicts;
54entity_t		*cl_visedicts[MAX_VISEDICTS];
55
56/*
57=====================
58CL_ClearState
59
60=====================
61*/
62void CL_ClearState (void)
63{
64	int			i;
65
66	if (!sv.active)
67		Host_ClearMemory ();
68
69// wipe the entire cl structure
70	memset (&cl, 0, sizeof(cl));
71
72	SZ_Clear (&cls.message);
73
74// clear other arrays
75	memset (cl_efrags, 0, sizeof(cl_efrags));
76	memset (cl_entities, 0, sizeof(cl_entities));
77	memset (cl_dlights, 0, sizeof(cl_dlights));
78	memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
79	memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
80	memset (cl_beams, 0, sizeof(cl_beams));
81
82//
83// allocate the efrags and chain together into a free list
84//
85	cl.free_efrags = cl_efrags;
86	for (i=0 ; i<MAX_EFRAGS-1 ; i++)
87		cl.free_efrags[i].entnext = &cl.free_efrags[i+1];
88	cl.free_efrags[i].entnext = NULL;
89}
90
91/*
92=====================
93CL_Disconnect
94
95Sends a disconnect message to the server
96This is also called on Host_Error, so it shouldn't cause any errors
97=====================
98*/
99void CL_Disconnect (void)
100{
101// stop sounds (especially looping!)
102	S_StopAllSounds (true);
103
104// bring the console down and fade the colors back to normal
105//	SCR_BringDownConsole ();
106
107// if running a local server, shut it down
108	if (cls.demoplayback)
109		CL_StopPlayback ();
110	else if (cls.state == ca_connected)
111	{
112		if (cls.demorecording)
113			CL_Stop_f ();
114
115		Con_DPrintf ("Sending clc_disconnect\n");
116		SZ_Clear (&cls.message);
117		MSG_WriteByte (&cls.message, clc_disconnect);
118		NET_SendUnreliableMessage (cls.netcon, &cls.message);
119		SZ_Clear (&cls.message);
120		NET_Close (cls.netcon);
121
122		cls.state = ca_disconnected;
123		if (sv.active)
124			Host_ShutdownServer(false);
125	}
126
127	cls.demoplayback = cls.timedemo = false;
128	cls.signon = 0;
129}
130
131void CL_Disconnect_f (void)
132{
133	CL_Disconnect ();
134	if (sv.active)
135		Host_ShutdownServer (false);
136}
137
138
139
140
141/*
142=====================
143CL_EstablishConnection
144
145Host should be either "local" or a net address to be passed on
146=====================
147*/
148void CL_EstablishConnection (const char *host)
149{
150	if (cls.state == ca_dedicated)
151		return;
152
153	if (cls.demoplayback)
154		return;
155
156	CL_Disconnect ();
157
158	cls.netcon = NET_Connect (host);
159	if (!cls.netcon)
160		Host_Error ("CL_Connect: connect failed\n");
161	Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
162
163	cls.demonum = -1;			// not in the demo loop now
164	cls.state = ca_connected;
165	cls.signon = 0;				// need all the signon messages before playing
166}
167
168/*
169=====================
170CL_SignonReply
171
172An svc_signonnum has been received, perform a client side setup
173=====================
174*/
175void CL_SignonReply (void)
176{
177	char 	str[8192];
178
179Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
180
181	switch (cls.signon)
182	{
183	case 1:
184		MSG_WriteByte (&cls.message, clc_stringcmd);
185		MSG_WriteString (&cls.message, "prespawn");
186		break;
187
188	case 2:
189		MSG_WriteByte (&cls.message, clc_stringcmd);
190		MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
191
192		MSG_WriteByte (&cls.message, clc_stringcmd);
193		MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
194
195		MSG_WriteByte (&cls.message, clc_stringcmd);
196		sprintf (str, "spawn %s", cls.spawnparms);
197		MSG_WriteString (&cls.message, str);
198		break;
199
200	case 3:
201		MSG_WriteByte (&cls.message, clc_stringcmd);
202		MSG_WriteString (&cls.message, "begin");
203		Cache_Report ();		// print remaining memory
204		break;
205
206	case 4:
207		SCR_EndLoadingPlaque ();		// allow normal screen updates
208		break;
209	}
210}
211
212/*
213=====================
214CL_NextDemo
215
216Called to play the next demo in the demo loop
217=====================
218*/
219void CL_NextDemo (void)
220{
221	char	str[1024];
222
223	if (cls.demonum == -1)
224		return;		// don't play demos
225
226	SCR_BeginLoadingPlaque ();
227
228	if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
229	{
230		cls.demonum = 0;
231		if (!cls.demos[cls.demonum][0])
232		{
233			Con_Printf ("No demos listed with startdemos\n");
234			cls.demonum = -1;
235			return;
236		}
237	}
238
239	sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
240	Cbuf_InsertText (str);
241	cls.demonum++;
242}
243
244/*
245==============
246CL_PrintEntities_f
247==============
248*/
249void CL_PrintEntities_f (void)
250{
251	entity_t	*ent;
252	int			i;
253
254	for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
255	{
256		Con_Printf ("%3i:",i);
257		if (!ent->model)
258		{
259			Con_Printf ("EMPTY\n");
260			continue;
261		}
262		Con_Printf ("%s:%2i  (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
263		,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
264	}
265}
266
267
268/*
269===============
270SetPal
271
272Debugging tool, just flashes the screen
273===============
274*/
275void SetPal (int i)
276{
277#if 0
278	static int old;
279	byte	pal[768];
280	int		c;
281
282	if (i == old)
283		return;
284	old = i;
285
286	if (i==0)
287		VID_SetPalette (host_basepal);
288	else if (i==1)
289	{
290		for (c=0 ; c<768 ; c+=3)
291		{
292			pal[c] = 0;
293			pal[c+1] = 255;
294			pal[c+2] = 0;
295		}
296		VID_SetPalette (pal);
297	}
298	else
299	{
300		for (c=0 ; c<768 ; c+=3)
301		{
302			pal[c] = 0;
303			pal[c+1] = 0;
304			pal[c+2] = 255;
305		}
306		VID_SetPalette (pal);
307	}
308#endif
309}
310
311/*
312===============
313CL_AllocDlight
314
315===============
316*/
317dlight_t *CL_AllocDlight (int key)
318{
319	int		i;
320	dlight_t	*dl;
321
322// first look for an exact key match
323	if (key)
324	{
325		dl = cl_dlights;
326		for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
327		{
328			if (dl->key == key)
329			{
330				memset (dl, 0, sizeof(*dl));
331				dl->key = key;
332				return dl;
333			}
334		}
335	}
336
337// then look for anything else
338	dl = cl_dlights;
339	for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
340	{
341		if (dl->die < cl.time)
342		{
343			memset (dl, 0, sizeof(*dl));
344			dl->key = key;
345			return dl;
346		}
347	}
348
349	dl = &cl_dlights[0];
350	memset (dl, 0, sizeof(*dl));
351	dl->key = key;
352	return dl;
353}
354
355
356/*
357===============
358CL_DecayLights
359
360===============
361*/
362void CL_DecayLights (void)
363{
364	int			i;
365	dlight_t	*dl;
366	float		time;
367
368	time = cl.time - cl.oldtime;
369
370	dl = cl_dlights;
371	for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
372	{
373		if (dl->die < cl.time || !dl->radius)
374			continue;
375
376		dl->radius -= time*dl->decay;
377		if (dl->radius < 0)
378			dl->radius = 0;
379	}
380}
381
382
383/*
384===============
385CL_LerpPoint
386
387Determines the fraction between the last two messages that the objects
388should be put at.
389===============
390*/
391float	CL_LerpPoint (void)
392{
393	float	f, frac;
394
395	f = cl.mtime[0] - cl.mtime[1];
396
397	if (!f || cl_nolerp.value || cls.timedemo || sv.active)
398	{
399		cl.time = cl.mtime[0];
400		return 1;
401	}
402
403	if (f > 0.1)
404	{	// dropped packet, or start of demo
405		cl.mtime[1] = cl.mtime[0] - 0.1;
406		f = 0.1;
407	}
408	frac = (cl.time - cl.mtime[1]) / f;
409//Con_Printf ("frac: %f\n",frac);
410	if (frac < 0)
411	{
412		if (frac < -0.01)
413		{
414SetPal(1);
415			cl.time = cl.mtime[1];
416//				Con_Printf ("low frac\n");
417		}
418		frac = 0;
419	}
420	else if (frac > 1)
421	{
422		if (frac > 1.01)
423		{
424SetPal(2);
425			cl.time = cl.mtime[0];
426//				Con_Printf ("high frac\n");
427		}
428		frac = 1;
429	}
430	else
431		SetPal(0);
432
433	return frac;
434}
435
436
437/*
438===============
439CL_RelinkEntities
440===============
441*/
442void CL_RelinkEntities (void)
443{
444	entity_t	*ent;
445	int			i, j;
446	float		frac, f, d;
447	vec3_t		delta;
448	float		bobjrotate;
449	vec3_t		oldorg;
450	dlight_t	*dl;
451
452// determine partial update time
453	frac = CL_LerpPoint ();
454
455	cl_numvisedicts = 0;
456
457//
458// interpolate player info
459//
460	for (i=0 ; i<3 ; i++)
461		cl.velocity[i] = cl.mvelocity[1][i] +
462			frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
463
464	if (cls.demoplayback)
465	{
466	// interpolate the angles
467		for (j=0 ; j<3 ; j++)
468		{
469			d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
470			if (d > 180)
471				d -= 360;
472			else if (d < -180)
473				d += 360;
474			cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
475		}
476	}
477
478	bobjrotate = anglemod(100*cl.time);
479
480// start on the entity after the world
481	for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
482	{
483		if (!ent->model)
484		{	// empty slot
485			if (ent->forcelink)
486				R_RemoveEfrags (ent);	// just became empty
487			continue;
488		}
489
490// if the object wasn't included in the last packet, remove it
491		if (ent->msgtime != cl.mtime[0])
492		{
493			ent->model = NULL;
494			continue;
495		}
496
497		VectorCopy (ent->origin, oldorg);
498
499		if (ent->forcelink)
500		{	// the entity was not updated in the last message
501			// so move to the final spot
502			VectorCopy (ent->msg_origins[0], ent->origin);
503			VectorCopy (ent->msg_angles[0], ent->angles);
504		}
505		else
506		{	// if the delta is large, assume a teleport and don't lerp
507			f = frac;
508			for (j=0 ; j<3 ; j++)
509			{
510				delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
511				if (delta[j] > 100 || delta[j] < -100)
512					f = 1;		// assume a teleportation, not a motion
513			}
514
515		// interpolate the origin and angles
516			for (j=0 ; j<3 ; j++)
517			{
518				ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
519
520				d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
521				if (d > 180)
522					d -= 360;
523				else if (d < -180)
524					d += 360;
525				ent->angles[j] = ent->msg_angles[1][j] + f*d;
526			}
527
528		}
529
530// rotate binary objects locally
531		if (ent->model->flags & EF_ROTATE)
532			ent->angles[1] = bobjrotate;
533
534		if (ent->effects & EF_BRIGHTFIELD)
535			R_EntityParticles (ent);
536#ifdef QUAKE2
537		if (ent->effects & EF_DARKFIELD)
538			R_DarkFieldParticles (ent);
539#endif
540		if (ent->effects & EF_MUZZLEFLASH)
541		{
542			vec3_t		fv, rv, uv;
543
544			dl = CL_AllocDlight (i);
545			VectorCopy (ent->origin,  dl->origin);
546			dl->origin[2] += 16;
547			AngleVectors (ent->angles, fv, rv, uv);
548
549			VectorMA (dl->origin, 18, fv, dl->origin);
550			dl->radius = 200 + (rand()&31);
551			dl->minlight = 32;
552			dl->die = cl.time + 0.1;
553		}
554		if (ent->effects & EF_BRIGHTLIGHT)
555		{
556			dl = CL_AllocDlight (i);
557			VectorCopy (ent->origin,  dl->origin);
558			dl->origin[2] += 16;
559			dl->radius = 400 + (rand()&31);
560			dl->die = cl.time + 0.001;
561		}
562		if (ent->effects & EF_DIMLIGHT)
563		{
564			dl = CL_AllocDlight (i);
565			VectorCopy (ent->origin,  dl->origin);
566			dl->radius = 200 + (rand()&31);
567			dl->die = cl.time + 0.001;
568		}
569#ifdef QUAKE2
570		if (ent->effects & EF_DARKLIGHT)
571		{
572			dl = CL_AllocDlight (i);
573			VectorCopy (ent->origin,  dl->origin);
574			dl->radius = 200.0 + (rand()&31);
575			dl->die = cl.time + 0.001;
576			dl->dark = true;
577		}
578		if (ent->effects & EF_LIGHT)
579		{
580			dl = CL_AllocDlight (i);
581			VectorCopy (ent->origin,  dl->origin);
582			dl->radius = 200;
583			dl->die = cl.time + 0.001;
584		}
585#endif
586
587		if (ent->model->flags & EF_GIB)
588			R_RocketTrail (oldorg, ent->origin, 2);
589		else if (ent->model->flags & EF_ZOMGIB)
590			R_RocketTrail (oldorg, ent->origin, 4);
591		else if (ent->model->flags & EF_TRACER)
592			R_RocketTrail (oldorg, ent->origin, 3);
593		else if (ent->model->flags & EF_TRACER2)
594			R_RocketTrail (oldorg, ent->origin, 5);
595		else if (ent->model->flags & EF_ROCKET)
596		{
597			R_RocketTrail (oldorg, ent->origin, 0);
598			dl = CL_AllocDlight (i);
599			VectorCopy (ent->origin, dl->origin);
600			dl->radius = 200;
601			dl->die = cl.time + 0.01;
602		}
603		else if (ent->model->flags & EF_GRENADE)
604			R_RocketTrail (oldorg, ent->origin, 1);
605		else if (ent->model->flags & EF_TRACER3)
606			R_RocketTrail (oldorg, ent->origin, 6);
607
608		ent->forcelink = false;
609
610		if (i == cl.viewentity && !chase_active.value)
611			continue;
612
613#ifdef QUAKE2
614		if ( ent->effects & EF_NODRAW )
615			continue;
616#endif
617		if (cl_numvisedicts < MAX_VISEDICTS)
618		{
619			cl_visedicts[cl_numvisedicts] = ent;
620			cl_numvisedicts++;
621		}
622	}
623
624}
625
626
627/*
628===============
629CL_ReadFromServer
630
631Read all incoming data from the server
632===============
633*/
634int CL_ReadFromServer (void)
635{
636	int		ret;
637
638	cl.oldtime = cl.time;
639	cl.time += host_frametime;
640
641	do
642	{
643		ret = CL_GetMessage ();
644		if (ret == -1)
645			Host_Error ("CL_ReadFromServer: lost server connection");
646		if (!ret)
647			break;
648
649		cl.last_received_message = realtime;
650		CL_ParseServerMessage ();
651	} while (ret && cls.state == ca_connected);
652
653	if (cl_shownet.value)
654		Con_Printf ("\n");
655
656	CL_RelinkEntities ();
657	CL_UpdateTEnts ();
658
659//
660// bring the links up to date
661//
662	return 0;
663}
664
665/*
666=================
667CL_SendCmd
668=================
669*/
670void CL_SendCmd (void)
671{
672	usercmd_t		cmd;
673
674	if (cls.state != ca_connected)
675		return;
676
677	if (cls.signon == SIGNONS)
678	{
679	// get basic movement from keyboard
680		CL_BaseMove (&cmd);
681
682	// allow mice or other external controllers to add to the move
683		IN_Move (&cmd);
684
685	// send the unreliable message
686		CL_SendMove (&cmd);
687
688	}
689
690	if (cls.demoplayback)
691	{
692		SZ_Clear (&cls.message);
693		return;
694	}
695
696// send the reliable message
697	if (!cls.message.cursize)
698		return;		// no message at all
699
700	if (!NET_CanSendMessage (cls.netcon))
701	{
702		Con_DPrintf ("CL_WriteToServer: can't send\n");
703		return;
704	}
705
706	if (NET_SendMessage (cls.netcon, &cls.message) == -1)
707		Host_Error ("CL_WriteToServer: lost server connection");
708
709	SZ_Clear (&cls.message);
710}
711
712/*
713=================
714CL_Init
715=================
716*/
717void CL_Init (void)
718{
719	SZ_Alloc (&cls.message, 1024);
720
721	CL_InitInput ();
722	CL_InitTEnts ();
723
724//
725// register our commands
726//
727	Cvar_RegisterVariable (&cl_name);
728	Cvar_RegisterVariable (&cl_color);
729	Cvar_RegisterVariable (&cl_upspeed);
730	Cvar_RegisterVariable (&cl_forwardspeed);
731	Cvar_RegisterVariable (&cl_backspeed);
732	Cvar_RegisterVariable (&cl_sidespeed);
733	Cvar_RegisterVariable (&cl_movespeedkey);
734	Cvar_RegisterVariable (&cl_yawspeed);
735	Cvar_RegisterVariable (&cl_pitchspeed);
736	Cvar_RegisterVariable (&cl_anglespeedkey);
737	Cvar_RegisterVariable (&cl_shownet);
738	Cvar_RegisterVariable (&cl_nolerp);
739	Cvar_RegisterVariable (&lookspring);
740	Cvar_RegisterVariable (&lookstrafe);
741	Cvar_RegisterVariable (&sensitivity);
742
743	Cvar_RegisterVariable (&m_pitch);
744	Cvar_RegisterVariable (&m_yaw);
745	Cvar_RegisterVariable (&m_forward);
746	Cvar_RegisterVariable (&m_side);
747
748//	Cvar_RegisterVariable (&cl_autofire);
749
750	Cmd_AddCommand ("entities", CL_PrintEntities_f);
751	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
752	Cmd_AddCommand ("record", CL_Record_f);
753	Cmd_AddCommand ("stop", CL_Stop_f);
754	Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
755	Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
756}
757
758