]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
fix for mistake in pcx loader (had two pointers swapped)
[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
65 /*
66 ===============
67 V_CalcRoll
68
69 Used by view and sv_user
70 ===============
71 */
72 float V_CalcRoll (vec3_t angles, vec3_t velocity)
73 {
74         vec3_t  right;
75         float   sign;
76         float   side;
77         float   value;
78         
79         AngleVectors (angles, NULL, right, NULL);
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 += cl.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 = cl.frametime * cl.pitchvel;
209         cl.pitchvel += cl.frametime * v_centerspeed.value;
210         
211 //Con_Printf ("move: %f (%f)\n", move, cl.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                                                 SCREEN 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;
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;                // put sbar face into pain frame
279
280         if (gl_polyblend.value)
281         {
282                 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
283                 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
284                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
285                 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
286                         cl.cshifts[CSHIFT_DAMAGE].percent = 150;
287
288                 if (armor > blood)              
289                 {
290                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
291                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
292                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
293                 }
294                 else if (armor)
295                 {
296                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
297                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
298                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
299                 }
300                 else
301                 {
302                         cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
303                         cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
304                         cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
305                 }
306         }
307
308 //
309 // calculate view angle kicks
310 //
311         ent = &cl_entities[cl.viewentity];
312         
313         VectorSubtract (from, ent->render.origin, from);
314         VectorNormalize (from);
315         
316         AngleVectors (ent->render.angles, forward, right, NULL);
317
318         side = DotProduct (from, right);
319         v_dmg_roll = count*side*v_kickroll.value;
320         
321         side = DotProduct (from, forward);
322         v_dmg_pitch = count*side*v_kickpitch.value;
323
324         v_dmg_time = v_kicktime.value;
325 }
326
327
328 /*
329 ==================
330 V_cshift_f
331 ==================
332 */
333 void V_cshift_f (void)
334 {
335         cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
336         cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
337         cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
338         cshift_empty.percent = atoi(Cmd_Argv(4));
339 }
340
341
342 /*
343 ==================
344 V_BonusFlash_f
345
346 When you run over an item, the server sends this command
347 ==================
348 */
349 void V_BonusFlash_f (void)
350 {
351         if (gl_polyblend.value)
352         {
353                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
354                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
355                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
356                 cl.cshifts[CSHIFT_BONUS].percent = 50;
357         }
358 }
359
360 /*
361 =============
362 V_SetContentsColor
363
364 Underwater, lava, etc each has a color shift
365 =============
366 */
367 void V_SetContentsColor (int contents)
368 {
369         cshift_t* c;
370         c = &cl.cshifts[CSHIFT_CONTENTS]; // just to shorten the code below
371         if (!gl_polyblend.value)
372         {
373                 c->percent = 0;
374                 return;
375         }
376         switch (contents)
377         {
378         case CONTENTS_EMPTY:
379         case CONTENTS_SOLID:
380                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
381                 c->destcolor[0] = cshift_empty.destcolor[0];
382                 c->destcolor[1] = cshift_empty.destcolor[1];
383                 c->destcolor[2] = cshift_empty.destcolor[2];
384                 c->percent = cshift_empty.percent;
385                 break;
386         case CONTENTS_LAVA:
387                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
388                 c->destcolor[0] = cshift_lava.destcolor[0];
389                 c->destcolor[1] = cshift_lava.destcolor[1];
390                 c->destcolor[2] = cshift_lava.destcolor[2];
391                 c->percent = cshift_lava.percent;
392                 break;
393         case CONTENTS_SLIME:
394                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
395                 c->destcolor[0] = cshift_slime.destcolor[0];
396                 c->destcolor[1] = cshift_slime.destcolor[1];
397                 c->destcolor[2] = cshift_slime.destcolor[2];
398                 c->percent = cshift_slime.percent;
399                 break;
400         default:
401                 //cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
402                 c->destcolor[0] = cshift_water.destcolor[0];
403                 c->destcolor[1] = cshift_water.destcolor[1];
404                 c->destcolor[2] = cshift_water.destcolor[2];
405                 c->percent = cshift_water.percent;
406         }
407 }
408
409 /*
410 =============
411 V_CalcPowerupCshift
412 =============
413 */
414 void V_CalcPowerupCshift (void)
415 {
416         if (!gl_polyblend.value)
417         {
418                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
419                 return;
420         }
421         if (cl.items & IT_QUAD)
422         {
423                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
424                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
425                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
426                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
427         }
428         else if (cl.items & IT_SUIT)
429         {
430                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
431                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
432                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
433                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
434         }
435         else if (cl.items & IT_INVISIBILITY)
436         {
437                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
438                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
439                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
440                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
441         }
442         else if (cl.items & IT_INVULNERABILITY)
443         {
444                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
445                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
446                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
447                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
448         }
449         else
450                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
451 }
452
453 /*
454 =============
455 V_CalcBlend
456 =============
457 */
458 // LordHavoc: fixed V_CalcBlend
459 void V_CalcBlend (void)
460 {
461         float   r, g, b, a, a2;
462         int             j;
463
464         r = 0;
465         g = 0;
466         b = 0;
467         a = 0;
468
469 //      if (gl_cshiftpercent.value)
470 //      {
471                 for (j=0 ; j<NUM_CSHIFTS ; j++) 
472                 {
473 //                      a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
474                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
475
476                         if (!a2)
477                                 continue;
478                         if (a2 > 1)
479                                 a2 = 1;
480                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
481                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
482                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
483                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
484                 }
485                 // saturate color (to avoid blending in black)
486                 if (a)
487                 {
488                         a2 = 1 / a;
489                         r *= a2;
490                         g *= a2;
491                         b *= a2;
492                 }
493 //      }
494
495         v_blend[0] = bound(0, r * (1.0/255.0), 1);
496         v_blend[1] = bound(0, g * (1.0/255.0), 1);
497         v_blend[2] = bound(0, b * (1.0/255.0), 1);
498         v_blend[3] = bound(0, a              , 1);
499 }
500
501 /*
502 =============
503 V_UpdateBlends
504 =============
505 */
506 void V_UpdateBlends (void)
507 {
508         int             i, j;
509         qboolean        new;
510
511         V_CalcPowerupCshift ();
512         
513         new = false;
514         
515         for (i=0 ; i<NUM_CSHIFTS ; i++)
516         {
517                 if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
518                 {
519                         new = true;
520                         cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
521                 }
522                 for (j=0 ; j<3 ; j++)
523                         if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
524                         {
525                                 new = true;
526                                 cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
527                         }
528         }
529         
530 // drop the damage value
531         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
532         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
533                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
534
535 // drop the bonus value
536         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
537         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
538                 cl.cshifts[CSHIFT_BONUS].percent = 0;
539
540         if (!new)
541                 return;
542
543         V_CalcBlend ();
544 }
545
546 /* 
547 ============================================================================== 
548  
549                                                 VIEW RENDERING 
550  
551 ============================================================================== 
552 */ 
553
554 float angledelta (float a)
555 {
556         a = ANGLEMOD(a);
557         if (a > 180)
558                 a -= 360;
559         return a;
560 }
561
562 /*
563 ==================
564 CalcGunAngle
565 ==================
566 */
567 void CalcGunAngle (void)
568 {       
569         /*
570         float   yaw, pitch, move;
571         static float oldyaw = 0;
572         static float oldpitch = 0;
573         
574         yaw = r_refdef.viewangles[YAW];
575         pitch = -r_refdef.viewangles[PITCH];
576
577         yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
578         if (yaw > 10)
579                 yaw = 10;
580         if (yaw < -10)
581                 yaw = -10;
582         pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
583         if (pitch > 10)
584                 pitch = 10;
585         if (pitch < -10)
586                 pitch = -10;
587         move = cl.frametime*20;
588         if (yaw > oldyaw)
589         {
590                 if (oldyaw + move < yaw)
591                         yaw = oldyaw + move;
592         }
593         else
594         {
595                 if (oldyaw - move > yaw)
596                         yaw = oldyaw - move;
597         }
598         
599         if (pitch > oldpitch)
600         {
601                 if (oldpitch + move < pitch)
602                         pitch = oldpitch + move;
603         }
604         else
605         {
606                 if (oldpitch - move > pitch)
607                         pitch = oldpitch - move;
608         }
609         
610         oldyaw = yaw;
611         oldpitch = pitch;
612
613         cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
614         cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
615         */
616         cl.viewent.render.angles[YAW] = r_refdef.viewangles[YAW];
617         cl.viewent.render.angles[PITCH] = -r_refdef.viewangles[PITCH];
618
619         cl.viewent.render.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
620         cl.viewent.render.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
621         cl.viewent.render.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
622 }
623
624 /*
625 ==============
626 V_BoundOffsets
627 ==============
628 */
629 void V_BoundOffsets (void)
630 {
631         entity_t        *ent;
632         
633         ent = &cl_entities[cl.viewentity];
634
635 // absolutely bound refresh relative to entity clipping hull
636 // so the view can never be inside a solid wall
637
638         if (r_refdef.vieworg[0] < ent->render.origin[0] - 14)
639                 r_refdef.vieworg[0] = ent->render.origin[0] - 14;
640         else if (r_refdef.vieworg[0] > ent->render.origin[0] + 14)
641                 r_refdef.vieworg[0] = ent->render.origin[0] + 14;
642         if (r_refdef.vieworg[1] < ent->render.origin[1] - 14)
643                 r_refdef.vieworg[1] = ent->render.origin[1] - 14;
644         else if (r_refdef.vieworg[1] > ent->render.origin[1] + 14)
645                 r_refdef.vieworg[1] = ent->render.origin[1] + 14;
646         if (r_refdef.vieworg[2] < ent->render.origin[2] - 22)
647                 r_refdef.vieworg[2] = ent->render.origin[2] - 22;
648         else if (r_refdef.vieworg[2] > ent->render.origin[2] + 30)
649                 r_refdef.vieworg[2] = ent->render.origin[2] + 30;
650 }
651
652 /*
653 ==============
654 V_AddIdle
655
656 Idle swaying
657 ==============
658 */
659 void V_AddIdle (void)
660 {
661         r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
662         r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
663         r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
664 }
665
666
667 /*
668 ==============
669 V_CalcViewRoll
670
671 Roll is induced by movement and damage
672 ==============
673 */
674 void V_CalcViewRoll (void)
675 {
676         float           side;
677                 
678         side = V_CalcRoll (cl_entities[cl.viewentity].render.angles, cl.velocity);
679         r_refdef.viewangles[ROLL] += side;
680
681         if (v_dmg_time > 0)
682         {
683                 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
684                 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
685                 v_dmg_time -= cl.frametime;
686         }
687
688         if (cl.stats[STAT_HEALTH] <= 0)
689         {
690                 r_refdef.viewangles[ROLL] = 80; // dead view angle
691                 return;
692         }
693
694 }
695
696
697 /*
698 ==================
699 V_CalcIntermissionRefdef
700
701 ==================
702 */
703 void V_CalcIntermissionRefdef (void)
704 {
705         entity_t        *ent, *view;
706         float           old;
707
708 // ent is the player model (visible when out of body)
709         ent = &cl_entities[cl.viewentity];
710 // view is the weapon model (only visible from inside body)
711         view = &cl.viewent;
712
713         VectorCopy (ent->render.origin, r_refdef.vieworg);
714         VectorCopy (ent->render.angles, r_refdef.viewangles);
715         view->render.model = NULL;
716
717 // always idle in intermission
718         old = v_idlescale.value;
719         v_idlescale.value = 1;
720         V_AddIdle ();
721         v_idlescale.value = old;
722 }
723
724 /*
725 ==================
726 V_CalcRefdef
727
728 ==================
729 */
730 void V_CalcRefdef (void)
731 {
732         entity_t        *ent, *view;
733         int                     i;
734         vec3_t          forward;
735         vec3_t          angles;
736         float           bob;
737 //      static float oldz = 0;
738
739         V_DriftPitch ();
740
741 // ent is the player model (visible when out of body)
742         ent = &cl_entities[cl.viewentity];
743 // view is the weapon model (only visible from inside body)
744         view = &cl.viewent;
745         
746
747 // transform the view offset by the model's matrix to get the offset from model origin for the view
748         if (!chase_active.value) // LordHavoc: get rid of angle problems in chase_active mode
749         {
750                 ent->render.angles[YAW] = cl.viewangles[YAW];   // the model should face the view dir
751                 ent->render.angles[PITCH] = -cl.viewangles[PITCH];      // the model should face the view dir
752         }
753                                                                                 
754         
755         bob = V_CalcBob ();
756         
757 // refresh position
758         VectorCopy (ent->render.origin, r_refdef.vieworg);
759         r_refdef.vieworg[2] += cl.viewheight + bob;
760
761         // LordHavoc: the protocol has changed...  so this is an obsolete approach
762 // never let it sit exactly on a node line, because a water plane can
763 // dissapear when viewed with the eye exactly on it.
764 // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis
765 //      r_refdef.vieworg[0] += 1.0/32;
766 //      r_refdef.vieworg[1] += 1.0/32;
767 //      r_refdef.vieworg[2] += 1.0/32;
768
769         if (!intimerefresh)
770                 VectorCopy (cl.viewangles, r_refdef.viewangles);
771         V_CalcViewRoll ();
772         V_AddIdle ();
773
774 // offsets
775         angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
776         angles[YAW] = ent->render.angles[YAW];
777         angles[ROLL] = ent->render.angles[ROLL];
778
779         AngleVectors (angles, forward, NULL, NULL);
780
781         V_BoundOffsets ();
782                 
783 // set up gun position
784         VectorCopy (cl.viewangles, view->render.angles);
785         
786         CalcGunAngle ();
787
788         VectorCopy (ent->render.origin, view->render.origin);
789         view->render.origin[2] += cl.viewheight;
790
791         for (i=0 ; i<3 ; i++)
792         {
793                 view->render.origin[i] += forward[i]*bob*0.4;
794 //              view->render.origin[i] += right[i]*bob*0.4;
795 //              view->render.origin[i] += up[i]*bob*0.8;
796         }
797         view->render.origin[2] += bob;
798
799         view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
800         view->render.frame = cl.stats[STAT_WEAPONFRAME];
801         view->render.colormap = -1; // no special coloring
802
803 // set up the refresh position
804
805         // LordHavoc: this never looked all that good to begin with...
806         /*
807 // smooth out stair step ups
808 if (cl.onground && ent->render.origin[2] - oldz > 0)
809 {
810         float steptime;
811         
812         steptime = cl.time - cl.oldtime;
813         if (steptime < 0)
814 //FIXME         I_Error ("steptime < 0");
815                 steptime = 0;
816
817         oldz += steptime * 80;
818         if (oldz > ent->render.origin[2])
819                 oldz = ent->render.origin[2];
820         if (ent->render.origin[2] - oldz > 12)
821                 oldz = ent->render.origin[2] - 12;
822         r_refdef.vieworg[2] += oldz - ent->render.origin[2];
823         view->render.origin[2] += oldz - ent->render.origin[2];
824 }
825 else
826         oldz = ent->render.origin[2];
827         */
828
829 // LordHavoc: origin view kick added
830         if (!intimerefresh && v_punch.value)
831         {
832                 VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
833                 VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
834         }
835
836         if (chase_active.value)
837                 Chase_Update ();
838 }
839
840 /*
841 ==================
842 V_RenderView
843
844 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
845 the entity origin, so any view position inside that will be valid
846 ==================
847 */
848 void V_RenderView (void)
849 {
850         if (con_forcedup)
851                 return;
852
853         if (cl.intermission)
854                 V_CalcIntermissionRefdef ();    
855         else
856                 V_CalcRefdef ();
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         Cvar_RegisterVariable (&gl_polyblend);
890
891         Cvar_RegisterVariable (&cl_rollspeed);
892         Cvar_RegisterVariable (&cl_rollangle);
893         Cvar_RegisterVariable (&cl_bob);
894         Cvar_RegisterVariable (&cl_bobcycle);
895         Cvar_RegisterVariable (&cl_bobup);
896
897         Cvar_RegisterVariable (&v_kicktime);
898         Cvar_RegisterVariable (&v_kickroll);
899         Cvar_RegisterVariable (&v_kickpitch);   
900
901         Cvar_RegisterVariable (&v_punch);
902 }
903
904