Home | History | Annotate | Download | only in client
      1 /*
      2 Copyright (C) 1996-1997 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 // view.c -- player eye positioning
     21 
     22 #include "quakedef.h"
     23 #include "r_local.h"
     24 
     25 /*
     26 
     27 The view is allowed to move slightly from it's true position for bobbing,
     28 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
     29 entities sent from the server may not include everything in the pvs, especially
     30 when crossing a water boudnary.
     31 
     32 */
     33 
     34 cvar_t	lcd_x = CVAR2("lcd_x", "0");	// FIXME: make this work sometime...
     35 
     36 cvar_t	cl_rollspeed = CVAR2("cl_rollspeed", "200");
     37 cvar_t	cl_rollangle = CVAR2("cl_rollangle", "2.0");
     38 
     39 cvar_t	cl_bob = CVAR3("cl_bob","0.02", false);
     40 cvar_t	cl_bobcycle = CVAR3("cl_bobcycle","0.6", false);
     41 cvar_t	cl_bobup = CVAR3("cl_bobup","0.5", false);
     42 
     43 cvar_t	v_kicktime = CVAR3("v_kicktime", "0.5", false);
     44 cvar_t	v_kickroll = CVAR3("v_kickroll", "0.6", false);
     45 cvar_t	v_kickpitch = CVAR3("v_kickpitch", "0.6", false);
     46 
     47 cvar_t	v_iyaw_cycle = CVAR3("v_iyaw_cycle", "2", false);
     48 cvar_t	v_iroll_cycle = CVAR3("v_iroll_cycle", "0.5", false);
     49 cvar_t	v_ipitch_cycle = CVAR3("v_ipitch_cycle", "1", false);
     50 cvar_t	v_iyaw_level = CVAR3("v_iyaw_level", "0.3", false);
     51 cvar_t	v_iroll_level = CVAR3("v_iroll_level", "0.1", false);
     52 cvar_t	v_ipitch_level = CVAR3("v_ipitch_level", "0.3", false);
     53 
     54 cvar_t	v_idlescale = CVAR3("v_idlescale", "0", false);
     55 
     56 cvar_t	crosshair = CVAR3("crosshair", "0", true);
     57 cvar_t	crosshaircolor = CVAR3("crosshaircolor", "79", true);
     58 
     59 cvar_t  cl_crossx = CVAR3("cl_crossx", "0", true);
     60 cvar_t  cl_crossy = CVAR3("cl_crossy", "0", true);
     61 
     62 #ifdef GLQUAKE
     63 cvar_t	gl_cshiftpercent = CVAR3("gl_cshiftpercent", "100", false);
     64 #endif
     65 
     66 cvar_t  v_contentblend = CVAR3("v_contentblend", "1", false);
     67 
     68 float	v_dmg_time, v_dmg_roll, v_dmg_pitch;
     69 
     70 extern	int			in_forward, in_forward2, in_back;
     71 
     72 frame_t		*view_frame;
     73 player_state_t		*view_message;
     74 
     75 /*
     76 ===============
     77 V_CalcRoll
     78 
     79 ===============
     80 */
     81 float V_CalcRoll (vec3_t angles, vec3_t velocity)
     82 {
     83 	vec3_t	forward, right, up;
     84 	float	sign;
     85 	float	side;
     86 	float	value;
     87 
     88 	AngleVectors (angles, forward, right, up);
     89 	side = DotProduct (velocity, right);
     90 	sign = side < 0 ? -1 : 1;
     91 	side = fabs(side);
     92 
     93 	value = cl_rollangle.value;
     94 
     95 	if (side < cl_rollspeed.value)
     96 		side = side * value / cl_rollspeed.value;
     97 	else
     98 		side = value;
     99 
    100 	return side*sign;
    101 
    102 }
    103 
    104 
    105 /*
    106 ===============
    107 V_CalcBob
    108 
    109 ===============
    110 */
    111 float V_CalcBob (void)
    112 {
    113 	static	double	bobtime;
    114 	static float	bob;
    115 	float	cycle;
    116 
    117 	if (cl.spectator)
    118 		return 0;
    119 
    120 	if (onground == -1)
    121 		return bob;		// just use old value
    122 
    123 	bobtime += host_frametime;
    124 	cycle = bobtime - (int)(bobtime/cl_bobcycle.value)*cl_bobcycle.value;
    125 	cycle /= cl_bobcycle.value;
    126 	if (cycle < cl_bobup.value)
    127 		cycle = M_PI * cycle / cl_bobup.value;
    128 	else
    129 		cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
    130 
    131 // bob is proportional to simulated velocity in the xy plane
    132 // (don't count Z, or jumping messes it up)
    133 
    134 	bob = sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value;
    135 	bob = bob*0.3 + bob*0.7*sin(cycle);
    136 	if (bob > 4)
    137 		bob = 4;
    138 	else if (bob < -7)
    139 		bob = -7;
    140 	return bob;
    141 
    142 }
    143 
    144 
    145 //=============================================================================
    146 
    147 
    148 cvar_t	v_centermove = CVAR3("v_centermove", "0.15", false);
    149 cvar_t	v_centerspeed = CVAR2("v_centerspeed","500");
    150 
    151 
    152 void V_StartPitchDrift (void)
    153 {
    154 #if 1
    155 	if (cl.laststop == cl.time)
    156 	{
    157 		return;		// something else is keeping it from drifting
    158 	}
    159 #endif
    160 	if (cl.nodrift || !cl.pitchvel)
    161 	{
    162 		cl.pitchvel = v_centerspeed.value;
    163 		cl.nodrift = false;
    164 		cl.driftmove = 0;
    165 	}
    166 }
    167 
    168 void V_StopPitchDrift (void)
    169 {
    170 	cl.laststop = cl.time;
    171 	cl.nodrift = true;
    172 	cl.pitchvel = 0;
    173 }
    174 
    175 /*
    176 ===============
    177 V_DriftPitch
    178 
    179 Moves the client pitch angle towards cl.idealpitch sent by the server.
    180 
    181 If the user is adjusting pitch manually, either with lookup/lookdown,
    182 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
    183 
    184 Drifting is enabled when the center view key is hit, mlook is released and
    185 lookspring is non 0, or when
    186 ===============
    187 */
    188 void V_DriftPitch (void)
    189 {
    190 	float		delta, move;
    191 
    192 	if (view_message->onground == -1 || cls.demoplayback )
    193 	{
    194 		cl.driftmove = 0;
    195 		cl.pitchvel = 0;
    196 		return;
    197 	}
    198 
    199 // don't count small mouse motion
    200 	if (cl.nodrift)
    201 	{
    202 		if ( fabs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < 200)
    203 			cl.driftmove = 0;
    204 		else
    205 			cl.driftmove += host_frametime;
    206 
    207 		if ( cl.driftmove > v_centermove.value)
    208 		{
    209 			V_StartPitchDrift ();
    210 		}
    211 		return;
    212 	}
    213 
    214 	delta = 0 - cl.viewangles[PITCH];
    215 
    216 	if (!delta)
    217 	{
    218 		cl.pitchvel = 0;
    219 		return;
    220 	}
    221 
    222 	move = host_frametime * cl.pitchvel;
    223 	cl.pitchvel += host_frametime * v_centerspeed.value;
    224 
    225 //Con_Printf ("move: %f (%f)\n", move, host_frametime);
    226 
    227 	if (delta > 0)
    228 	{
    229 		if (move > delta)
    230 		{
    231 			cl.pitchvel = 0;
    232 			move = delta;
    233 		}
    234 		cl.viewangles[PITCH] += move;
    235 	}
    236 	else if (delta < 0)
    237 	{
    238 		if (move > -delta)
    239 		{
    240 			cl.pitchvel = 0;
    241 			move = -delta;
    242 		}
    243 		cl.viewangles[PITCH] -= move;
    244 	}
    245 }
    246 
    247 
    248 
    249 
    250 
    251 /*
    252 ==============================================================================
    253 
    254 						PALETTE FLASHES
    255 
    256 ==============================================================================
    257 */
    258 
    259 
    260 cshift_t	cshift_empty = { {130,80,50}, 0 };
    261 cshift_t	cshift_water = { {130,80,50}, 128 };
    262 cshift_t	cshift_slime = { {0,25,5}, 150 };
    263 cshift_t	cshift_lava = { {255,80,0}, 150 };
    264 
    265 cvar_t		v_gamma = CVAR3("gamma", "1", true);
    266 
    267 byte		gammatable[256];	// palette is sent through this
    268 
    269 
    270 #ifdef	GLQUAKE
    271 byte		ramps[3][256];
    272 float		v_blend[4];		// rgba 0.0 - 1.0
    273 #endif	// GLQUAKE
    274 
    275 void BuildGammaTable (float g)
    276 {
    277 	int		i, inf;
    278 
    279 	if (g == 1.0)
    280 	{
    281 		for (i=0 ; i<256 ; i++)
    282 			gammatable[i] = i;
    283 		return;
    284 	}
    285 
    286 	for (i=0 ; i<256 ; i++)
    287 	{
    288 		inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
    289 		if (inf < 0)
    290 			inf = 0;
    291 		if (inf > 255)
    292 			inf = 255;
    293 		gammatable[i] = inf;
    294 	}
    295 }
    296 
    297 /*
    298 =================
    299 V_CheckGamma
    300 =================
    301 */
    302 qboolean V_CheckGamma (void)
    303 {
    304 	static float oldgammavalue;
    305 
    306 	if (v_gamma.value == oldgammavalue)
    307 		return false;
    308 	oldgammavalue = v_gamma.value;
    309 
    310 	BuildGammaTable (v_gamma.value);
    311 	vid.recalc_refdef = 1;				// force a surface cache flush
    312 
    313 	return true;
    314 }
    315 
    316 
    317 
    318 /*
    319 ===============
    320 V_ParseDamage
    321 ===============
    322 */
    323 void V_ParseDamage (void)
    324 {
    325 	int		armor, blood;
    326 	vec3_t	from;
    327 	int		i;
    328 	vec3_t	forward, right, up;
    329 	float	side;
    330 	float	count;
    331 
    332 	armor = MSG_ReadByte ();
    333 	blood = MSG_ReadByte ();
    334 	for (i=0 ; i<3 ; i++)
    335 		from[i] = MSG_ReadCoord ();
    336 
    337 	count = blood*0.5 + armor*0.5;
    338 	if (count < 10)
    339 		count = 10;
    340 
    341 	cl.faceanimtime = cl.time + 0.2;		// but sbar face into pain frame
    342 
    343 	cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
    344 	if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
    345 		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
    346 	if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
    347 		cl.cshifts[CSHIFT_DAMAGE].percent = 150;
    348 
    349 	if (armor > blood)
    350 	{
    351 		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
    352 		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
    353 		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
    354 	}
    355 	else if (armor)
    356 	{
    357 		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
    358 		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
    359 		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
    360 	}
    361 	else
    362 	{
    363 		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
    364 		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
    365 		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
    366 	}
    367 
    368 //
    369 // calculate view angle kicks
    370 //
    371 	VectorSubtract (from, cl.simorg, from);
    372 	VectorNormalize (from);
    373 
    374 	AngleVectors (cl.simangles, forward, right, up);
    375 
    376 	side = DotProduct (from, right);
    377 	v_dmg_roll = count*side*v_kickroll.value;
    378 
    379 	side = DotProduct (from, forward);
    380 	v_dmg_pitch = count*side*v_kickpitch.value;
    381 
    382 	v_dmg_time = v_kicktime.value;
    383 }
    384 
    385 
    386 /*
    387 ==================
    388 V_cshift_f
    389 ==================
    390 */
    391 void V_cshift_f (void)
    392 {
    393 	cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
    394 	cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
    395 	cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
    396 	cshift_empty.percent = atoi(Cmd_Argv(4));
    397 }
    398 
    399 
    400 /*
    401 ==================
    402 V_BonusFlash_f
    403 
    404 When you run over an item, the server sends this command
    405 ==================
    406 */
    407 void V_BonusFlash_f (void)
    408 {
    409 	cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
    410 	cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
    411 	cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
    412 	cl.cshifts[CSHIFT_BONUS].percent = 50;
    413 }
    414 
    415 /*
    416 =============
    417 V_SetContentsColor
    418 
    419 Underwater, lava, etc each has a color shift
    420 =============
    421 */
    422 void V_SetContentsColor (int contents)
    423 {
    424 	if (!v_contentblend.value) {
    425 		cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
    426 		return;
    427 	}
    428 
    429 	switch (contents)
    430 	{
    431 	case CONTENTS_EMPTY:
    432 		cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
    433 		break;
    434 	case CONTENTS_LAVA:
    435 		cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
    436 		break;
    437 	case CONTENTS_SOLID:
    438 	case CONTENTS_SLIME:
    439 		cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
    440 		break;
    441 	default:
    442 		cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
    443 	}
    444 }
    445 
    446 /*
    447 =============
    448 V_CalcPowerupCshift
    449 =============
    450 */
    451 void V_CalcPowerupCshift (void)
    452 {
    453 	if (cl.stats[STAT_ITEMS] & IT_QUAD)
    454 	{
    455 		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
    456 		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
    457 		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
    458 		cl.cshifts[CSHIFT_POWERUP].percent = 30;
    459 	}
    460 	else if (cl.stats[STAT_ITEMS] & IT_SUIT)
    461 	{
    462 		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
    463 		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
    464 		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
    465 		cl.cshifts[CSHIFT_POWERUP].percent = 20;
    466 	}
    467 	else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
    468 	{
    469 		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
    470 		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
    471 		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
    472 		cl.cshifts[CSHIFT_POWERUP].percent = 100;
    473 	}
    474 	else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
    475 	{
    476 		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
    477 		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
    478 		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
    479 		cl.cshifts[CSHIFT_POWERUP].percent = 30;
    480 	}
    481 	else
    482 		cl.cshifts[CSHIFT_POWERUP].percent = 0;
    483 }
    484 
    485 
    486 /*
    487 =============
    488 V_CalcBlend
    489 =============
    490 */
    491 #ifdef	GLQUAKE
    492 void V_CalcBlend (void)
    493 {
    494 	float	r, g, b, a, a2;
    495 	int		j;
    496 
    497 	r = 0;
    498 	g = 0;
    499 	b = 0;
    500 	a = 0;
    501 
    502 	for (j=0 ; j<NUM_CSHIFTS ; j++)
    503 	{
    504 		if (!gl_cshiftpercent.value)
    505 			continue;
    506 
    507 		a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
    508 
    509 //		a2 = (cl.cshifts[j].percent/2)/255.0;
    510 		if (!a2)
    511 			continue;
    512 		a = a + a2*(1-a);
    513 //Con_Printf ("j:%i a:%f\n", j, a);
    514 		a2 = a2/a;
    515 		r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2;
    516 		g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2;
    517 		b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2;
    518 	}
    519 
    520 	v_blend[0] = r/255.0;
    521 	v_blend[1] = g/255.0;
    522 	v_blend[2] = b/255.0;
    523 	v_blend[3] = a;
    524 	if (v_blend[3] > 1)
    525 		v_blend[3] = 1;
    526 	if (v_blend[3] < 0)
    527 		v_blend[3] = 0;
    528 }
    529 #endif
    530 
    531 /*
    532 =============
    533 V_UpdatePalette
    534 =============
    535 */
    536 #ifdef	GLQUAKE
    537 void V_UpdatePalette (void)
    538 {
    539 	int		i, j;
    540 	qboolean	new;
    541 	byte	*basepal, *newpal;
    542 	byte	pal[768];
    543 	float	r,g,b,a;
    544 	int		ir, ig, ib;
    545 	qboolean force;
    546 
    547 	V_CalcPowerupCshift ();
    548 
    549 	new = false;
    550 
    551 	for (i=0 ; i<NUM_CSHIFTS ; i++)
    552 	{
    553 		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
    554 		{
    555 			new = true;
    556 			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
    557 		}
    558 		for (j=0 ; j<3 ; j++)
    559 			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
    560 			{
    561 				new = true;
    562 				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
    563 			}
    564 	}
    565 
    566 // drop the damage value
    567 	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
    568 	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
    569 		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
    570 
    571 // drop the bonus value
    572 	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
    573 	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
    574 		cl.cshifts[CSHIFT_BONUS].percent = 0;
    575 
    576 	force = V_CheckGamma ();
    577 	if (!new && !force)
    578 		return;
    579 
    580 	V_CalcBlend ();
    581 
    582 //Con_Printf("b: %4.2f %4.2f %4.2f %4.6f\n", v_blend[0],	v_blend[1],	v_blend[2],	v_blend[3]);
    583 
    584 	a = v_blend[3];
    585 	r = 255*v_blend[0]*a;
    586 	g = 255*v_blend[1]*a;
    587 	b = 255*v_blend[2]*a;
    588 
    589 	a = 1-a;
    590 	for (i=0 ; i<256 ; i++)
    591 	{
    592 		ir = i*a + r;
    593 		ig = i*a + g;
    594 		ib = i*a + b;
    595 		if (ir > 255)
    596 			ir = 255;
    597 		if (ig > 255)
    598 			ig = 255;
    599 		if (ib > 255)
    600 			ib = 255;
    601 
    602 		ramps[0][i] = gammatable[ir];
    603 		ramps[1][i] = gammatable[ig];
    604 		ramps[2][i] = gammatable[ib];
    605 	}
    606 
    607 	basepal = host_basepal;
    608 	newpal = pal;
    609 
    610 	for (i=0 ; i<256 ; i++)
    611 	{
    612 		ir = basepal[0];
    613 		ig = basepal[1];
    614 		ib = basepal[2];
    615 		basepal += 3;
    616 
    617 		newpal[0] = ramps[0][ir];
    618 		newpal[1] = ramps[1][ig];
    619 		newpal[2] = ramps[2][ib];
    620 		newpal += 3;
    621 	}
    622 
    623 	VID_ShiftPalette (pal);
    624 }
    625 #else	// !GLQUAKE
    626 /*
    627 =============
    628 V_UpdatePalette
    629 =============
    630 */
    631 void V_UpdatePalette (void)
    632 {
    633 	int		i, j;
    634 	qboolean	new;
    635 	byte	*basepal, *newpal;
    636 	byte	pal[768];
    637 	int		r,g,b;
    638 	qboolean force;
    639 
    640 	V_CalcPowerupCshift ();
    641 
    642 	new = false;
    643 
    644 	for (i=0 ; i<NUM_CSHIFTS ; i++)
    645 	{
    646 		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
    647 		{
    648 			new = true;
    649 			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
    650 		}
    651 		for (j=0 ; j<3 ; j++)
    652 			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
    653 			{
    654 				new = true;
    655 				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
    656 			}
    657 	}
    658 
    659 // drop the damage value
    660 	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
    661 	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
    662 		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
    663 
    664 // drop the bonus value
    665 	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
    666 	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
    667 		cl.cshifts[CSHIFT_BONUS].percent = 0;
    668 
    669 	force = V_CheckGamma ();
    670 	if (!new && !force)
    671 		return;
    672 
    673 	basepal = host_basepal;
    674 	newpal = pal;
    675 
    676 	for (i=0 ; i<256 ; i++)
    677 	{
    678 		r = basepal[0];
    679 		g = basepal[1];
    680 		b = basepal[2];
    681 		basepal += 3;
    682 
    683 		for (j=0 ; j<NUM_CSHIFTS ; j++)
    684 		{
    685 			r += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[0]-r))>>8;
    686 			g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8;
    687 			b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8;
    688 		}
    689 
    690 		newpal[0] = gammatable[r];
    691 		newpal[1] = gammatable[g];
    692 		newpal[2] = gammatable[b];
    693 		newpal += 3;
    694 	}
    695 
    696 	VID_ShiftPalette (pal);
    697 }
    698 
    699 #endif	// !GLQUAKE
    700 
    701 /*
    702 ==============================================================================
    703 
    704 						VIEW RENDERING
    705 
    706 ==============================================================================
    707 */
    708 
    709 float angledelta (float a)
    710 {
    711 	a = anglemod(a);
    712 	if (a > 180)
    713 		a -= 360;
    714 	return a;
    715 }
    716 
    717 /*
    718 ==================
    719 CalcGunAngle
    720 ==================
    721 */
    722 void CalcGunAngle (void)
    723 {
    724 	float	yaw, pitch, move;
    725 	static float oldyaw = 0;
    726 	static float oldpitch = 0;
    727 
    728 	yaw = r_refdef.viewangles[YAW];
    729 	pitch = -r_refdef.viewangles[PITCH];
    730 
    731 	yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
    732 	if (yaw > 10)
    733 		yaw = 10;
    734 	if (yaw < -10)
    735 		yaw = -10;
    736 	pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
    737 	if (pitch > 10)
    738 		pitch = 10;
    739 	if (pitch < -10)
    740 		pitch = -10;
    741 	move = host_frametime*20;
    742 	if (yaw > oldyaw)
    743 	{
    744 		if (oldyaw + move < yaw)
    745 			yaw = oldyaw + move;
    746 	}
    747 	else
    748 	{
    749 		if (oldyaw - move > yaw)
    750 			yaw = oldyaw - move;
    751 	}
    752 
    753 	if (pitch > oldpitch)
    754 	{
    755 		if (oldpitch + move < pitch)
    756 			pitch = oldpitch + move;
    757 	}
    758 	else
    759 	{
    760 		if (oldpitch - move > pitch)
    761 			pitch = oldpitch - move;
    762 	}
    763 
    764 	oldyaw = yaw;
    765 	oldpitch = pitch;
    766 
    767 	cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
    768 	cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
    769 }
    770 
    771 /*
    772 ==============
    773 V_BoundOffsets
    774 ==============
    775 */
    776 void V_BoundOffsets (void)
    777 {
    778 // absolutely bound refresh reletive to entity clipping hull
    779 // so the view can never be inside a solid wall
    780 
    781 	if (r_refdef.vieworg[0] < cl.simorg[0] - 14)
    782 		r_refdef.vieworg[0] = cl.simorg[0] - 14;
    783 	else if (r_refdef.vieworg[0] > cl.simorg[0] + 14)
    784 		r_refdef.vieworg[0] = cl.simorg[0] + 14;
    785 	if (r_refdef.vieworg[1] < cl.simorg[1] - 14)
    786 		r_refdef.vieworg[1] = cl.simorg[1] - 14;
    787 	else if (r_refdef.vieworg[1] > cl.simorg[1] + 14)
    788 		r_refdef.vieworg[1] = cl.simorg[1] + 14;
    789 	if (r_refdef.vieworg[2] < cl.simorg[2] - 22)
    790 		r_refdef.vieworg[2] = cl.simorg[2] - 22;
    791 	else if (r_refdef.vieworg[2] > cl.simorg[2] + 30)
    792 		r_refdef.vieworg[2] = cl.simorg[2] + 30;
    793 }
    794 
    795 /*
    796 ==============
    797 V_AddIdle
    798 
    799 Idle swaying
    800 ==============
    801 */
    802 void V_AddIdle (void)
    803 {
    804 	r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
    805 	r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
    806 	r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
    807 
    808 	cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
    809 	cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
    810 	cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
    811 }
    812 
    813 
    814 /*
    815 ==============
    816 V_CalcViewRoll
    817 
    818 Roll is induced by movement and damage
    819 ==============
    820 */
    821 void V_CalcViewRoll (void)
    822 {
    823 	float		side;
    824 
    825 	side = V_CalcRoll (cl.simangles, cl.simvel);
    826 	r_refdef.viewangles[ROLL] += side;
    827 
    828 	if (v_dmg_time > 0)
    829 	{
    830 		r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
    831 		r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
    832 		v_dmg_time -= host_frametime;
    833 	}
    834 
    835 }
    836 
    837 
    838 /*
    839 ==================
    840 V_CalcIntermissionRefdef
    841 
    842 ==================
    843 */
    844 void V_CalcIntermissionRefdef (void)
    845 {
    846 	entity_t	*view;
    847 	float		old;
    848 
    849 // view is the weapon model
    850 	view = &cl.viewent;
    851 
    852 	VectorCopy (cl.simorg, r_refdef.vieworg);
    853 	VectorCopy (cl.simangles, r_refdef.viewangles);
    854 	view->model = NULL;
    855 
    856 // allways idle in intermission
    857 	old = v_idlescale.value;
    858 	v_idlescale.value = 1;
    859 	V_AddIdle ();
    860 	v_idlescale.value = old;
    861 }
    862 
    863 /*
    864 ==================
    865 V_CalcRefdef
    866 
    867 ==================
    868 */
    869 void V_CalcRefdef (void)
    870 {
    871 	entity_t	*view;
    872 	int			i;
    873 	vec3_t		forward, right, up;
    874 	float		bob;
    875 	static float oldz = 0;
    876 
    877 	V_DriftPitch ();
    878 
    879 // view is the weapon model (only visible from inside body)
    880 	view = &cl.viewent;
    881 
    882 	bob = V_CalcBob ();
    883 
    884 // refresh position from simulated origin
    885 	VectorCopy (cl.simorg, r_refdef.vieworg);
    886 
    887 	r_refdef.vieworg[2] += bob;
    888 
    889 // never let it sit exactly on a node line, because a water plane can
    890 // dissapear when viewed with the eye exactly on it.
    891 // the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
    892 	r_refdef.vieworg[0] += 1.0/16;
    893 	r_refdef.vieworg[1] += 1.0/16;
    894 	r_refdef.vieworg[2] += 1.0/16;
    895 
    896 	VectorCopy (cl.simangles, r_refdef.viewangles);
    897 	V_CalcViewRoll ();
    898 	V_AddIdle ();
    899 
    900 	if (view_message->flags & PF_GIB)
    901 		r_refdef.vieworg[2] += 8;	// gib view height
    902 	else if (view_message->flags & PF_DEAD)
    903 		r_refdef.vieworg[2] -= 16;	// corpse view height
    904 	else
    905 		r_refdef.vieworg[2] += 22;	// view height
    906 
    907 	if (view_message->flags & PF_DEAD)		// PF_GIB will also set PF_DEAD
    908 		r_refdef.viewangles[ROLL] = 80;	// dead view angle
    909 
    910 
    911 // offsets
    912 	AngleVectors (cl.simangles, forward, right, up);
    913 
    914 // set up gun position
    915 	VectorCopy (cl.simangles, view->angles);
    916 
    917 	CalcGunAngle ();
    918 
    919 	VectorCopy (cl.simorg, view->origin);
    920 	view->origin[2] += 22;
    921 
    922 	for (i=0 ; i<3 ; i++)
    923 	{
    924 		view->origin[i] += forward[i]*bob*0.4;
    925 //		view->origin[i] += right[i]*bob*0.4;
    926 //		view->origin[i] += up[i]*bob*0.8;
    927 	}
    928 	view->origin[2] += bob;
    929 
    930 // fudge position around to keep amount of weapon visible
    931 // roughly equal with different FOV
    932 	if (scr_viewsize.value == 110)
    933 		view->origin[2] += 1;
    934 	else if (scr_viewsize.value == 100)
    935 		view->origin[2] += 2;
    936 	else if (scr_viewsize.value == 90)
    937 		view->origin[2] += 1;
    938 	else if (scr_viewsize.value == 80)
    939 		view->origin[2] += 0.5;
    940 
    941 	if (view_message->flags & (PF_GIB|PF_DEAD) )
    942  		view->model = NULL;
    943  	else
    944 		view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
    945 	view->frame = view_message->weaponframe;
    946 	view->colormap = vid.colormap;
    947 
    948 // set up the refresh position
    949 	r_refdef.viewangles[PITCH] += cl.punchangle;
    950 
    951 // smooth out stair step ups
    952 	if ( (view_message->onground != -1) && (cl.simorg[2] - oldz > 0) )
    953 	{
    954 		float steptime;
    955 
    956 		steptime = host_frametime;
    957 
    958 		oldz += steptime * 80;
    959 		if (oldz > cl.simorg[2])
    960 			oldz = cl.simorg[2];
    961 		if (cl.simorg[2] - oldz > 12)
    962 			oldz = cl.simorg[2] - 12;
    963 		r_refdef.vieworg[2] += oldz - cl.simorg[2];
    964 		view->origin[2] += oldz - cl.simorg[2];
    965 	}
    966 	else
    967 		oldz = cl.simorg[2];
    968 }
    969 
    970 /*
    971 =============
    972 DropPunchAngle
    973 =============
    974 */
    975 void DropPunchAngle (void)
    976 {
    977 	cl.punchangle -= 10*host_frametime;
    978 	if (cl.punchangle < 0)
    979 		cl.punchangle = 0;
    980 }
    981 
    982 /*
    983 ==================
    984 V_RenderView
    985 
    986 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
    987 the entity origin, so any view position inside that will be valid
    988 ==================
    989 */
    990 extern vrect_t scr_vrect;
    991 
    992 void V_RenderView (void)
    993 {
    994 //	if (cl.simangles[ROLL])
    995 //		Sys_Error ("cl.simangles[ROLL]");	// DEBUG
    996 cl.simangles[ROLL] = 0;	// FIXME @@@
    997 
    998 	if (cls.state != ca_active)
    999 		return;
   1000 
   1001 	view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
   1002 	view_message = &view_frame->playerstate[cl.playernum];
   1003 
   1004 	DropPunchAngle ();
   1005 	if (cl.intermission)
   1006 	{	// intermission / finale rendering
   1007 		V_CalcIntermissionRefdef ();
   1008 	}
   1009 	else
   1010 	{
   1011 		V_CalcRefdef ();
   1012 	}
   1013 
   1014 	R_PushDlights ();
   1015 	R_RenderView ();
   1016 
   1017 #ifndef GLQUAKE
   1018 	if (crosshair.value)
   1019 		Draw_Crosshair();
   1020 #endif
   1021 
   1022 }
   1023 
   1024 //============================================================================
   1025 
   1026 /*
   1027 =============
   1028 V_Init
   1029 =============
   1030 */
   1031 void V_Init (void)
   1032 {
   1033 	Cmd_AddCommand ("v_cshift", V_cshift_f);
   1034 	Cmd_AddCommand ("bf", V_BonusFlash_f);
   1035 	Cmd_AddCommand ("centerview", V_StartPitchDrift);
   1036 
   1037 	Cvar_RegisterVariable (&v_centermove);
   1038 	Cvar_RegisterVariable (&v_centerspeed);
   1039 
   1040 	Cvar_RegisterVariable (&v_iyaw_cycle);
   1041 	Cvar_RegisterVariable (&v_iroll_cycle);
   1042 	Cvar_RegisterVariable (&v_ipitch_cycle);
   1043 	Cvar_RegisterVariable (&v_iyaw_level);
   1044 	Cvar_RegisterVariable (&v_iroll_level);
   1045 	Cvar_RegisterVariable (&v_ipitch_level);
   1046 
   1047 	Cvar_RegisterVariable (&v_contentblend);
   1048 
   1049 	Cvar_RegisterVariable (&v_idlescale);
   1050 	Cvar_RegisterVariable (&crosshaircolor);
   1051 	Cvar_RegisterVariable (&crosshair);
   1052 	Cvar_RegisterVariable (&cl_crossx);
   1053 	Cvar_RegisterVariable (&cl_crossy);
   1054 #ifdef GLQUAKE
   1055 	Cvar_RegisterVariable (&gl_cshiftpercent);
   1056 #endif
   1057 
   1058 	Cvar_RegisterVariable (&cl_rollspeed);
   1059 	Cvar_RegisterVariable (&cl_rollangle);
   1060 	Cvar_RegisterVariable (&cl_bob);
   1061 	Cvar_RegisterVariable (&cl_bobcycle);
   1062 	Cvar_RegisterVariable (&cl_bobup);
   1063 
   1064 	Cvar_RegisterVariable (&v_kicktime);
   1065 	Cvar_RegisterVariable (&v_kickroll);
   1066 	Cvar_RegisterVariable (&v_kickpitch);
   1067 
   1068 	BuildGammaTable (1.0);	// no gamma yet
   1069 	Cvar_RegisterVariable (&v_gamma);
   1070 }
   1071 
   1072 
   1073