]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
fixed a flaw in Mod_Q1BSP_RecursiveRecalcNodeBBox, it was merging bounding boxes...
[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         VectorClear(gunorg);
325         Matrix4x4_CreateIdentity(&viewmodelmatrix);
326         Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
327         if (cls.state == ca_connected && cls.signon == SIGNONS)
328         {
329                 // ent is the view entity (visible when out of body)
330                 ent = &cl_entities[cl.viewentity];
331                 if (cl.intermission)
332                 {
333                         // entity is a fixed camera, just copy the matrix
334                         Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix);
335                         Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix);
336                         r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
337                         viewmodelmatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
338                 }
339                 else
340                 {
341                         // player can look around, so take the origin from the entity,
342                         // and the angles from the input system
343                         Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
344                         VectorCopy(cl.viewangles, viewangles);
345
346                         if (cl.onground)
347                         {
348                                 if (!cl.oldonground)
349                                         cl.hitgroundtime = cl.time;
350                                 cl.lastongroundtime = cl.time;
351                         }
352                         cl.oldonground = cl.onground;
353
354                         // stair smoothing
355                         //Con_Printf("cl.onground %i oldz %f newz %f\n", cl.onground, oldz, vieworg[2]);
356                         if (cl.onground && oldz < vieworg[2])
357                         {
358                                 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
359                                 oldz = vieworg[2] = bound(vieworg[2] - 16, oldz, vieworg[2]);
360                         }
361                         else if (cl.onground && oldz > vieworg[2])
362                         {
363                                 oldz -= (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
364                                 oldz = vieworg[2] = bound(vieworg[2], oldz, vieworg[2] + 16);
365                         }
366                         else
367                                 oldz = vieworg[2];
368
369                         if (chase_active.value)
370                         {
371                                 // observing entity from third person
372                                 vec_t camback, camup, dist, forward[3], chase_dest[3];
373
374                                 camback = bound(0, chase_back.value, 128);
375                                 if (chase_back.value != camback)
376                                         Cvar_SetValueQuick(&chase_back, camback);
377                                 camup = bound(-48, chase_up.value, 96);
378                                 if (chase_up.value != camup)
379                                         Cvar_SetValueQuick(&chase_up, camup);
380
381                                 // this + 22 is to match view_ofs for compatibility with older versions
382                                 camup += 22;
383
384                                 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
385                                 {
386                                         // look straight down from high above
387                                         viewangles[0] = 90;
388                                         camback = 2048;
389                                 }
390                                 AngleVectors(viewangles, forward, NULL, NULL);
391
392                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
393                                 dist = -camback - 8;
394                                 chase_dest[0] = vieworg[0] + forward[0] * dist;
395                                 chase_dest[1] = vieworg[1] + forward[1] * dist;
396                                 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
397                                 trace = CL_TraceBox(vieworg, vec3_origin, vec3_origin, chase_dest, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, false);
398                                 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
399                         }
400                         else
401                         {
402                                 // first person view from entity
403                                 // angles
404                                 if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
405                                         viewangles[ROLL] = 80;  // dead view angle
406                                 VectorAdd(viewangles, cl.punchangle, viewangles);
407                                 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.movement_velocity);
408                                 if (v_dmg_time > 0)
409                                 {
410                                         viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
411                                         viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
412                                         v_dmg_time -= cl.frametime;
413                                 }
414                                 // origin
415                                 VectorAdd(vieworg, cl.punchvector, vieworg);
416                                 vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
417                                 if (cl.stats[STAT_HEALTH] > 0)
418                                 {
419                                         double xyspeed, bob;
420
421                                         xyspeed = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]);
422                                         if (cl_bob.value && cl_bobcycle.value)
423                                         {
424                                                 float cycle;
425                                                 // LordHavoc: this code is *weird*, but not replacable (I think it
426                                                 // should be done in QC on the server, but oh well, quake is quake)
427                                                 // LordHavoc: figured out bobup: the time at which the sin is at 180
428                                                 // degrees (which allows lengthening or squishing the peak or valley)
429                                                 cycle = cl.time / cl_bobcycle.value;
430                                                 cycle -= (int) cycle;
431                                                 if (cycle < cl_bobup.value)
432                                                         cycle = sin(M_PI * cycle / cl_bobup.value);
433                                                 else
434                                                         cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
435                                                 // bob is proportional to velocity in the xy plane
436                                                 // (don't count Z, or jumping messes it up)
437                                                 bob = xyspeed * cl_bob.value;
438                                                 bob = bob*0.3 + bob*0.7*cycle;
439                                                 vieworg[2] += bound(-7, bob, 4);
440                                         }
441
442                                         VectorCopy(vieworg, gunorg);
443
444                                         if (cl_bobmodel.value)
445                                         {
446                                                 // calculate for swinging gun model
447                                                 // the gun bobs when running on the ground, but doesn't bob when you're in the air.
448                                                 // Sajt: I tried to smooth out the transitions between bob and no bob, which works
449                                                 // for the most part, but for some reason when you go through a message trigger or
450                                                 // pick up an item or anything like that it will momentarily jolt the gun.
451                                                 vec3_t forward, right, up;
452                                                 float bspeed;
453                                                 float s;
454                                                 float t;
455
456                                                 s = cl.time * cl_bobmodel_speed.value;
457                                                 if (cl.onground)
458                                                 {
459                                                         if (cl.time - cl.hitgroundtime < 0.2)
460                                                         {
461                                                                 // just hit the ground, speed the bob back up over the next 0.2 seconds
462                                                                 t = cl.time - cl.hitgroundtime;
463                                                                 t = bound(0, t, 0.2);
464                                                                 t *= 5;
465                                                         }
466                                                         else
467                                                                 t = 1;
468                                                 }
469                                                 else
470                                                 {
471                                                         // recently left the ground, slow the bob down over the next 0.2 seconds
472                                                         t = cl.time - cl.lastongroundtime;
473                                                         t = 0.2 - bound(0, t, 0.2);
474                                                         t *= 5;
475                                                 }
476
477                                                 bspeed = bound (0, xyspeed, 400) * 0.01f;
478                                                 AngleVectors (viewangles, forward, right, up);
479                                                 bob = bspeed * cl_bobmodel_side.value * sin (s) * t;
480                                                 VectorMA (gunorg, bob, right, gunorg);
481                                                 bob = bspeed * cl_bobmodel_up.value * cos (s * 2) * t;
482                                                 VectorMA (gunorg, bob, up, gunorg);
483                                         }
484                                 }
485                         }
486                         // calculate a view matrix for rendering the scene
487                         if (v_idlescale.value)
488                                 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);
489                         else
490                                 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);
491                         // calculate a viewmodel matrix for use in view-attached entities
492                         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
493                 }
494         }
495 }
496
497 void V_FadeViewFlashs(void)
498 {
499         // drop the damage value
500         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
501         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
502                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
503         // drop the bonus value
504         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
505         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
506                 cl.cshifts[CSHIFT_BONUS].percent = 0;
507 }
508
509 void V_CalcViewBlend(void)
510 {
511         float a2;
512         int j;
513         r_refdef.viewblend[0] = 0;
514         r_refdef.viewblend[1] = 0;
515         r_refdef.viewblend[2] = 0;
516         r_refdef.viewblend[3] = 0;
517         r_refdef.fovscale_x = cl.viewzoom;
518         r_refdef.fovscale_y = cl.viewzoom;
519         if (cls.state == ca_connected && cls.signon == SIGNONS && gl_polyblend.value > 0)
520         {
521                 // set contents color
522                 int supercontents;
523                 vec3_t vieworigin;
524                 Matrix4x4_OriginFromMatrix(&r_refdef.viewentitymatrix, vieworigin);
525                 supercontents = CL_PointSuperContents(vieworigin);
526                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
527                 {
528                         r_refdef.fovscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
529                         r_refdef.fovscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
530                         if (supercontents & SUPERCONTENTS_LAVA)
531                         {
532                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
533                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
534                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
535                         }
536                         else if (supercontents & SUPERCONTENTS_SLIME)
537                         {
538                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
539                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
540                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
541                         }
542                         else
543                         {
544                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
545                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
546                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
547                         }
548                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
549                 }
550                 else
551                 {
552                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
553                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
554                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
555                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
556                 }
557
558                 if (gamemode != GAME_TRANSFUSION)
559                 {
560                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
561                         {
562                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
563                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
564                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
565                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
566                         }
567                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
568                         {
569                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
570                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
571                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
572                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
573                         }
574                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
575                         {
576                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
577                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
578                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
579                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
580                         }
581                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
582                         {
583                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
584                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
585                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
586                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
587                         }
588                         else
589                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
590                 }
591
592                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
593                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
594                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
595                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
596
597                 // LordHavoc: fixed V_CalcBlend
598                 for (j = 0;j < NUM_CSHIFTS;j++)
599                 {
600                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
601                         if (a2 > 0)
602                         {
603                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
604                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
605                         }
606                 }
607                 // saturate color (to avoid blending in black)
608                 if (r_refdef.viewblend[3])
609                 {
610                         a2 = 1 / r_refdef.viewblend[3];
611                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
612                 }
613
614                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
615                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
616                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
617                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
618         }
619 }
620
621 //============================================================================
622
623 /*
624 =============
625 V_Init
626 =============
627 */
628 void V_Init (void)
629 {
630         Cmd_AddCommand ("v_cshift", V_cshift_f);
631         Cmd_AddCommand ("bf", V_BonusFlash_f);
632         Cmd_AddCommand ("centerview", V_StartPitchDrift);
633
634         Cvar_RegisterVariable (&v_centermove);
635         Cvar_RegisterVariable (&v_centerspeed);
636
637         Cvar_RegisterVariable (&v_iyaw_cycle);
638         Cvar_RegisterVariable (&v_iroll_cycle);
639         Cvar_RegisterVariable (&v_ipitch_cycle);
640         Cvar_RegisterVariable (&v_iyaw_level);
641         Cvar_RegisterVariable (&v_iroll_level);
642         Cvar_RegisterVariable (&v_ipitch_level);
643
644         Cvar_RegisterVariable (&v_idlescale);
645         Cvar_RegisterVariable (&crosshair);
646
647         Cvar_RegisterVariable (&cl_rollspeed);
648         Cvar_RegisterVariable (&cl_rollangle);
649         Cvar_RegisterVariable (&cl_bob);
650         Cvar_RegisterVariable (&cl_bobcycle);
651         Cvar_RegisterVariable (&cl_bobup);
652         Cvar_RegisterVariable (&cl_bobmodel);
653         Cvar_RegisterVariable (&cl_bobmodel_side);
654         Cvar_RegisterVariable (&cl_bobmodel_up);
655         Cvar_RegisterVariable (&cl_bobmodel_speed);
656
657         Cvar_RegisterVariable (&v_kicktime);
658         Cvar_RegisterVariable (&v_kickroll);
659         Cvar_RegisterVariable (&v_kickpitch);
660
661         Cvar_RegisterVariable (&cl_stairsmoothspeed);
662
663         Cvar_RegisterVariable (&chase_back);
664         Cvar_RegisterVariable (&chase_up);
665         Cvar_RegisterVariable (&chase_active);
666         if (gamemode == GAME_GOODVSBAD2)
667                 Cvar_RegisterVariable (&chase_stevie);
668
669         Cvar_RegisterVariable (&v_deathtilt);
670 }
671