made darkplaces able to compile as C++ again, and fixed all conversion warnings when...
[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 #include "cl_collision.h"
24
25 /*
26
27 The view is allowed to move slightly from it's true position for bobbing,
28 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
29 entities sent from the server may not include everything in the pvs, especially
30 when crossing a water boudnary.
31
32 */
33
34 cvar_t cl_rollspeed = {0, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
35 cvar_t cl_rollangle = {0, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
36
37 cvar_t cl_bob = {0, "cl_bob","0.02", "view bobbing amount"};
38 cvar_t cl_bobcycle = {0, "cl_bobcycle","0.6", "view bobbing speed"};
39 cvar_t cl_bobup = {0, "cl_bobup","0.5", "view bobbing adjustment that makes the up or down swing of the bob last longer"};
40
41 cvar_t cl_bobmodel = {0, "cl_bobmodel", "1", "enables gun bobbing"};
42 cvar_t cl_bobmodel_side = {0, "cl_bobmodel_side", "0.05", "gun bobbing sideways sway amount"};
43 cvar_t cl_bobmodel_up = {0, "cl_bobmodel_up", "0.02", "gun bobbing upward movement amount"};
44 cvar_t cl_bobmodel_speed = {0, "cl_bobmodel_speed", "7", "gun bobbing speed"};
45
46 cvar_t v_kicktime = {0, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
47 cvar_t v_kickroll = {0, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
48 cvar_t v_kickpitch = {0, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
49
50 cvar_t v_iyaw_cycle = {0, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
51 cvar_t v_iroll_cycle = {0, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
52 cvar_t v_ipitch_cycle = {0, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
53 cvar_t v_iyaw_level = {0, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
54 cvar_t v_iroll_level = {0, "v_iroll_level", "0.1", "v_idlescale roll amount"};
55 cvar_t v_ipitch_level = {0, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
56
57 cvar_t v_idlescale = {0, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
58
59 cvar_t crosshair = {CVAR_SAVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
60
61 cvar_t v_centermove = {0, "v_centermove", "0.15", "how long before the view begins to center itself (if freelook/+mlook/+jlook/+klook are off)"};
62 cvar_t v_centerspeed = {0, "v_centerspeed","500", "how fast the view centers itself"};
63
64 cvar_t cl_stairsmoothspeed = {CVAR_SAVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
65
66 cvar_t chase_back = {CVAR_SAVE, "chase_back", "48", "chase cam distance from the player"};
67 cvar_t chase_up = {CVAR_SAVE, "chase_up", "24", "chase cam distance from the player"};
68 cvar_t chase_active = {CVAR_SAVE, "chase_active", "0", "enables chase cam"};
69 // GAME_GOODVSBAD2
70 cvar_t chase_stevie = {0, "chase_stevie", "0", "chase cam view from above (used only by GoodVsBad2)"};
71
72 cvar_t v_deathtilt = {0, "v_deathtilt", "1", "whether to use sideways view when dead"};
73 cvar_t v_deathtiltangle = {0, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
74
75 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
76
77
78 /*
79 ===============
80 V_CalcRoll
81
82 Used by view and sv_user
83 ===============
84 */
85 float V_CalcRoll (vec3_t angles, vec3_t velocity)
86 {
87         vec3_t  right;
88         float   sign;
89         float   side;
90         float   value;
91
92         AngleVectors (angles, NULL, right, NULL);
93         side = DotProduct (velocity, right);
94         sign = side < 0 ? -1 : 1;
95         side = fabs(side);
96
97         value = cl_rollangle.value;
98
99         if (side < cl_rollspeed.value)
100                 side = side * value / cl_rollspeed.value;
101         else
102                 side = value;
103
104         return side*sign;
105
106 }
107
108 void V_StartPitchDrift (void)
109 {
110         if (cl.laststop == cl.time)
111                 return;         // something else is keeping it from drifting
112
113         if (cl.nodrift || !cl.pitchvel)
114         {
115                 cl.pitchvel = v_centerspeed.value;
116                 cl.nodrift = false;
117                 cl.driftmove = 0;
118         }
119 }
120
121 void V_StopPitchDrift (void)
122 {
123         cl.laststop = cl.time;
124         cl.nodrift = true;
125         cl.pitchvel = 0;
126 }
127
128 /*
129 ===============
130 V_DriftPitch
131
132 Moves the client pitch angle towards cl.idealpitch sent by the server.
133
134 If the user is adjusting pitch manually, either with lookup/lookdown,
135 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
136
137 Drifting is enabled when the center view key is hit, mlook is released and
138 lookspring is non 0, or when
139 ===============
140 */
141 void V_DriftPitch (void)
142 {
143         float           delta, move;
144
145         if (noclip_anglehack || !cl.onground || cls.demoplayback )
146         {
147                 cl.driftmove = 0;
148                 cl.pitchvel = 0;
149                 return;
150         }
151
152 // don't count small mouse motion
153         if (cl.nodrift)
154         {
155                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
156                         cl.driftmove = 0;
157                 else
158                         cl.driftmove += cl.frametime;
159
160                 if ( cl.driftmove > v_centermove.value)
161                 {
162                         V_StartPitchDrift ();
163                 }
164                 return;
165         }
166
167         delta = cl.idealpitch - cl.viewangles[PITCH];
168
169         if (!delta)
170         {
171                 cl.pitchvel = 0;
172                 return;
173         }
174
175         move = cl.frametime * cl.pitchvel;
176         cl.pitchvel += cl.frametime * v_centerspeed.value;
177
178         if (delta > 0)
179         {
180                 if (move > delta)
181                 {
182                         cl.pitchvel = 0;
183                         move = delta;
184                 }
185                 cl.viewangles[PITCH] += move;
186         }
187         else if (delta < 0)
188         {
189                 if (move > -delta)
190                 {
191                         cl.pitchvel = 0;
192                         move = -delta;
193                 }
194                 cl.viewangles[PITCH] -= move;
195         }
196 }
197
198
199 /*
200 ==============================================================================
201
202                                                 SCREEN FLASHES
203
204 ==============================================================================
205 */
206
207
208 /*
209 ===============
210 V_ParseDamage
211 ===============
212 */
213 void V_ParseDamage (void)
214 {
215         int armor, blood;
216         vec3_t from;
217         //vec3_t forward, right;
218         vec3_t localfrom;
219         entity_t *ent;
220         //float side;
221         float count;
222
223         armor = MSG_ReadByte ();
224         blood = MSG_ReadByte ();
225         MSG_ReadVector(from, cls.protocol);
226
227         count = blood*0.5 + armor*0.5;
228         if (count < 10)
229                 count = 10;
230
231         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
232
233         cl.cshifts[CSHIFT_DAMAGE].percent += (int)(3*count);
234         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
235                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
236         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
237                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
238
239         if (armor > blood)
240         {
241                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
242                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
243                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
244         }
245         else if (armor)
246         {
247                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
248                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
249                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
250         }
251         else
252         {
253                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
254                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
255                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
256         }
257
258         // calculate view angle kicks
259         if (cl.entities[cl.viewentity].state_current.active)
260         {
261                 ent = &cl.entities[cl.viewentity];
262                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
263                 VectorNormalize(localfrom);
264                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
265                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
266                 v_dmg_time = v_kicktime.value;
267         }
268 }
269
270 static cshift_t v_cshift;
271
272 /*
273 ==================
274 V_cshift_f
275 ==================
276 */
277 static void V_cshift_f (void)
278 {
279         v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
280         v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
281         v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
282         v_cshift.percent = atoi(Cmd_Argv(4));
283 }
284
285
286 /*
287 ==================
288 V_BonusFlash_f
289
290 When you run over an item, the server sends this command
291 ==================
292 */
293 static void V_BonusFlash_f (void)
294 {
295         cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
296         cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
297         cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
298         cl.cshifts[CSHIFT_BONUS].percent = 50;
299 }
300
301 /*
302 ==============================================================================
303
304                                                 VIEW RENDERING
305
306 ==============================================================================
307 */
308
309 extern matrix4x4_t viewmodelmatrix;
310
311 #include "cl_collision.h"
312 #include "csprogs.h"
313
314 /*
315 ==================
316 V_CalcRefdef
317
318 ==================
319 */
320 void V_CalcRefdef (void)
321 {
322         static float oldz;
323         entity_t *ent;
324         float vieworg[3], gunorg[3], viewangles[3];
325         trace_t trace;
326         if(csqc_loaded)
327                 return;
328         VectorClear(gunorg);
329         viewmodelmatrix = identitymatrix;
330         r_refdef.viewentitymatrix = identitymatrix;
331         if (cls.state == ca_connected && cls.signon == SIGNONS)
332         {
333                 // ent is the view entity (visible when out of body)
334                 ent = &cl.entities[cl.viewentity];
335                 if (cl.intermission)
336                 {
337                         // entity is a fixed camera, just copy the matrix
338                         if (cls.protocol == PROTOCOL_QUAKEWORLD)
339                                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1);
340                         else
341                         {
342                                 r_refdef.viewentitymatrix = ent->render.matrix;
343                                 r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
344                         }
345                         viewmodelmatrix = r_refdef.viewentitymatrix;
346                 }
347                 else
348                 {
349                         // player can look around, so take the origin from the entity,
350                         // and the angles from the input system
351                         Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
352                         VectorCopy(cl.viewangles, viewangles);
353
354                         // apply qw weapon recoil effect (this did not work in QW)
355                         // TODO: add a cvar to disable this
356                         viewangles[PITCH] += cl.qw_weaponkick;
357
358                         if (cl.onground)
359                         {
360                                 if (!cl.oldonground)
361                                         cl.hitgroundtime = cl.time;
362                                 cl.lastongroundtime = cl.time;
363                         }
364                         cl.oldonground = cl.onground;
365
366                         // stair smoothing
367                         //Con_Printf("cl.onground %i oldz %f newz %f\n", cl.onground, oldz, vieworg[2]);
368                         if (cl.onground && oldz < vieworg[2])
369                         {
370                                 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
371                                 oldz = vieworg[2] = bound(vieworg[2] - 16, oldz, vieworg[2]);
372                         }
373                         else if (cl.onground && oldz > vieworg[2])
374                         {
375                                 oldz -= (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
376                                 oldz = vieworg[2] = bound(vieworg[2], oldz, vieworg[2] + 16);
377                         }
378                         else
379                                 oldz = vieworg[2];
380
381                         if (chase_active.value)
382                         {
383                                 // observing entity from third person
384                                 vec_t camback, camup, dist, forward[3], chase_dest[3];
385
386                                 camback = bound(0, chase_back.value, 128);
387                                 if (chase_back.value != camback)
388                                         Cvar_SetValueQuick(&chase_back, camback);
389                                 camup = bound(-48, chase_up.value, 96);
390                                 if (chase_up.value != camup)
391                                         Cvar_SetValueQuick(&chase_up, camup);
392
393                                 // this + 22 is to match view_ofs for compatibility with older versions
394                                 camup += 22;
395
396                                 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
397                                 {
398                                         // look straight down from high above
399                                         viewangles[0] = 90;
400                                         camback = 2048;
401                                 }
402                                 AngleVectors(viewangles, forward, NULL, NULL);
403
404                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
405                                 dist = -camback - 8;
406                                 chase_dest[0] = vieworg[0] + forward[0] * dist;
407                                 chase_dest[1] = vieworg[1] + forward[1] * dist;
408                                 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
409                                 trace = CL_TraceBox(vieworg, vec3_origin, vec3_origin, chase_dest, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, false);
410                                 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
411                         }
412                         else
413                         {
414                                 // first person view from entity
415                                 // angles
416                                 if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
417                                         viewangles[ROLL] = v_deathtiltangle.value;
418                                 VectorAdd(viewangles, cl.punchangle, viewangles);
419                                 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.movement_velocity);
420                                 if (v_dmg_time > 0)
421                                 {
422                                         viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
423                                         viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
424                                         v_dmg_time -= cl.frametime;
425                                 }
426                                 // origin
427                                 VectorAdd(vieworg, cl.punchvector, vieworg);
428                                 vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
429                                 if (cl.stats[STAT_HEALTH] > 0)
430                                 {
431                                         double xyspeed, bob;
432
433                                         xyspeed = sqrt(cl.movement_velocity[0]*cl.movement_velocity[0] + cl.movement_velocity[1]*cl.movement_velocity[1]);
434                                         if (cl_bob.value && cl_bobcycle.value)
435                                         {
436                                                 float cycle;
437                                                 // LordHavoc: this code is *weird*, but not replacable (I think it
438                                                 // should be done in QC on the server, but oh well, quake is quake)
439                                                 // LordHavoc: figured out bobup: the time at which the sin is at 180
440                                                 // degrees (which allows lengthening or squishing the peak or valley)
441                                                 cycle = cl.time / cl_bobcycle.value;
442                                                 cycle -= (int) cycle;
443                                                 if (cycle < cl_bobup.value)
444                                                         cycle = sin(M_PI * cycle / cl_bobup.value);
445                                                 else
446                                                         cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
447                                                 // bob is proportional to velocity in the xy plane
448                                                 // (don't count Z, or jumping messes it up)
449                                                 bob = xyspeed * cl_bob.value;
450                                                 bob = bob*0.3 + bob*0.7*cycle;
451                                                 vieworg[2] += bound(-7, bob, 4);
452                                         }
453
454                                         VectorCopy(vieworg, gunorg);
455
456                                         if (cl_bobmodel.value)
457                                         {
458                                                 // calculate for swinging gun model
459                                                 // the gun bobs when running on the ground, but doesn't bob when you're in the air.
460                                                 // Sajt: I tried to smooth out the transitions between bob and no bob, which works
461                                                 // for the most part, but for some reason when you go through a message trigger or
462                                                 // pick up an item or anything like that it will momentarily jolt the gun.
463                                                 vec3_t forward, right, up;
464                                                 float bspeed;
465                                                 float s;
466                                                 float t;
467
468                                                 s = cl.time * cl_bobmodel_speed.value;
469                                                 if (cl.onground)
470                                                 {
471                                                         if (cl.time - cl.hitgroundtime < 0.2)
472                                                         {
473                                                                 // just hit the ground, speed the bob back up over the next 0.2 seconds
474                                                                 t = cl.time - cl.hitgroundtime;
475                                                                 t = bound(0, t, 0.2);
476                                                                 t *= 5;
477                                                         }
478                                                         else
479                                                                 t = 1;
480                                                 }
481                                                 else
482                                                 {
483                                                         // recently left the ground, slow the bob down over the next 0.2 seconds
484                                                         t = cl.time - cl.lastongroundtime;
485                                                         t = 0.2 - bound(0, t, 0.2);
486                                                         t *= 5;
487                                                 }
488
489                                                 bspeed = bound (0, xyspeed, 400) * 0.01f;
490                                                 AngleVectors (viewangles, forward, right, up);
491                                                 bob = bspeed * cl_bobmodel_side.value * sin (s) * t;
492                                                 VectorMA (gunorg, bob, right, gunorg);
493                                                 bob = bspeed * cl_bobmodel_up.value * cos (s * 2) * t;
494                                                 VectorMA (gunorg, bob, up, gunorg);
495                                         }
496                                 }
497                         }
498                         // calculate a view matrix for rendering the scene
499                         if (v_idlescale.value)
500                                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0] + v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value, viewangles[1] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value, viewangles[2] + v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 1);
501                         else
502                                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2] + v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 1);
503                         // calculate a viewmodel matrix for use in view-attached entities
504                         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
505                         VectorCopy(vieworg, csqc_origin);
506                         VectorCopy(viewangles, csqc_angles);
507                 }
508         }
509 }
510
511 void V_FadeViewFlashs(void)
512 {
513         // drop the damage value
514         cl.cshifts[CSHIFT_DAMAGE].percent -= (int)((cl.time - cl.oldtime)*150);
515         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
516                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
517         // drop the bonus value
518         cl.cshifts[CSHIFT_BONUS].percent -= (int)((cl.time - cl.oldtime)*100);
519         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
520                 cl.cshifts[CSHIFT_BONUS].percent = 0;
521 }
522
523 void V_CalcViewBlend(void)
524 {
525         float a2;
526         int j;
527         r_refdef.viewblend[0] = 0;
528         r_refdef.viewblend[1] = 0;
529         r_refdef.viewblend[2] = 0;
530         r_refdef.viewblend[3] = 0;
531         r_refdef.frustumscale_x = 1;
532         r_refdef.frustumscale_y = 1;
533         if (cls.state == ca_connected && cls.signon == SIGNONS && gl_polyblend.value > 0)
534         {
535                 // set contents color
536                 int supercontents;
537                 vec3_t vieworigin;
538                 Matrix4x4_OriginFromMatrix(&r_refdef.viewentitymatrix, vieworigin);
539                 supercontents = CL_PointSuperContents(vieworigin);
540                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
541                 {
542                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
543                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
544                         if (supercontents & SUPERCONTENTS_LAVA)
545                         {
546                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
547                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
548                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
549                         }
550                         else if (supercontents & SUPERCONTENTS_SLIME)
551                         {
552                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
553                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
554                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
555                         }
556                         else
557                         {
558                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
559                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
560                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
561                         }
562                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
563                 }
564                 else
565                 {
566                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
567                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
568                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
569                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
570                 }
571
572                 if (gamemode != GAME_TRANSFUSION)
573                 {
574                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
575                         {
576                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
577                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
578                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
579                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
580                         }
581                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
582                         {
583                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
584                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
585                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
586                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
587                         }
588                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
589                         {
590                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
591                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
592                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
593                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
594                         }
595                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
596                         {
597                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
598                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
599                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
600                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
601                         }
602                         else
603                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
604                 }
605
606                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
607                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
608                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
609                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
610
611                 // LordHavoc: fixed V_CalcBlend
612                 for (j = 0;j < NUM_CSHIFTS;j++)
613                 {
614                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
615                         if (a2 > 0)
616                         {
617                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
618                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
619                         }
620                 }
621                 // saturate color (to avoid blending in black)
622                 if (r_refdef.viewblend[3])
623                 {
624                         a2 = 1 / r_refdef.viewblend[3];
625                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
626                 }
627
628                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
629                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
630                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
631                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
632         }
633 }
634
635 //============================================================================
636
637 /*
638 =============
639 V_Init
640 =============
641 */
642 void V_Init (void)
643 {
644         Cmd_AddCommand ("v_cshift", V_cshift_f, "sets tint color of view");
645         Cmd_AddCommand ("bf", V_BonusFlash_f, "briefly flashes a bright color tint on view (used when items are picked up)");
646         Cmd_AddCommand ("centerview", V_StartPitchDrift, "gradually recenter view (stop looking up/down)");
647
648         Cvar_RegisterVariable (&v_centermove);
649         Cvar_RegisterVariable (&v_centerspeed);
650
651         Cvar_RegisterVariable (&v_iyaw_cycle);
652         Cvar_RegisterVariable (&v_iroll_cycle);
653         Cvar_RegisterVariable (&v_ipitch_cycle);
654         Cvar_RegisterVariable (&v_iyaw_level);
655         Cvar_RegisterVariable (&v_iroll_level);
656         Cvar_RegisterVariable (&v_ipitch_level);
657
658         Cvar_RegisterVariable (&v_idlescale);
659         Cvar_RegisterVariable (&crosshair);
660
661         Cvar_RegisterVariable (&cl_rollspeed);
662         Cvar_RegisterVariable (&cl_rollangle);
663         Cvar_RegisterVariable (&cl_bob);
664         Cvar_RegisterVariable (&cl_bobcycle);
665         Cvar_RegisterVariable (&cl_bobup);
666         Cvar_RegisterVariable (&cl_bobmodel);
667         Cvar_RegisterVariable (&cl_bobmodel_side);
668         Cvar_RegisterVariable (&cl_bobmodel_up);
669         Cvar_RegisterVariable (&cl_bobmodel_speed);
670
671         Cvar_RegisterVariable (&v_kicktime);
672         Cvar_RegisterVariable (&v_kickroll);
673         Cvar_RegisterVariable (&v_kickpitch);
674
675         Cvar_RegisterVariable (&cl_stairsmoothspeed);
676
677         Cvar_RegisterVariable (&chase_back);
678         Cvar_RegisterVariable (&chase_up);
679         Cvar_RegisterVariable (&chase_active);
680         if (gamemode == GAME_GOODVSBAD2)
681                 Cvar_RegisterVariable (&chase_stevie);
682
683         Cvar_RegisterVariable (&v_deathtilt);
684         Cvar_RegisterVariable (&v_deathtiltangle);
685 }
686