]> git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
added CVAR_SAVE and CVAR_NOTIFY flags to cvar_t structure (at the beginning), updated...
[xonotic/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 /*
722 ==================
723 V_CalcRefdef
724
725 ==================
726 */
727 void V_CalcRefdef (void)
728 {
729         entity_t        *ent, *view;
730         int                     i;
731         vec3_t          forward;
732         vec3_t          angles;
733         float           bob;
734 //      static float oldz = 0;
735
736         V_DriftPitch ();
737
738 // ent is the player model (visible when out of body)
739         ent = &cl_entities[cl.viewentity];
740 // view is the weapon model (only visible from inside body)
741         view = &cl.viewent;
742
743
744         if (chase_active.value)
745         {
746                 VectorCopy (ent->render.origin, r_refdef.vieworg);
747                 VectorCopy (cl.viewangles, r_refdef.viewangles);
748                 Chase_Update ();
749                 V_AddIdle ();
750         }
751         else
752         {
753         // transform the view offset by the model's matrix to get the offset from model origin for the view
754         //      if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
755         //      {
756         //              ent->render.angles[YAW] = cl.viewangles[YAW];   // the model should face the view dir
757         //              ent->render.angles[PITCH] = -cl.viewangles[PITCH];      // the model should face the view dir
758         //      }
759
760                 bob = V_CalcBob ();
761
762         // refresh position
763                 VectorCopy (ent->render.origin, r_refdef.vieworg);
764                 r_refdef.vieworg[2] += cl.viewheight + bob;
765
766                 // LordHavoc: the protocol has changed...  so this is an obsolete approach
767         // never let it sit exactly on a node line, because a water plane can
768         // dissapear when viewed with the eye exactly on it.
769         // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
770         //      r_refdef.vieworg[0] += 1.0/32;
771         //      r_refdef.vieworg[1] += 1.0/32;
772         //      r_refdef.vieworg[2] += 1.0/32;
773
774                 if (!intimerefresh)
775                         VectorCopy (cl.viewangles, r_refdef.viewangles);
776                 V_CalcViewRoll ();
777                 V_AddIdle ();
778
779         // offsets
780                 angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
781                 angles[YAW] = ent->render.angles[YAW];
782                 angles[ROLL] = ent->render.angles[ROLL];
783
784                 AngleVectors (angles, forward, NULL, NULL);
785
786                 V_BoundOffsets ();
787
788         // set up gun position
789                 VectorCopy (cl.viewangles, view->render.angles);
790
791                 CalcGunAngle ();
792
793                 VectorCopy (ent->render.origin, view->render.origin);
794                 view->render.origin[2] += cl.viewheight;
795
796                 for (i=0 ; i<3 ; i++)
797                 {
798                         view->render.origin[i] += forward[i]*bob*0.4;
799         //              view->render.origin[i] += right[i]*bob*0.4;
800         //              view->render.origin[i] += up[i]*bob*0.8;
801                 }
802                 view->render.origin[2] += bob;
803
804                 view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
805                 view->render.frame = cl.stats[STAT_WEAPONFRAME];
806                 view->render.colormap = -1; // no special coloring
807
808         // set up the refresh position
809
810                 // LordHavoc: this never looked all that good to begin with...
811                 /*
812         // smooth out stair step ups
813         if (cl.onground && ent->render.origin[2] - oldz > 0)
814         {
815                 float steptime;
816
817                 steptime = cl.time - cl.oldtime;
818                 if (steptime < 0)
819         //FIXME         I_Error ("steptime < 0");
820                         steptime = 0;
821
822                 oldz += steptime * 80;
823                 if (oldz > ent->render.origin[2])
824                         oldz = ent->render.origin[2];
825                 if (ent->render.origin[2] - oldz > 12)
826                         oldz = ent->render.origin[2] - 12;
827                 r_refdef.vieworg[2] += oldz - ent->render.origin[2];
828                 view->render.origin[2] += oldz - ent->render.origin[2];
829         }
830         else
831                 oldz = ent->render.origin[2];
832                 */
833
834         // LordHavoc: origin view kick added
835                 if (!intimerefresh && v_punch.value)
836                 {
837                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
838                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
839                 }
840         }
841 }
842
843 /*
844 ==================
845 V_RenderView
846
847 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
848 the entity origin, so any view position inside that will be valid
849 ==================
850 */
851 void V_RenderView (void)
852 {
853         if (con_forcedup)
854                 return;
855
856         if (cl.intermission)
857                 V_CalcIntermissionRefdef ();    
858         else
859                 V_CalcRefdef ();
860
861         R_RenderView ();
862 }
863
864 //============================================================================
865
866 /*
867 =============
868 V_Init
869 =============
870 */
871 void V_Init (void)
872 {
873         Cmd_AddCommand ("v_cshift", V_cshift_f);        
874         Cmd_AddCommand ("bf", V_BonusFlash_f);
875         Cmd_AddCommand ("centerview", V_StartPitchDrift);
876
877         Cvar_RegisterVariable (&v_centermove);
878         Cvar_RegisterVariable (&v_centerspeed);
879
880         Cvar_RegisterVariable (&v_iyaw_cycle);
881         Cvar_RegisterVariable (&v_iroll_cycle);
882         Cvar_RegisterVariable (&v_ipitch_cycle);
883         Cvar_RegisterVariable (&v_iyaw_level);
884         Cvar_RegisterVariable (&v_iroll_level);
885         Cvar_RegisterVariable (&v_ipitch_level);
886
887         Cvar_RegisterVariable (&v_idlescale);
888         Cvar_RegisterVariable (&crosshair);
889 //      Cvar_RegisterVariable (&gl_cshiftpercent);
890         Cvar_RegisterVariable (&gl_polyblend);
891
892         Cvar_RegisterVariable (&cl_rollspeed);
893         Cvar_RegisterVariable (&cl_rollangle);
894         Cvar_RegisterVariable (&cl_bob);
895         Cvar_RegisterVariable (&cl_bobcycle);
896         Cvar_RegisterVariable (&cl_bobup);
897
898         Cvar_RegisterVariable (&v_kicktime);
899         Cvar_RegisterVariable (&v_kickroll);
900         Cvar_RegisterVariable (&v_kickpitch);   
901
902         Cvar_RegisterVariable (&v_punch);
903 }
904
905