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