rewrote RecursiveHullCheck, no longer gets stuck on angle changes, and is generally...
[divverent/darkplaces.git] / view.c
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
24 /*
25
26 The view is allowed to move slightly from it's true position for bobbing,
27 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
28 entities sent from the server may not include everything in the pvs, especially
29 when crossing a water boudnary.
30
31 */
32
33 cvar_t  cl_rollspeed = {0, "cl_rollspeed", "200"};
34 cvar_t  cl_rollangle = {0, "cl_rollangle", "2.0"};
35
36 cvar_t  cl_bob = {0, "cl_bob","0.02"};
37 cvar_t  cl_bobcycle = {0, "cl_bobcycle","0.6"};
38 cvar_t  cl_bobup = {0, "cl_bobup","0.5"};
39
40 cvar_t  v_kicktime = {0, "v_kicktime", "0.5"};
41 cvar_t  v_kickroll = {0, "v_kickroll", "0.6"};
42 cvar_t  v_kickpitch = {0, "v_kickpitch", "0.6"};
43
44 cvar_t  v_punch = {0, "v_punch", "1"};
45
46 cvar_t  v_iyaw_cycle = {0, "v_iyaw_cycle", "2"};
47 cvar_t  v_iroll_cycle = {0, "v_iroll_cycle", "0.5"};
48 cvar_t  v_ipitch_cycle = {0, "v_ipitch_cycle", "1"};
49 cvar_t  v_iyaw_level = {0, "v_iyaw_level", "0.3"};
50 cvar_t  v_iroll_level = {0, "v_iroll_level", "0.1"};
51 cvar_t  v_ipitch_level = {0, "v_ipitch_level", "0.3"};
52
53 cvar_t  v_idlescale = {0, "v_idlescale", "0"};
54
55 cvar_t  crosshair = {CVAR_SAVE, "crosshair", "0"};
56
57 //cvar_t        gl_cshiftpercent = {0, "gl_cshiftpercent", "100"};
58 cvar_t  gl_polyblend = {CVAR_SAVE, "gl_polyblend", "1"};
59
60 cvar_t  v_centermove = {0, "v_centermove", "0.15"};
61 cvar_t  v_centerspeed = {0, "v_centerspeed","500"};
62
63 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
64
65
66 /*
67 ===============
68 V_CalcRoll
69
70 Used by view and sv_user
71 ===============
72 */
73 float V_CalcRoll (vec3_t angles, vec3_t velocity)
74 {
75         vec3_t  right;
76         float   sign;
77         float   side;
78         float   value;
79         
80         AngleVectors (angles, NULL, right, NULL);
81         side = DotProduct (velocity, right);
82         sign = side < 0 ? -1 : 1;
83         side = fabs(side);
84         
85         value = cl_rollangle.value;
86 //      if (cl.inwater)
87 //              value *= 6;
88
89         if (side < cl_rollspeed.value)
90                 side = side * value / cl_rollspeed.value;
91         else
92                 side = value;
93
94         return side*sign;
95
96 }
97
98
99 /*
100 ===============
101 V_CalcBob
102
103 ===============
104 */
105 float V_CalcBob (void)
106 {
107         float   bob;
108         float   cycle;
109         
110         cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value;
111         cycle /= cl_bobcycle.value;
112         if (cycle < cl_bobup.value)
113                 cycle = M_PI * cycle / cl_bobup.value;
114         else
115                 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
116
117 // bob is proportional to velocity in the xy plane
118 // (don't count Z, or jumping messes it up)
119
120         bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
121 //Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
122         bob = bob*0.3 + bob*0.7*sin(cycle);
123         if (bob > 4)
124                 bob = 4;
125         else if (bob < -7)
126                 bob = -7;
127         return bob;
128         
129 }
130
131
132 //=============================================================================
133
134
135 void V_StartPitchDrift (void)
136 {
137 #if 1
138         if (cl.laststop == cl.time)
139         {
140                 return;         // something else is keeping it from drifting
141         }
142 #endif
143         if (cl.nodrift || !cl.pitchvel)
144         {
145                 cl.pitchvel = v_centerspeed.value;
146                 cl.nodrift = false;
147                 cl.driftmove = 0;
148         }
149 }
150
151 void V_StopPitchDrift (void)
152 {
153         cl.laststop = cl.time;
154         cl.nodrift = true;
155         cl.pitchvel = 0;
156 }
157
158 /*
159 ===============
160 V_DriftPitch
161
162 Moves the client pitch angle towards cl.idealpitch sent by the server.
163
164 If the user is adjusting pitch manually, either with lookup/lookdown,
165 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
166
167 Drifting is enabled when the center view key is hit, mlook is released and
168 lookspring is non 0, or when
169 ===============
170 */
171 void V_DriftPitch (void)
172 {
173         float           delta, move;
174
175         if (noclip_anglehack || !cl.onground || cls.demoplayback )
176         {
177                 cl.driftmove = 0;
178                 cl.pitchvel = 0;
179                 return;
180         }
181
182 // don't count small mouse motion
183         if (cl.nodrift)
184         {
185                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
186                         cl.driftmove = 0;
187                 else
188                         cl.driftmove += cl.frametime;
189         
190                 if ( cl.driftmove > v_centermove.value)
191                 {
192                         V_StartPitchDrift ();
193                 }
194                 return;
195         }
196         
197         delta = cl.idealpitch - cl.viewangles[PITCH];
198
199         if (!delta)
200         {
201                 cl.pitchvel = 0;
202                 return;
203         }
204
205         move = cl.frametime * cl.pitchvel;
206         cl.pitchvel += cl.frametime * v_centerspeed.value;
207         
208 //Con_Printf ("move: %f (%f)\n", move, cl.frametime);
209
210         if (delta > 0)
211         {
212                 if (move > delta)
213                 {
214                         cl.pitchvel = 0;
215                         move = delta;
216                 }
217                 cl.viewangles[PITCH] += move;
218         }
219         else if (delta < 0)
220         {
221                 if (move > -delta)
222                 {
223                         cl.pitchvel = 0;
224                         move = -delta;
225                 }
226                 cl.viewangles[PITCH] -= move;
227         }
228 }
229
230
231
232
233
234 /*
235 ============================================================================== 
236  
237                                                 SCREEN FLASHES 
238  
239 ============================================================================== 
240 */ 
241  
242  
243 cshift_t        cshift_empty = { {130,80,50}, 0 };
244 cshift_t        cshift_water = { {130,80,50}, 128 };
245 cshift_t        cshift_slime = { {0,25,5}, 150 };
246 cshift_t        cshift_lava = { {255,80,0}, 150 };
247
248 byte            ramps[3][256];
249 float           v_blend[4];             // rgba 0.0 - 1.0
250
251 /*
252 ===============
253 V_ParseDamage
254 ===============
255 */
256 void V_ParseDamage (void)
257 {
258         int             armor, blood;
259         vec3_t  from;
260         int             i;
261         vec3_t  forward, right;
262         entity_t        *ent;
263         float   side;
264         float   count;
265         
266         armor = MSG_ReadByte ();
267         blood = MSG_ReadByte ();
268         for (i=0 ; i<3 ; i++)
269                 from[i] = MSG_ReadCoord ();
270
271         count = blood*0.5 + armor*0.5;
272         if (count < 10)
273                 count = 10;
274
275         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
276
277         if (gl_polyblend.value)
278         {
279                 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
280                 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
281                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
282                 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
283                         cl.cshifts[CSHIFT_DAMAGE].percent = 150;
284
285                 if (armor > blood)              
286                 {
287                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
288                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
289                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
290                 }
291                 else if (armor)
292                 {
293                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
294                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
295                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
296                 }
297                 else
298                 {
299                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
300                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
301                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
302                 }
303         }
304
305 //
306 // calculate view angle kicks
307 //
308         ent = &cl_entities[cl.viewentity];
309         
310         VectorSubtract (from, ent->render.origin, from);
311         VectorNormalize (from);
312         
313         AngleVectors (ent->render.angles, forward, right, NULL);
314
315         side = DotProduct (from, right);
316         v_dmg_roll = count*side*v_kickroll.value;
317         
318         side = DotProduct (from, forward);
319         v_dmg_pitch = count*side*v_kickpitch.value;
320
321         v_dmg_time = v_kicktime.value;
322 }
323
324
325 /*
326 ==================
327 V_cshift_f
328 ==================
329 */
330 void V_cshift_f (void)
331 {
332         cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
333         cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
334         cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
335         cshift_empty.percent = atoi(Cmd_Argv(4));
336 }
337
338
339 /*
340 ==================
341 V_BonusFlash_f
342
343 When you run over an item, the server sends this command
344 ==================
345 */
346 void V_BonusFlash_f (void)
347 {
348         if (gl_polyblend.value)
349         {
350                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
351                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
352                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
353                 cl.cshifts[CSHIFT_BONUS].percent = 50;
354         }
355 }
356
357 /*
358 =============
359 V_SetContentsColor
360
361 Underwater, lava, etc each has a color shift
362 =============
363 */
364 void V_SetContentsColor (int contents)
365 {
366         cshift_t* c;
367         c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
368         if (!gl_polyblend.value)
369         {
370                 c->percent = 0;
371                 return;
372         }
373         switch (contents)
374         {
375         case CONTENTS_EMPTY:
376         case CONTENTS_SOLID:
377                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
378                 c->destcolor[0] = cshift_empty.destcolor[0];
379                 c->destcolor[1] = cshift_empty.destcolor[1];
380                 c->destcolor[2] = cshift_empty.destcolor[2];
381                 c->percent = cshift_empty.percent;
382                 break;
383         case CONTENTS_LAVA:
384                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
385                 c->destcolor[0] = cshift_lava.destcolor[0];
386                 c->destcolor[1] = cshift_lava.destcolor[1];
387                 c->destcolor[2] = cshift_lava.destcolor[2];
388                 c->percent = cshift_lava.percent;
389                 break;
390         case CONTENTS_SLIME:
391                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
392                 c->destcolor[0] = cshift_slime.destcolor[0];
393                 c->destcolor[1] = cshift_slime.destcolor[1];
394                 c->destcolor[2] = cshift_slime.destcolor[2];
395                 c->percent = cshift_slime.percent;
396                 break;
397         default:
398                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
399                 c->destcolor[0] = cshift_water.destcolor[0];
400                 c->destcolor[1] = cshift_water.destcolor[1];
401                 c->destcolor[2] = cshift_water.destcolor[2];
402                 c->percent = cshift_water.percent;
403         }
404 }
405
406 /*
407 =============
408 V_CalcPowerupCshift
409 =============
410 */
411 void V_CalcPowerupCshift (void)
412 {
413         if (!gl_polyblend.value)
414         {
415                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
416                 return;
417         }
418         if (cl.items & IT_QUAD)
419         {
420                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
421                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
422                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
423                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
424         }
425         else if (cl.items & IT_SUIT)
426         {
427                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
428                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
429                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
430                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
431         }
432         else if (cl.items & IT_INVISIBILITY)
433         {
434                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
435                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
436                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
437                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
438         }
439         else if (cl.items & IT_INVULNERABILITY)
440         {
441                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
442                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
443                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
444                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
445         }
446         else
447                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
448 }
449
450 /*
451 =============
452 V_CalcBlend
453 =============
454 */
455 // LordHavoc: fixed V_CalcBlend
456 void V_CalcBlend (void)
457 {
458         float   r, g, b, a, a2;
459         int             j;
460
461         r = 0;
462         g = 0;
463         b = 0;
464         a = 0;
465
466 //      if (gl_cshiftpercent.value)
467 //      {
468                 for (j=0 ; j<NUM_CSHIFTS ; j++) 
469                 {
470 //                      a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
471                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
472
473                         if (!a2)
474                                 continue;
475                         if (a2 > 1)
476                                 a2 = 1;
477                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
478                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
479                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
480                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
481                 }
482                 // saturate color (to avoid blending in black)
483                 if (a)
484                 {
485                         a2 = 1 / a;
486                         r *= a2;
487                         g *= a2;
488                         b *= a2;
489                 }
490 //      }
491
492         v_blend[0] = bound(0, r * (1.0/255.0), 1);
493         v_blend[1] = bound(0, g * (1.0/255.0), 1);
494         v_blend[2] = bound(0, b * (1.0/255.0), 1);
495         v_blend[3] = bound(0, a              , 1);
496 }
497
498 /*
499 =============
500 V_UpdateBlends
501 =============
502 */
503 void V_UpdateBlends (void)
504 {
505         int             i, j;
506         qboolean        new;
507
508         V_CalcPowerupCshift ();
509         
510         new = false;
511
512         for (i=0 ; i<NUM_CSHIFTS ; i++)
513         {
514                 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
515                 {
516                         new = true;
517                         cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
518                 }
519                 for (j=0 ; j<3 ; j++)
520                         if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
521                         {
522                                 new = true;
523                                 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
524                         }
525         }
526         
527 // drop the damage value
528         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
529         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
530                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
531
532 // drop the bonus value
533         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
534         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
535                 cl.cshifts[CSHIFT_BONUS].percent = 0;
536
537         if (!new)
538                 return;
539
540         V_CalcBlend ();
541 }
542
543 /* 
544 ============================================================================== 
545  
546                                                 VIEW RENDERING 
547  
548 ============================================================================== 
549 */ 
550
551 float angledelta (float a)
552 {
553         a = ANGLEMOD(a);
554         if (a > 180)
555                 a -= 360;
556         return a;
557 }
558
559 /*
560 ==================
561 CalcGunAngle
562 ==================
563 */
564 void CalcGunAngle (void)
565 {
566         /*
567         float   yaw, pitch, move;
568         static float oldyaw = 0;
569         static float oldpitch = 0;
570         
571         yaw = r_refdef.viewangles[YAW];
572         pitch = -r_refdef.viewangles[PITCH];
573
574         yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
575         if (yaw > 10)
576                 yaw = 10;
577         if (yaw < -10)
578                 yaw = -10;
579         pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
580         if (pitch > 10)
581                 pitch = 10;
582         if (pitch < -10)
583                 pitch = -10;
584         move = cl.frametime*20;
585         if (yaw > oldyaw)
586         {
587                 if (oldyaw + move < yaw)
588                         yaw = oldyaw + move;
589         }
590         else
591         {
592                 if (oldyaw - move > yaw)
593                         yaw = oldyaw - move;
594         }
595
596         if (pitch > oldpitch)
597         {
598                 if (oldpitch + move < pitch)
599                         pitch = oldpitch + move;
600         }
601         else
602         {
603                 if (oldpitch - move > pitch)
604                         pitch = oldpitch - move;
605         }
606         
607         oldyaw = yaw;
608         oldpitch = pitch;
609
610         cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
611         cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
612         */
613         cl.viewent.render.angles[YAW] = r_refdef.viewangles[YAW];
614         cl.viewent.render.angles[PITCH] = -r_refdef.viewangles[PITCH];
615
616         cl.viewent.render.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
617         cl.viewent.render.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
618         cl.viewent.render.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
619 }
620
621 /*
622 ==============
623 V_BoundOffsets
624 ==============
625 */
626 void V_BoundOffsets (void)
627 {
628         entity_t        *ent;
629         
630         ent = &cl_entities[cl.viewentity];
631
632 // absolutely bound refresh relative to entity clipping hull
633 // so the view can never be inside a solid wall
634
635         if (r_refdef.vieworg[0] < ent->render.origin[0] - 14)
636                 r_refdef.vieworg[0] = ent->render.origin[0] - 14;
637         else if (r_refdef.vieworg[0] > ent->render.origin[0] + 14)
638                 r_refdef.vieworg[0] = ent->render.origin[0] + 14;
639         if (r_refdef.vieworg[1] < ent->render.origin[1] - 14)
640                 r_refdef.vieworg[1] = ent->render.origin[1] - 14;
641         else if (r_refdef.vieworg[1] > ent->render.origin[1] + 14)
642                 r_refdef.vieworg[1] = ent->render.origin[1] + 14;
643         if (r_refdef.vieworg[2] < ent->render.origin[2] - 22)
644                 r_refdef.vieworg[2] = ent->render.origin[2] - 22;
645         else if (r_refdef.vieworg[2] > ent->render.origin[2] + 30)
646                 r_refdef.vieworg[2] = ent->render.origin[2] + 30;
647 }
648
649 /*
650 ==============
651 V_AddIdle
652
653 Idle swaying
654 ==============
655 */
656 void V_AddIdle (void)
657 {
658         r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
659         r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
660         r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
661 }
662
663
664 /*
665 ==============
666 V_CalcViewRoll
667
668 Roll is induced by movement and damage
669 ==============
670 */
671 void V_CalcViewRoll (void)
672 {
673         float           side;
674                 
675         side = V_CalcRoll (cl_entities[cl.viewentity].render.angles, cl.velocity);
676         r_refdef.viewangles[ROLL] += side;
677
678         if (v_dmg_time > 0)
679         {
680                 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
681                 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
682                 v_dmg_time -= cl.frametime;
683         }
684
685         if (cl.stats[STAT_HEALTH] <= 0)
686         {
687                 r_refdef.viewangles[ROLL] = 80; // dead view angle
688                 return;
689         }
690
691 }
692
693
694 /*
695 ==================
696 V_CalcIntermissionRefdef
697
698 ==================
699 */
700 void V_CalcIntermissionRefdef (void)
701 {
702         entity_t        *ent, *view;
703         float           old;
704
705 // ent is the player model (visible when out of body)
706         ent = &cl_entities[cl.viewentity];
707 // view is the weapon model (only visible from inside body)
708         view = &cl.viewent;
709
710         VectorCopy (ent->render.origin, r_refdef.vieworg);
711         VectorCopy (ent->render.angles, r_refdef.viewangles);
712         view->render.model = NULL;
713
714 // always idle in intermission
715         old = v_idlescale.value;
716         v_idlescale.value = 1;
717         V_AddIdle ();
718         v_idlescale.value = old;
719 }
720
721 extern void CL_LerpUpdate(entity_t *e, int frame, int modelindex);
722
723 /*
724 ==================
725 V_CalcRefdef
726
727 ==================
728 */
729 void V_CalcRefdef (void)
730 {
731         entity_t        *ent, *view;
732         int                     i;
733         vec3_t          forward;
734         vec3_t          angles;
735         float           bob;
736 //      static float oldz = 0;
737
738         V_DriftPitch ();
739
740 // ent is the player model (visible when out of body)
741         ent = &cl_entities[cl.viewentity];
742 // view is the weapon model (only visible from inside body)
743         view = &cl.viewent;
744
745
746         if (chase_active.value)
747         {
748                 VectorCopy (ent->render.origin, r_refdef.vieworg);
749                 VectorCopy (cl.viewangles, r_refdef.viewangles);
750                 Chase_Update ();
751                 V_AddIdle ();
752         }
753         else
754         {
755         // transform the view offset by the model's matrix to get the offset from model origin for the view
756         //      if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
757         //      {
758         //              ent->render.angles[YAW] = cl.viewangles[YAW];   // the model should face the view dir
759         //              ent->render.angles[PITCH] = -cl.viewangles[PITCH];      // the model should face the view dir
760         //      }
761
762                 bob = V_CalcBob ();
763
764         // refresh position
765                 VectorCopy (ent->render.origin, r_refdef.vieworg);
766                 r_refdef.vieworg[2] += cl.viewheight + bob;
767
768                 // LordHavoc: the protocol has changed...  so this is an obsolete approach
769         // never let it sit exactly on a node line, because a water plane can
770         // dissapear when viewed with the eye exactly on it.
771         // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
772         //      r_refdef.vieworg[0] += 1.0/32;
773         //      r_refdef.vieworg[1] += 1.0/32;
774         //      r_refdef.vieworg[2] += 1.0/32;
775
776                 if (!intimerefresh)
777                         VectorCopy (cl.viewangles, r_refdef.viewangles);
778                 V_CalcViewRoll ();
779                 V_AddIdle ();
780
781         // offsets
782                 angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
783                 angles[YAW] = ent->render.angles[YAW];
784                 angles[ROLL] = ent->render.angles[ROLL];
785
786                 AngleVectors (angles, forward, NULL, NULL);
787
788                 V_BoundOffsets ();
789
790         // set up gun position
791                 VectorCopy (ent->render.origin, view->render.origin);
792                 view->render.origin[2] += cl.viewheight;
793                 VectorCopy (cl.viewangles, view->render.angles);
794
795                 CalcGunAngle ();
796
797                 for (i=0 ; i<3 ; i++)
798                 {
799                         view->render.origin[i] += forward[i]*bob*0.4;
800         //              view->render.origin[i] += right[i]*bob*0.4;
801         //              view->render.origin[i] += up[i]*bob*0.8;
802                 }
803                 view->render.origin[2] += bob;
804
805                 // FIXME: this setup code is somewhat evil (CL_LerpUpdate should be private)
806                 CL_LerpUpdate(view, cl.stats[STAT_WEAPONFRAME], cl.stats[STAT_WEAPON]);
807
808                 view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
809                 view->render.frame = cl.stats[STAT_WEAPONFRAME];
810                 view->render.colormap = -1; // no special coloring
811                 view->render.alpha = ent->render.alpha; // LordHavoc: if the player is transparent, so is the gun
812                 view->render.effects = ent->render.effects;
813                 view->render.scale = 1;
814                 VectorCopy(ent->render.colormod, view->render.colormod);
815
816         // set up the refresh position
817
818                 // LordHavoc: this never looked all that good to begin with...
819                 /*
820         // smooth out stair step ups
821         if (cl.onground && ent->render.origin[2] - oldz > 0)
822         {
823                 float steptime;
824
825                 steptime = cl.time - cl.oldtime;
826                 if (steptime < 0)
827         //FIXME         I_Error ("steptime < 0");
828                         steptime = 0;
829
830                 oldz += steptime * 80;
831                 if (oldz > ent->render.origin[2])
832                         oldz = ent->render.origin[2];
833                 if (ent->render.origin[2] - oldz > 12)
834                         oldz = ent->render.origin[2] - 12;
835                 r_refdef.vieworg[2] += oldz - ent->render.origin[2];
836                 view->render.origin[2] += oldz - ent->render.origin[2];
837         }
838         else
839                 oldz = ent->render.origin[2];
840                 */
841
842         // LordHavoc: origin view kick added
843                 if (!intimerefresh && v_punch.value)
844                 {
845                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
846                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
847                 }
848         }
849 }
850
851 /*
852 ==================
853 V_RenderView
854
855 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
856 the entity origin, so any view position inside that will be valid
857 ==================
858 */
859 void V_RenderView (void)
860 {
861         if (con_forcedup)
862                 return;
863
864         if (cl.intermission)
865                 V_CalcIntermissionRefdef ();    
866         else
867                 V_CalcRefdef ();
868
869         R_RenderView ();
870 }
871
872 //============================================================================
873
874 /*
875 =============
876 V_Init
877 =============
878 */
879 void V_Init (void)
880 {
881         Cmd_AddCommand ("v_cshift", V_cshift_f);        
882         Cmd_AddCommand ("bf", V_BonusFlash_f);
883         Cmd_AddCommand ("centerview", V_StartPitchDrift);
884
885         Cvar_RegisterVariable (&v_centermove);
886         Cvar_RegisterVariable (&v_centerspeed);
887
888         Cvar_RegisterVariable (&v_iyaw_cycle);
889         Cvar_RegisterVariable (&v_iroll_cycle);
890         Cvar_RegisterVariable (&v_ipitch_cycle);
891         Cvar_RegisterVariable (&v_iyaw_level);
892         Cvar_RegisterVariable (&v_iroll_level);
893         Cvar_RegisterVariable (&v_ipitch_level);
894
895         Cvar_RegisterVariable (&v_idlescale);
896         Cvar_RegisterVariable (&crosshair);
897 //      Cvar_RegisterVariable (&gl_cshiftpercent);
898         Cvar_RegisterVariable (&gl_polyblend);
899
900         Cvar_RegisterVariable (&cl_rollspeed);
901         Cvar_RegisterVariable (&cl_rollangle);
902         Cvar_RegisterVariable (&cl_bob);
903         Cvar_RegisterVariable (&cl_bobcycle);
904         Cvar_RegisterVariable (&cl_bobup);
905
906         Cvar_RegisterVariable (&v_kicktime);
907         Cvar_RegisterVariable (&v_kickroll);
908         Cvar_RegisterVariable (&v_kickpitch);   
909
910         Cvar_RegisterVariable (&v_punch);
911 }
912
913