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