1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // cl_view.c -- player rendering positioning
21 
22 #include "client.h"
23 #include "localent.h"
24 
25 extern int deferred_model_index;
26 
27 //=============
28 //
29 // development tools for weapons
30 //
31 int			gun_frame;
32 struct model_s	*gun_model;
33 
34 //=============
35 
36 cvar_t		*crosshair;
37 cvar_t		*cl_testparticles;
38 cvar_t		*cl_testentities;
39 cvar_t		*cl_testlights;
40 cvar_t		*cl_testblend;
41 
42 cvar_t		*cl_stats;
43 
44 cvar_t		*cl_drawfps;
45 cvar_t		*cl_drawfps_x;
46 cvar_t		*cl_drawfps_y;
47 
48 cvar_t		*cl_stfu_ilkhan;
49 cvar_t		*cl_drawmaptime_x;
50 cvar_t		*cl_drawmaptime_y;
51 
52 cvar_t		*cl_defermodels;
53 cvar_t		*cl_particlecount;
54 
55 cvar_t		*scr_crosshair_x;
56 cvar_t		*scr_crosshair_y;
57 
58 extern cvar_t		*scr_showturtle;
59 
60 int			r_numdlights;
61 dlight_t	r_dlights[MAX_DLIGHTS];
62 
63 int			r_numentities;
64 entity_t	r_entities[MAX_ENTITIES];
65 
66 int			r_numparticles;
67 particle_t	*r_particles;//[MAX_PARTICLES];
68 
69 lightstyle_t	r_lightstyles[MAX_LIGHTSTYLES];
70 
71 char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
72 int num_cl_weaponmodels;
73 
74 
75 /*
76 ====================
77 V_ClearScene
78 
79 Specifies the model that will be used as the world
80 ====================
81 */
V_ClearScene(void)82 void V_ClearScene (void)
83 {
84 	r_numdlights = 0;
85 	r_numentities = 0;
86 	r_numparticles = 0;
87 }
88 
89 /*
90 =====================
91 V_AddEntity
92 
93 =====================
94 */
V_AddEntity(entity_t * ent)95 void V_AddEntity (entity_t *ent)
96 {
97 	if (r_numentities >= MAX_ENTITIES)
98 		return;
99 
100 	r_entities[r_numentities++] = *ent;
101 }
102 
103 
104 /*
105 =====================
106 V_AddParticle
107 
108 =====================
109 */
V_AddParticle(vec3_t org,unsigned color,float alpha)110 void V_AddParticle (vec3_t org, unsigned color, float alpha)
111 {
112 	particle_t	*p;
113 
114 	if (r_numparticles >= cl_particlecount->intvalue)
115 		return;
116 
117 	if (color > 0xFF)
118 		Com_Error (ERR_DROP, "V_AddParticle: bad color %d", color);
119 
120 	p = &r_particles[r_numparticles++];
121 	FastVectorCopy (*org, p->origin);
122 	p->color = color;
123 	p->alpha = alpha;
124 }
125 
126 /*
127 =====================
128 V_AddLight
129 
130 =====================
131 */
V_AddLight(vec3_t org,float intensity,float r,float g,float b)132 void V_AddLight (vec3_t org, float intensity, float r, float g, float b)
133 {
134 	dlight_t	*dl;
135 
136 	if (r_numdlights >= MAX_DLIGHTS)
137 		return;
138 
139 	dl = &r_dlights[r_numdlights++];
140 	FastVectorCopy (*org, dl->origin);
141 	dl->intensity = intensity;
142 	dl->color[0] = r;
143 	dl->color[1] = g;
144 	dl->color[2] = b;
145 }
146 
147 
148 /*
149 =====================
150 V_AddLightStyle
151 
152 =====================
153 */
V_AddLightStyle(int style,float r,float g,float b)154 void V_AddLightStyle (int style, float r, float g, float b)
155 {
156 	lightstyle_t	*ls;
157 
158 	if (style < 0 || style > MAX_LIGHTSTYLES)
159 		Com_Error (ERR_DROP, "Bad light style %i", style);
160 
161 	ls = &r_lightstyles[style];
162 
163 	ls->white = r+g+b;
164 	ls->rgb[0] = r;
165 	ls->rgb[1] = g;
166 	ls->rgb[2] = b;
167 }
168 
169 /*
170 ================
171 V_TestParticles
172 
173 If cl_testparticles is set, create 4096 particles in the view
174 ================
175 */
V_TestParticles(void)176 void V_TestParticles (void)
177 {
178 	particle_t	*p;
179 	int			i, j;
180 	float		d, r, u;
181 
182 	r_numparticles = cl_particlecount->intvalue;
183 	for (i=0 ; i<r_numparticles ; i++)
184 	{
185 		d = i*0.25f;
186 		r = 4*((i&7)-3.5f);
187 		u = 4*(((i>>3)&7)-3.5f);
188 		p = &r_particles[i];
189 
190 		for (j=0 ; j<3 ; j++)
191 			p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*d +
192 			cl.v_right[j]*r + cl.v_up[j]*u;
193 
194 		p->color = 8;
195 		p->alpha = cl_testparticles->value;
196 	}
197 }
198 
199 /*
200 ================
201 V_TestEntities
202 
203 If cl_testentities is set, create 32 player models
204 ================
205 */
V_TestEntities(void)206 void V_TestEntities (void)
207 {
208 	int			i, j;
209 	float		f, r;
210 	entity_t	*ent;
211 
212 	r_numentities = 32;
213 	memset (r_entities, 0, sizeof(r_entities));
214 
215 	for (i=0 ; i<r_numentities ; i++)
216 	{
217 		ent = &r_entities[i];
218 
219 		r = 64 * ( (i%4) - 1.5f );
220 		f = (float)(64 * (i/4) + 128);
221 
222 		for (j=0 ; j<3 ; j++)
223 			ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
224 			cl.v_right[j]*r;
225 
226 		ent->model = cl.baseclientinfo.model;
227 		ent->skin = cl.baseclientinfo.skin;
228 	}
229 }
230 
231 /*
232 ================
233 V_TestLights
234 
235 If cl_testlights is set, create 32 lights models
236 ================
237 */
238 /*
239 ================
240 V_TestLights
241 
242 If cl_testlights is set, create 32 lights models
243 ================
244 */
V_TestLights(void)245 void V_TestLights (void)
246 {
247 	int			i, j;
248 	float		f, r;
249 	dlight_t	*dl;
250 
251 	r_numdlights = 32;
252 	memset (r_dlights, 0, sizeof(r_dlights));
253 
254 	for (i=0 ; i<r_numdlights ; i++)
255 	{
256 		dl = &r_dlights[i];
257 
258 		r = 64 * ( (i%4) - 1.5f );
259 		f = 64 * (i/4.0f) + 128;
260 
261 		for (j=0 ; j<3 ; j++)
262 			dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
263 			cl.v_right[j]*r;
264 		dl->color[0] = (float)(((i%6)+1) & 1);
265 		dl->color[1] = (float)((((i%6)+1) & 2)>>1);
266 		dl->color[2] = (float)((((i%6)+1) & 4)>>2);
267 		dl->intensity = 200;
268 	}
269 }
270 
271 //===================================================================
272 
273 /*
274 =================
275 CL_PrepRefresh
276 
277 Call before entering a new level, or after changing dlls
278 =================
279 */
CL_PrepRefresh(void)280 void CL_PrepRefresh (void)
281 {
282 	char		mapname[MAX_QPATH];
283 
284 	int			i;
285 	int			maxclients;
286 
287 	float		rotate;
288 	vec3_t		axis;
289 
290 	if (!cl.configstrings[CS_MODELS+1][0])
291 		return;		// no map loaded
292 
293 	SCR_AddDirtyPoint (0, 0);
294 	SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
295 
296 	// let the render dll load the map
297 	Q_strncpy (mapname, cl.configstrings[CS_MODELS+1] + 5, sizeof(mapname)-1);	// skip "maps/"
298 	mapname[strlen(mapname)-4] = 0;		// cut off ".bsp"
299 
300 	Cvar_ForceSet ("$mapname", mapname);
301 
302 	// register models, pics, and skins
303 	Com_Printf ("Map: %s\r", LOG_CLIENT, mapname);
304 	SCR_UpdateScreen ();
305 
306 	// clear tents - dangling model pointers
307 	CL_ClearTEnts ();
308 
309 	re.BeginRegistration (mapname);
310 
311 	Com_Printf ("                                     \r", LOG_CLIENT);
312 
313 	Sys_SendKeyEvents ();
314 
315 	Netchan_Transmit (&cls.netchan, 0, NULL);
316 
317 	// precache status bar pics
318 	Com_Printf ("pics\r", LOG_CLIENT);
319 	SCR_UpdateScreen ();
320 	SCR_TouchPics ();
321 	//Com_Printf ("                                     \r", LOG_CLIENT);
322 
323 	Com_Printf ("models\r", LOG_CLIENT);
324 	SCR_UpdateScreen ();
325 	CL_RegisterTEntModels ();
326 
327 	num_cl_weaponmodels = 1;
328 	strcpy(cl_weaponmodels[0], "weapon.md2");
329 
330 	for (i=1 ; i<MAX_MODELS && cl.configstrings[CS_MODELS+i][0] ; i++)
331 	{
332 		if (cl.configstrings[CS_MODELS+i][0] == '#')
333 		{
334 			// special player weapon model
335 			if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
336 			{
337 				strncpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS+i]+1,
338 					sizeof(cl_weaponmodels[num_cl_weaponmodels]) - 1);
339 				num_cl_weaponmodels++;
340 			}
341 		}
342 	}
343 
344 	Netchan_Transmit (&cls.netchan, 0, NULL);
345 
346 	//modelindex 1 is always world.
347 	//is 0 ever used?
348 
349 	cl.model_clip[0] = NULL;
350 	cl.model_draw[0] = NULL;
351 
352 	cl.model_draw[1] = re.RegisterModel (cl.configstrings[CS_MODELS+1]);
353 
354 	if (cl.configstrings[CS_MODELS+1][0] == '*')
355 		cl.model_clip[1] = CM_InlineModel (cl.configstrings[CS_MODELS+1]);
356 	else
357 		cl.model_clip[1] = NULL;
358 
359 	if (cl_defermodels->intvalue && !cl_timedemo->intvalue)
360 	{
361 		deferred_model_index = 1;
362 		for (i = 2; i < MAX_MODELS; i++)
363 		{
364 			cl.model_clip[i] = NULL;
365 			cl.model_draw[i] = NULL;
366 		}
367 	}
368 	else
369 	{
370 		deferred_model_index = MAX_MODELS;
371 		for (i = 2; i < MAX_MODELS; i++)
372 		{
373 			if (!cl.configstrings[CS_MODELS+i][0])
374 				continue;
375 
376 			if (cl.configstrings[CS_MODELS+i][0] != '#')
377 			{
378 				cl.model_draw[i] = re.RegisterModel (cl.configstrings[CS_MODELS+i]);
379 				if (cl.configstrings[CS_MODELS+i][0] == '*')
380 					cl.model_clip[i] = CM_InlineModel (cl.configstrings[CS_MODELS+i]);
381 				else
382 					cl.model_clip[i] = NULL;
383 			}
384 
385 			Com_Printf ("%s                         \r", LOG_CLIENT, cl.configstrings[CS_MODELS+i]);
386 			//SCR_UpdateScreen ();
387 			Sys_SendKeyEvents ();
388 		}
389 	}
390 
391 	Com_Printf ("images\r                             ", LOG_CLIENT);
392 	SCR_UpdateScreen ();
393 	for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i][0] ; i++)
394 	{
395 		re.RegisterPic (cl.configstrings[CS_IMAGES+i]);
396 		Sys_SendKeyEvents ();	// pump message loop
397 	}
398 
399 	Com_Printf ("                                     \r", LOG_CLIENT);
400 
401 	maxclients = cl.maxclients;
402 
403 	Com_Printf ("clients\r", LOG_CLIENT);
404 	SCR_UpdateScreen ();
405 
406 	//must be zeroed to flush out old model pointers
407 	memset (&cl.clientinfo, 0, sizeof(cl.clientinfo));
408 
409 	for (i=0 ; i<maxclients ; i++)
410 	{
411 		if (!cl.configstrings[CS_PLAYERSKINS+i][0])
412 			continue;
413 
414 		SCR_UpdateScreen ();
415 		Sys_SendKeyEvents ();
416 		CL_ParseClientinfo (i);
417 	}
418 
419 	Netchan_Transmit (&cls.netchan, 0, NULL);
420 
421 	CL_LoadClientinfo (&cl.baseclientinfo, "");
422 
423 	// set sky textures and speed
424 	Com_Printf ("sky             \r", LOG_CLIENT);
425 	SCR_UpdateScreen ();
426 
427 	rotate = (float)atof (cl.configstrings[CS_SKYROTATE]);
428 
429 	if (sscanf (cl.configstrings[CS_SKYAXIS], "%f %f %f",
430 		&axis[0], &axis[1], &axis[2]) != 3)
431 	{
432 		VectorClear (axis);
433 	}
434 
435 	re.SetSky (cl.configstrings[CS_SKY], rotate, axis);
436 	Com_Printf ("   \r", LOG_CLIENT);
437 
438 	// the renderer can now free unneeded stuff
439 	if (deferred_model_index == MAX_MODELS)
440 		re.EndRegistration ();
441 
442 	// clear any lines of console text
443 	Con_ClearNotify ();
444 
445 	SCR_UpdateScreen ();
446 
447 	cl.refresh_prepped = true;
448 	cl.force_refdef = true;	// make sure we have a valid refdef
449 
450 	//probably out of date
451 	cl.frame.valid = false;
452 
453 	//reset current list
454 	cl.refdef.num_entities = 0;
455 	cl.refdef.entities = NULL;
456 
457 	cl.refdef.num_particles = 0;
458 	cl.refdef.particles = NULL;
459 
460 	cl.refdef.num_dlights = 0;
461 	cl.refdef.dlights = NULL;
462 	//cl.refdef.lightstyles = 0;
463 
464 	S_StopAllSounds ();
465 
466 	//can't use cls.realtime - could be out of date :)
467 	//if (!cl_timedemo->intvalue)
468 		//cl.defer_rendering = (int)(Sys_Milliseconds() + (cl_defertimer->value * 1000));
469 	//else
470 		cl.defer_rendering = 0;
471 
472 	// start the cd track
473 #ifdef CD_AUDIO
474 	CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
475 #endif
476 }
477 
478 /*
479 ====================
480 CalcFov
481 ====================
482 */
CalcFov(float fov_x,int width,int height)483 float CalcFov (float fov_x, int width, int height)
484 {
485 	static float	a;
486 	static float	last_fov;
487 	static int		lw, lh;
488 
489 	//r1: only calculate if needed
490 	if (width != lw || height != lh || *(int*)&fov_x != *(int*)&last_fov)
491 	{
492 		float			x;
493 
494 		if (fov_x < 1 || fov_x > 179)
495 			Com_Error (ERR_DROP, "Bad fov: %f", fov_x);
496 
497 		x = width / (float)tan(fov_x/360*M_PI);
498 
499 		a = (float)atan (height/x);
500 		a = a*360/M_PI;
501 
502 		last_fov = fov_x;
503 		lw = width;
504 		lh = height;
505 	}
506 
507 	return a;
508 }
509 
510 //============================================================================
511 
512 // gun frame debugging functions
V_Gun_Next_f(void)513 void V_Gun_Next_f (void)
514 {
515 	gun_frame++;
516 	Com_Printf ("frame %i\n", LOG_CLIENT, gun_frame);
517 }
518 
V_Gun_Prev_f(void)519 void V_Gun_Prev_f (void)
520 {
521 	gun_frame--;
522 	if (gun_frame < 0)
523 		gun_frame = 0;
524 	Com_Printf ("frame %i\n", LOG_CLIENT, gun_frame);
525 }
526 
V_Gun_Model_f(void)527 void V_Gun_Model_f (void)
528 {
529 	char	name[MAX_QPATH];
530 
531 	if (Cmd_Argc() != 2)
532 	{
533 		gun_model = NULL;
534 		return;
535 	}
536 	Com_sprintf (name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
537 	gun_model = re.RegisterModel (name);
538 }
539 
540 //============================================================================
541 
542 
543 /*
544 =================
545 SCR_DrawCrosshair
546 =================
547 */
SCR_DrawCrosshair(void)548 __inline void SCR_DrawCrosshair (void)
549 {
550 	if (!crosshair->intvalue)
551 		return;
552 
553 	/*if (crosshair->modified)
554 	{
555 		crosshair->modified = false;
556 		SCR_TouchPics ();
557 	}*/
558 
559 	if (!crosshair_pic[0])
560 		return;
561 
562 	re.DrawPic (scr_vrect.x + scr_crosshair_x->intvalue + ((scr_vrect.width - crosshair_width)>>1)
563 	, scr_vrect.y + + scr_crosshair_y->intvalue + ((scr_vrect.height - crosshair_height)>>1), crosshair_pic);
564 }
565 
566 int spc = 0;
567 int fps = 0;
568 int frames_this_second = 0;
569 uint32 frames_seconds = 0;
570 
571 static const char rateMsg[] = "RATEDROP";
572 static const char frameMsg[] = "OLDFRAME";
573 static const char parseMsg[] = "OLDPARSE";
574 static const char overflowMsg[] = "OVERFLOW";
575 
576 extern int EXPORT entitycmpfnc( const entity_t *, const entity_t * );
577 extern int			scr_draw_loading;
578 /*
579 ==================
580 V_RenderView
581 
582 ==================
583 */
584 #ifdef CL_STEREO_SUPPORT
V_RenderView(float stereo_separation)585 void V_RenderView( float stereo_separation )
586 #else
587 void V_RenderView(void)
588 #endif
589 {
590 	if (cls.state != ca_active)
591 		return;
592 
593 	if (!cl.refresh_prepped)
594 		return;			// still loading
595 
596 	if (cl_timedemo->intvalue)
597 	{
598 		if (!cl.timedemo_start)
599 			cl.timedemo_start = Sys_Milliseconds ();
600 		cl.timedemo_frames++;
601 	}
602 
603 	// an invalid frame will just use the exact previous refdef
604 	// we can't use the old frame if the video mode has changed, though...
605 	if ( cl.frame.valid && (cl.force_refdef || !cl_paused->intvalue) )
606 	{
607 		cl.force_refdef = false;
608 
609 		V_ClearScene ();
610 
611 		// build a refresh entity list and calc cl.sim*
612 		// this also calls CL_CalcViewValues which loads
613 		// v_forward, etc.
614 		CL_AddEntities ();
615 
616 		if (cl_testparticles->intvalue)
617 			V_TestParticles ();
618 
619 		if (cl_testentities->intvalue)
620 			V_TestEntities ();
621 
622 		if (cl_testlights->intvalue)
623 			V_TestLights ();
624 
625 		if (cl_testblend->value)
626 		{
627 			cl.refdef.blend[0] = 1;
628 			cl.refdef.blend[1] = 0.5;
629 			cl.refdef.blend[2] = 0.25;
630 			cl.refdef.blend[3] = 0.5;
631 		}
632 
633 		// offset vieworg appropriately if we're doing stereo separation
634 #ifdef CL_STEREO_SUPPORT
635 		if ( stereo_separation != 0 )
636 		{
637 			vec3_t tmp;
638 
639 			VectorScale( cl.v_right, stereo_separation, tmp );
640 			VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
641 		}
642 #endif
643 
644 		// never let it sit exactly on a node line, because a water plane can
645 		// dissapear when viewed with the eye exactly on it.
646 		// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
647 		cl.refdef.vieworg[0] += 1.0f/16;
648 		cl.refdef.vieworg[1] += 1.0f/16;
649 		cl.refdef.vieworg[2] += 1.0f/16;
650 
651 		cl.refdef.x = scr_vrect.x;
652 		cl.refdef.y = scr_vrect.y;
653 		cl.refdef.width = scr_vrect.width;
654 		cl.refdef.height = scr_vrect.height;
655 		cl.refdef.fov_y = CalcFov (cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
656 		cl.refdef.time = cl.time * 0.001f;
657 
658 		cl.refdef.areabits = cl.frame.areabits;
659 
660 		if (!cl_add_entities->intvalue)
661 			r_numentities = 0;
662 		if (!cl_add_particles->intvalue)
663 			r_numparticles = 0;
664 		if (!cl_add_lights->intvalue)
665 			r_numdlights = 0;
666 		if (!cl_add_blend->intvalue)
667 		{
668 			VectorClear (cl.refdef.blend);
669 		}
670 
671 		if (cl.defer_rendering > cls.realtime)
672 			r_numentities = 0;
673 
674 		cl.refdef.num_entities = r_numentities;
675 		cl.refdef.entities = r_entities;
676 		cl.refdef.num_particles = r_numparticles;
677 		cl.refdef.particles = r_particles;
678 		cl.refdef.num_dlights = r_numdlights;
679 		cl.refdef.dlights = r_dlights;
680 		cl.refdef.lightstyles = r_lightstyles;
681 
682 		cl.refdef.rdflags = cl.frame.playerstate.rdflags;
683 
684 		// sort entities for better cache locality
685         qsort( cl.refdef.entities, cl.refdef.num_entities, sizeof( cl.refdef.entities[0] ), (int (EXPORT *)(const void *, const void *))entitycmpfnc );
686 	}
687 
688 	if (!cl.force_refdef)
689 		re.RenderFrame (&cl.refdef);
690 
691 	if (cl_stats->intvalue)
692 		Com_Printf ("ent:%i  lt:%i  part:%i\n", LOG_CLIENT, r_numentities, r_numdlights, r_numparticles);
693 	//if ( log_stats->value && ( log_stats_file != 0 ) )
694 	//	fprintf( log_stats_file, "%i,%i,%i,",r_numentities, r_numdlights, r_numparticles);
695 
696 	if ((unsigned)(curtime - frames_seconds) >= 1000)
697 	{
698 		spc = serverPacketCount;
699 		fps = frames_this_second;
700 		frames_this_second = 0;
701 		serverPacketCount = 0;
702 		frames_seconds = curtime;
703 	}
704 
705 	if (cl_shownet->intvalue == -1)
706 	{
707 		char buff[16];
708 		int x;
709 		Com_sprintf (buff, sizeof(buff), "%d", spc);
710 		for (x = 0; x < strlen(buff); x++) {
711 			re.DrawChar (viddef.width-26+x*8, viddef.height / 2, 128 + buff[x]);
712 		}
713 	}
714 
715 	//r1: fps display
716 	if (cl_drawfps->intvalue)
717 	{
718 		int		x, len;
719 		char	buff[16];
720 
721 		frames_this_second++;
722 
723 		if (cl_drawfps->intvalue == 2)
724 		{
725 			fps = (int)(1.0f / cls.frametime);
726 		}
727 
728 		len = Com_sprintf (buff, sizeof(buff), "%d", fps);
729 		for (x = 0; x < len; x++)
730 		{
731 			re.DrawChar (viddef.width-26+x*8+cl_drawfps_x->intvalue, viddef.height - 16 + cl_drawfps_y->intvalue, 128 + buff[x]);
732 		}
733 	}
734 
735 	//r1: map timer (don't ask)
736 	if (cl_stfu_ilkhan->intvalue)
737 	{
738 		char buff[16];
739 		int x, len;
740 		int secs = (cl.frame.serverframe % 600) / 10;
741 		int mins = cl.frame.serverframe / 600;
742 
743 		len = Com_sprintf (buff, sizeof(buff), "%d:%.2d", mins, secs);
744 		for (x = 0; x < len; x++)
745 		{
746 			re.DrawChar (x * 8 + cl_drawmaptime_x->intvalue, viddef.height - 8 + cl_drawmaptime_y->intvalue, 128 + buff[x]);
747 		}
748 	}
749 
750 	//FIXME: incorrect use of scr_ prefix
751 	if (scr_showturtle->intvalue && !scr_draw_loading && cls.state == ca_active)
752 	{
753 		frame_t *old;
754 
755 		if (cl.surpressCount)
756 		{
757 			int x;
758 			for (x=0 ; x<sizeof(rateMsg)-1; x++)
759 				re.DrawChar (1+(x*8), 250, 128 + rateMsg[x] );
760 		}
761 
762 		old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK];
763 		if (old->serverframe != cl.frame.deltaframe)
764 		{
765 			// The frame that the server did the delta from
766 			// is too old, so we can't reconstruct it properly.
767 			int x;
768 			for (x=0 ; x<sizeof(frameMsg)-1; x++)
769 				re.DrawChar (1+(x*8), 266, 128 + frameMsg[x] );
770 		}
771 
772 		if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128)
773 		{
774 			int x;
775 			for (x=0 ; x<sizeof(parseMsg)-1; x++)
776 				re.DrawChar (1+(x*8), 282, 128 + parseMsg[x] );
777 		}
778 
779 		if (noFrameFromServerPacket > 2)
780 		{
781 			int x;
782 			for (x=0 ; x<sizeof(overflowMsg)-1; x++)
783 				re.DrawChar (1+(x*8), 298, 128 + overflowMsg[x] );
784 		}
785 	}
786 
787 	SCR_AddDirtyPoint (scr_vrect.x, scr_vrect.y);
788 	SCR_AddDirtyPoint (scr_vrect.x+scr_vrect.width-1,
789 		scr_vrect.y+scr_vrect.height-1);
790 
791 	SCR_DrawCrosshair ();
792 }
793 
794 
795 /*
796 =============
797 V_Viewpos_f
798 =============
799 */
V_Viewpos_f(void)800 void V_Viewpos_f (void)
801 {
802 	Com_Printf ("(%i %i %i) : %i\n", LOG_CLIENT, (int)cl.refdef.vieworg[0],
803 		(int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
804 		(int)cl.refdef.viewangles[YAW]);
805 }
806 
OnCrossHairChange(cvar_t * self,char * old,char * newValue)807 void OnCrossHairChange (cvar_t *self, char *old, char *newValue)
808 {
809 	self->modified = false;
810 	SCR_TouchPics();
811 }
812 
813 //ick
814 extern cparticle_t	*particles;
815 void CL_ClearParticles (int num);
816 static int num_particles;
_particlecount_changed(cvar_t * self,char * old,char * newValue)817 void _particlecount_changed (cvar_t *self, char *old, char *newValue)
818 {
819 	int		count;
820 
821 	//update cvar if we had to cap
822 	if (self->intvalue < 1024)
823 	{
824 		Cvar_Set (self->name, "1024");
825 		return;
826 	}
827 	else if (self->intvalue > 1048576)
828 	{
829 		Cvar_Set (self->name, "1048576");
830 		return;
831 	}
832 
833 	if (particles)
834 	{
835 		CL_ClearParticles (num_particles);
836 		Z_Free (particles);
837 	}
838 
839 	if (r_particles)
840 	{
841 		r_numparticles = 0;
842 		Z_Free (r_particles);
843 	}
844 
845 	count = self->intvalue;
846 
847 	particles = Z_TagMalloc (count * sizeof(*particles), TAGMALLOC_CL_PARTICLES);
848 	r_particles = Z_TagMalloc (count * sizeof(*r_particles), TAGMALLOC_CL_PARTICLES);
849 
850 	//allocated uninit
851 	CL_ClearParticles (count);
852 
853 	num_particles = count;
854 }
855 
856 /*
857 =============
858 V_Init
859 =============
860 */
V_Init(void)861 void V_Init (void)
862 {
863 	Cmd_AddCommand ("gun_next", V_Gun_Next_f);
864 	Cmd_AddCommand ("gun_prev", V_Gun_Prev_f);
865 	Cmd_AddCommand ("gun_model", V_Gun_Model_f);
866 
867 	Cmd_AddCommand ("viewpos", V_Viewpos_f);
868 
869 	crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE);
870 	crosshair->changed = OnCrossHairChange;
871 
872 	cl_particlecount = Cvar_Get ("cl_particlecount", "16384", 0);
873 	cl_particlecount->changed = _particlecount_changed;
874 	_particlecount_changed (cl_particlecount, cl_particlecount->string, cl_particlecount->string);
875 
876 	cl_testblend = Cvar_Get ("cl_testblend", "0", 0);
877 	cl_testparticles = Cvar_Get ("cl_testparticles", "0", 0);
878 	cl_testentities = Cvar_Get ("cl_testentities", "0", 0);
879 	cl_testlights = Cvar_Get ("cl_testlights", "0", 0);
880 
881 	cl_stats = Cvar_Get ("cl_stats", "0", 0);
882 
883 	cl_drawfps = Cvar_Get ("cl_drawfps", "0", 0);
884 	cl_drawfps_x = Cvar_Get ("cl_drawfps_x", "0", 0);
885 	cl_drawfps_y = Cvar_Get ("cl_drawfps_y", "0", 0);
886 
887 	cl_stfu_ilkhan = Cvar_Get ("cl_drawmaptime", "0", 0);
888 	cl_drawmaptime_x = Cvar_Get ("cl_drawmaptime_x", "0", 0);
889 	cl_drawmaptime_y = Cvar_Get ("cl_drawmaptime_y", "0", 0);
890 
891 	cl_defermodels = Cvar_Get ("cl_defermodels", "1", 0);
892 
893 	scr_crosshair_x = Cvar_Get ("scr_crosshair_x", "0", 0);
894 	scr_crosshair_y = Cvar_Get ("scr_crosshair_y", "0", 0);
895 }
896