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