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