]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
***map loader generates portals for the map*** (can you tell this is a big deal? :)
[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 = {"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->origin[i] += right[i]*bob*0.4;
798 //              view->origin[i] += up[i]*bob*0.8;
799         }
800         view->render.origin[2] += bob;
801
802 // fudge position around to keep amount of weapon visible
803 // roughly equal with different FOV
804
805         view->render.model = cl.model_precache[cl.stats[STAT_WEAPON]];
806         view->render.frame = cl.stats[STAT_WEAPONFRAME];
807         view->render.colormap = -1; // no special coloring
808
809 // set up the refresh position
810         if (!intimerefresh)
811         if (v_punch.value)
812         {
813                 VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
814         }
815
816 // smooth out stair step ups
817 if (cl.onground && ent->render.origin[2] - oldz > 0)
818 {
819         float steptime;
820         
821         steptime = cl.time - cl.oldtime;
822         if (steptime < 0)
823 //FIXME         I_Error ("steptime < 0");
824                 steptime = 0;
825
826         oldz += steptime * 80;
827         if (oldz > ent->render.origin[2])
828                 oldz = ent->render.origin[2];
829         if (ent->render.origin[2] - oldz > 12)
830                 oldz = ent->render.origin[2] - 12;
831         r_refdef.vieworg[2] += oldz - ent->render.origin[2];
832         view->render.origin[2] += oldz - ent->render.origin[2];
833 }
834 else
835         oldz = ent->render.origin[2];
836
837 // LordHavoc: origin view kick
838         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
839
840         if (chase_active.value)
841                 Chase_Update ();
842 }
843
844 /*
845 ==================
846 V_RenderView
847
848 The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
849 the entity origin, so any view position inside that will be valid
850 ==================
851 */
852 void V_RenderView (void)
853 {
854         if (con_forcedup)
855                 return;
856
857         if (cl.intermission)
858                 V_CalcIntermissionRefdef ();    
859         else
860                 V_CalcRefdef ();
861
862         R_RenderView ();
863 }
864
865 //============================================================================
866
867 /*
868 =============
869 V_Init
870 =============
871 */
872 void V_Init (void)
873 {
874         Cmd_AddCommand ("v_cshift", V_cshift_f);        
875         Cmd_AddCommand ("bf", V_BonusFlash_f);
876         Cmd_AddCommand ("centerview", V_StartPitchDrift);
877
878         Cvar_RegisterVariable (&v_centermove);
879         Cvar_RegisterVariable (&v_centerspeed);
880
881         Cvar_RegisterVariable (&v_iyaw_cycle);
882         Cvar_RegisterVariable (&v_iroll_cycle);
883         Cvar_RegisterVariable (&v_ipitch_cycle);
884         Cvar_RegisterVariable (&v_iyaw_level);
885         Cvar_RegisterVariable (&v_iroll_level);
886         Cvar_RegisterVariable (&v_ipitch_level);
887
888         Cvar_RegisterVariable (&v_idlescale);
889         Cvar_RegisterVariable (&crosshair);
890         Cvar_RegisterVariable (&cl_crossx);
891         Cvar_RegisterVariable (&cl_crossy);
892 //      Cvar_RegisterVariable (&gl_cshiftpercent);
893         Cvar_RegisterVariable (&gl_polyblend);
894
895         Cvar_RegisterVariable (&cl_rollspeed);
896         Cvar_RegisterVariable (&cl_rollangle);
897         Cvar_RegisterVariable (&cl_bob);
898         Cvar_RegisterVariable (&cl_bobcycle);
899         Cvar_RegisterVariable (&cl_bobup);
900
901         Cvar_RegisterVariable (&v_kicktime);
902         Cvar_RegisterVariable (&v_kickroll);
903         Cvar_RegisterVariable (&v_kickpitch);   
904
905         Cvar_RegisterVariable (&v_punch);
906 }
907
908