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