]> git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
Implement classic Quake-style weapon bobbing. Enable by default for Quake.
[xonotic/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 #include "image.h"
25
26 /*
27
28 The view is allowed to move slightly from it's true position for bobbing,
29 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
30 entities sent from the server may not include everything in the pvs, especially
31 when crossing a water boudnary.
32
33 */
34
35 cvar_t cl_rollspeed = {CVAR_CLIENT, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
36 cvar_t cl_rollangle = {CVAR_CLIENT, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
37
38 cvar_t cl_bob = {CVAR_CLIENT | CVAR_SAVE, "cl_bob","0.02", "view bobbing amount"};
39 cvar_t cl_bobcycle = {CVAR_CLIENT | CVAR_SAVE, "cl_bobcycle","0.6", "view bobbing speed"};
40 cvar_t cl_bobup = {CVAR_CLIENT | CVAR_SAVE, "cl_bobup","0.5", "view bobbing adjustment that makes the up or down swing of the bob last longer"};
41 cvar_t cl_bob2 = {CVAR_CLIENT | CVAR_SAVE, "cl_bob2","0", "sideways view bobbing amount"};
42 cvar_t cl_bob2cycle = {CVAR_CLIENT | CVAR_SAVE, "cl_bob2cycle","0.6", "sideways view bobbing speed"};
43 cvar_t cl_bob2smooth = {CVAR_CLIENT | CVAR_SAVE, "cl_bob2smooth","0.05", "how fast the view goes back when you stop touching the ground"};
44 cvar_t cl_bobfall = {CVAR_CLIENT | CVAR_SAVE, "cl_bobfall","0", "how much the view swings down when falling (influenced by the speed you hit the ground with)"};
45 cvar_t cl_bobfallcycle = {CVAR_CLIENT | CVAR_SAVE, "cl_bobfallcycle","3", "speed of the bobfall swing"};
46 cvar_t cl_bobfallminspeed = {CVAR_CLIENT | CVAR_SAVE, "cl_bobfallminspeed","200", "necessary amount of speed for bob-falling to occur"};
47 cvar_t cl_bobmodel = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel", "1", "enables gun bobbing"};
48 cvar_t cl_bobmodel_side = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel_side", "0", "gun bobbing sideways sway amount"};
49 cvar_t cl_bobmodel_up = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel_up", "0", "gun bobbing upward movement amount"};
50 cvar_t cl_bobmodel_forward = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel_forward", "0.25", "gun bobbing forward movement amount"};
51 cvar_t cl_bobmodel_classic = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel_classic", "1", "classic Quake-style forward gun bobbing"};
52 cvar_t cl_bobmodel_speed = {CVAR_CLIENT | CVAR_SAVE, "cl_bobmodel_speed", "6", "gun bobbing speed"};
53 cvar_t cl_bob_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_bob_limit", "4", "limits bobbing to this much distance from view_ofs"};
54 cvar_t cl_bob_limit_heightcheck = {CVAR_CLIENT | CVAR_SAVE, "cl_bob_limit_heightcheck", "0", "check ceiling and floor height against cl_bob_limit and scale down all view bobbing if could result in camera being in solid"};
55 cvar_t cl_bob_limit_heightcheck_dontcrosswatersurface = {CVAR_CLIENT | CVAR_SAVE, "cl_bob_limit_heightcheck_dontcrosswatersurface", "1", "limit cl_bob_limit to not crossing liquid surfaces also"};
56 cvar_t cl_bob_velocity_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_bob_velocity_limit", "400", "limits the xyspeed value in the bobbing code"};
57
58 cvar_t cl_leanmodel = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel", "0", "enables gun leaning"};
59 cvar_t cl_leanmodel_side_speed = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_side_speed", "0.7", "gun leaning sideways speed"};
60 cvar_t cl_leanmodel_side_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_side_limit", "35", "gun leaning sideways limit"};
61 cvar_t cl_leanmodel_side_highpass1 = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_side_highpass1", "30", "gun leaning sideways pre-highpass in 1/s"};
62 cvar_t cl_leanmodel_side_highpass = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_side_highpass", "3", "gun leaning sideways highpass in 1/s"};
63 cvar_t cl_leanmodel_side_lowpass = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_side_lowpass", "20", "gun leaning sideways lowpass in 1/s"};
64 cvar_t cl_leanmodel_up_speed = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_up_speed", "0.65", "gun leaning upward speed"};
65 cvar_t cl_leanmodel_up_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_up_limit", "50", "gun leaning upward limit"};
66 cvar_t cl_leanmodel_up_highpass1 = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_up_highpass1", "5", "gun leaning upward pre-highpass in 1/s"};
67 cvar_t cl_leanmodel_up_highpass = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_up_highpass", "15", "gun leaning upward highpass in 1/s"};
68 cvar_t cl_leanmodel_up_lowpass = {CVAR_CLIENT | CVAR_SAVE, "cl_leanmodel_up_lowpass", "20", "gun leaning upward lowpass in 1/s"};
69
70 cvar_t cl_followmodel = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel", "0", "enables gun following"};
71 cvar_t cl_followmodel_side_speed = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_side_speed", "0.25", "gun following sideways speed"};
72 cvar_t cl_followmodel_side_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_side_limit", "6", "gun following sideways limit"};
73 cvar_t cl_followmodel_side_highpass1 = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_side_highpass1", "30", "gun following sideways pre-highpass in 1/s"};
74 cvar_t cl_followmodel_side_highpass = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_side_highpass", "5", "gun following sideways highpass in 1/s"};
75 cvar_t cl_followmodel_side_lowpass = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_side_lowpass", "10", "gun following sideways lowpass in 1/s"};
76 cvar_t cl_followmodel_up_speed = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_up_speed", "0.5", "gun following upward speed"};
77 cvar_t cl_followmodel_up_limit = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_up_limit", "5", "gun following upward limit"};
78 cvar_t cl_followmodel_up_highpass1 = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_up_highpass1", "60", "gun following upward pre-highpass in 1/s"};
79 cvar_t cl_followmodel_up_highpass = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_up_highpass", "2", "gun following upward highpass in 1/s"};
80 cvar_t cl_followmodel_up_lowpass = {CVAR_CLIENT | CVAR_SAVE, "cl_followmodel_up_lowpass", "10", "gun following upward lowpass in 1/s"};
81
82 cvar_t cl_viewmodel_scale = {CVAR_CLIENT, "cl_viewmodel_scale", "1", "changes size of gun model, lower values prevent poking into walls but cause strange artifacts on lighting and especially r_stereo/vid_stereobuffer options where the size of the gun becomes visible"};
83
84 cvar_t v_kicktime = {CVAR_CLIENT, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
85 cvar_t v_kickroll = {CVAR_CLIENT, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
86 cvar_t v_kickpitch = {CVAR_CLIENT, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
87
88 cvar_t v_iyaw_cycle = {CVAR_CLIENT, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
89 cvar_t v_iroll_cycle = {CVAR_CLIENT, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
90 cvar_t v_ipitch_cycle = {CVAR_CLIENT, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
91 cvar_t v_iyaw_level = {CVAR_CLIENT, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
92 cvar_t v_iroll_level = {CVAR_CLIENT, "v_iroll_level", "0.1", "v_idlescale roll amount"};
93 cvar_t v_ipitch_level = {CVAR_CLIENT, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
94
95 cvar_t v_idlescale = {CVAR_CLIENT, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
96
97 cvar_t v_isometric = {CVAR_CLIENT, "v_isometric", "0", "changes view to isometric (non-perspective)"};
98 cvar_t v_isometric_verticalfov = {CVAR_CLIENT, "v_isometric_verticalfov", "512", "vertical field of view in game units (horizontal is computed using aspect ratio based on this)"};
99 cvar_t v_isometric_xx = {CVAR_CLIENT, "v_isometric_xx", "1", "camera matrix"};
100 cvar_t v_isometric_xy = {CVAR_CLIENT, "v_isometric_xy", "0", "camera matrix"};
101 cvar_t v_isometric_xz = {CVAR_CLIENT, "v_isometric_xz", "0", "camera matrix"};
102 cvar_t v_isometric_yx = {CVAR_CLIENT, "v_isometric_yx", "0", "camera matrix"};
103 cvar_t v_isometric_yy = {CVAR_CLIENT, "v_isometric_yy", "1", "camera matrix"};
104 cvar_t v_isometric_yz = {CVAR_CLIENT, "v_isometric_yz", "0", "camera matrix"};
105 cvar_t v_isometric_zx = {CVAR_CLIENT, "v_isometric_zx", "0", "camera matrix"};
106 cvar_t v_isometric_zy = {CVAR_CLIENT, "v_isometric_zy", "0", "camera matrix"};
107 cvar_t v_isometric_zz = {CVAR_CLIENT, "v_isometric_zz", "1", "camera matrix"};
108 cvar_t v_isometric_tx = {CVAR_CLIENT, "v_isometric_tx", "0", "camera position (player-relative)"};
109 cvar_t v_isometric_ty = {CVAR_CLIENT, "v_isometric_ty", "0", "camera position (player-relative)"};
110 cvar_t v_isometric_tz = {CVAR_CLIENT, "v_isometric_tz", "0", "camera position (player-relative)"};
111 cvar_t v_isometric_rot_pitch = {CVAR_CLIENT, "v_isometric_rot_pitch", "60", "camera rotation"};
112 cvar_t v_isometric_rot_yaw = {CVAR_CLIENT, "v_isometric_rot_yaw", "135", "camera rotation"};
113 cvar_t v_isometric_rot_roll = {CVAR_CLIENT, "v_isometric_rot_roll", "0", "camera rotation"};
114 cvar_t v_isometric_relx = {CVAR_CLIENT, "v_isometric_relx", "0", "camera position*forward"};
115 cvar_t v_isometric_rely = {CVAR_CLIENT, "v_isometric_rely", "0", "camera position*left"};
116 cvar_t v_isometric_relz = {CVAR_CLIENT, "v_isometric_relz", "0", "camera position*up"};
117 cvar_t v_isometric_flipcullface = {CVAR_CLIENT, "v_isometric_flipcullface", "0", "flips the backface culling"};
118 cvar_t v_isometric_locked_orientation = {CVAR_CLIENT, "v_isometric_locked_orientation", "1", "camera rotation is fixed"};
119 cvar_t v_isometric_usevieworiginculling = {CVAR_CLIENT, "v_isometric_usevieworiginculling", "0", "check visibility to the player location (can look pretty weird)"};
120
121 cvar_t crosshair = {CVAR_CLIENT | CVAR_SAVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
122
123 cvar_t v_centermove = {CVAR_CLIENT, "v_centermove", "0.15", "how long before the view begins to center itself (if freelook/+mlook/+jlook/+klook are off)"};
124 cvar_t v_centerspeed = {CVAR_CLIENT, "v_centerspeed","500", "how fast the view centers itself"};
125
126 cvar_t cl_stairsmoothspeed = {CVAR_CLIENT | CVAR_SAVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
127
128 cvar_t cl_smoothviewheight = {CVAR_CLIENT | CVAR_SAVE, "cl_smoothviewheight", "0", "time of the averaging to the viewheight value so that it creates a smooth transition. higher values = longer transition, 0 for instant transition."};
129
130 cvar_t chase_back = {CVAR_CLIENT | CVAR_SAVE, "chase_back", "48", "chase cam distance from the player"};
131 cvar_t chase_up = {CVAR_CLIENT | CVAR_SAVE, "chase_up", "24", "chase cam distance from the player"};
132 cvar_t chase_active = {CVAR_CLIENT | CVAR_SAVE, "chase_active", "0", "enables chase cam"};
133 cvar_t chase_overhead = {CVAR_CLIENT | CVAR_SAVE, "chase_overhead", "0", "chase cam looks straight down if this is not zero"};
134 // GAME_GOODVSBAD2
135 cvar_t chase_stevie = {CVAR_CLIENT, "chase_stevie", "0", "(GOODVSBAD2 only) chase cam view from above"};
136
137 cvar_t v_deathtilt = {CVAR_CLIENT, "v_deathtilt", "1", "whether to use sideways view when dead"};
138 cvar_t v_deathtiltangle = {CVAR_CLIENT, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
139
140 // Prophecy camera pitchangle by Alexander "motorsep" Zubov
141 cvar_t chase_pitchangle = {CVAR_CLIENT | CVAR_SAVE, "chase_pitchangle", "55", "chase cam pitch angle"};
142
143 cvar_t v_yshearing = {CVAR_CLIENT, "v_yshearing", "0", "be all out of gum (set this to the maximum angle to allow Y shearing for - try values like 75)"};
144
145 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
146
147
148 /*
149 ===============
150 V_CalcRoll
151
152 Used by view and sv_user
153 ===============
154 */
155 float V_CalcRoll (const vec3_t angles, const vec3_t velocity)
156 {
157         vec3_t  right;
158         float   sign;
159         float   side;
160         float   value;
161
162         AngleVectors (angles, NULL, right, NULL);
163         side = DotProduct (velocity, right);
164         sign = side < 0 ? -1 : 1;
165         side = fabs(side);
166
167         value = cl_rollangle.value;
168
169         if (side < cl_rollspeed.value)
170                 side = side * value / cl_rollspeed.value;
171         else
172                 side = value;
173
174         return side*sign;
175
176 }
177
178 void V_StartPitchDrift (void)
179 {
180         if (cl.laststop == cl.time)
181                 return;         // something else is keeping it from drifting
182
183         if (cl.nodrift || !cl.pitchvel)
184         {
185                 cl.pitchvel = v_centerspeed.value;
186                 cl.nodrift = false;
187                 cl.driftmove = 0;
188         }
189 }
190
191 void V_StartPitchDrift_f(cmd_state_t *cmd)
192 {
193         V_StartPitchDrift();
194 }
195
196 void V_StopPitchDrift (void)
197 {
198         cl.laststop = cl.time;
199         cl.nodrift = true;
200         cl.pitchvel = 0;
201 }
202
203 /*
204 ===============
205 V_DriftPitch
206
207 Moves the client pitch angle towards cl.idealpitch sent by the server.
208
209 If the user is adjusting pitch manually, either with lookup/lookdown,
210 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
211
212 Drifting is enabled when the center view key is hit, mlook is released and
213 lookspring is non 0, or when
214 ===============
215 */
216 void V_DriftPitch (void)
217 {
218         float           delta, move;
219
220         if (noclip_anglehack || !cl.onground || cls.demoplayback )
221         {
222                 cl.driftmove = 0;
223                 cl.pitchvel = 0;
224                 return;
225         }
226
227 // don't count small mouse motion
228         if (cl.nodrift)
229         {
230                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
231                         cl.driftmove = 0;
232                 else
233                         cl.driftmove += cl.realframetime;
234
235                 if ( cl.driftmove > v_centermove.value)
236                 {
237                         V_StartPitchDrift ();
238                 }
239                 return;
240         }
241
242         delta = cl.idealpitch - cl.viewangles[PITCH];
243
244         if (!delta)
245         {
246                 cl.pitchvel = 0;
247                 return;
248         }
249
250         move = cl.realframetime * cl.pitchvel;
251         cl.pitchvel += cl.realframetime * v_centerspeed.value;
252
253         if (delta > 0)
254         {
255                 if (move > delta)
256                 {
257                         cl.pitchvel = 0;
258                         move = delta;
259                 }
260                 cl.viewangles[PITCH] += move;
261         }
262         else if (delta < 0)
263         {
264                 if (move > -delta)
265                 {
266                         cl.pitchvel = 0;
267                         move = -delta;
268                 }
269                 cl.viewangles[PITCH] -= move;
270         }
271 }
272
273
274 /*
275 ==============================================================================
276
277                                                 SCREEN FLASHES
278
279 ==============================================================================
280 */
281
282
283 /*
284 ===============
285 V_ParseDamage
286 ===============
287 */
288 void V_ParseDamage (void)
289 {
290         int armor, blood;
291         vec3_t from;
292         //vec3_t forward, right;
293         vec3_t localfrom;
294         entity_t *ent;
295         //float side;
296         float count;
297
298         armor = MSG_ReadByte(&cl_message);
299         blood = MSG_ReadByte(&cl_message);
300         MSG_ReadVector(&cl_message, from, cls.protocol);
301
302         // Send the Dmg Globals to CSQC
303         CL_VM_UpdateDmgGlobals(blood, armor, from);
304
305         count = blood*0.5 + armor*0.5;
306         if (count < 10)
307                 count = 10;
308
309         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
310
311         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
312         cl.cshifts[CSHIFT_DAMAGE].alphafade = 150;
313         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
314                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
315         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
316                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
317
318         if (armor > blood)
319         {
320                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
321                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
322                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
323         }
324         else if (armor)
325         {
326                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
327                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
328                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
329         }
330         else
331         {
332                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
333                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
334                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
335         }
336
337         // calculate view angle kicks
338         if (cl.entities[cl.viewentity].state_current.active)
339         {
340                 ent = &cl.entities[cl.viewentity];
341                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
342                 VectorNormalize(localfrom);
343                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
344                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
345                 v_dmg_time = v_kicktime.value;
346         }
347 }
348
349 static cshift_t v_cshift;
350
351 /*
352 ==================
353 V_cshift_f
354 ==================
355 */
356 static void V_cshift_f(cmd_state_t *cmd)
357 {
358         v_cshift.destcolor[0] = atof(Cmd_Argv(cmd, 1));
359         v_cshift.destcolor[1] = atof(Cmd_Argv(cmd, 2));
360         v_cshift.destcolor[2] = atof(Cmd_Argv(cmd, 3));
361         v_cshift.percent = atof(Cmd_Argv(cmd, 4));
362 }
363
364
365 /*
366 ==================
367 V_BonusFlash_f
368
369 When you run over an item, the server sends this command
370 ==================
371 */
372 static void V_BonusFlash_f(cmd_state_t *cmd)
373 {
374         if(Cmd_Argc(cmd) == 1)
375         {
376                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
377                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
378                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
379                 cl.cshifts[CSHIFT_BONUS].percent = 50;
380                 cl.cshifts[CSHIFT_BONUS].alphafade = 100;
381         }
382         else if(Cmd_Argc(cmd) >= 4 && Cmd_Argc(cmd) <= 6)
383         {
384                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(cmd, 1)) * 255;
385                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(cmd, 2)) * 255;
386                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(cmd, 3)) * 255;
387                 if(Cmd_Argc(cmd) >= 5)
388                         cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(cmd, 4)) * 255; // yes, these are HEXADECIMAL percent ;)
389                 else
390                         cl.cshifts[CSHIFT_BONUS].percent = 50;
391                 if(Cmd_Argc(cmd) >= 6)
392                         cl.cshifts[CSHIFT_BONUS].alphafade = atof(Cmd_Argv(cmd, 5)) * 255;
393                 else
394                         cl.cshifts[CSHIFT_BONUS].alphafade = 100;
395         }
396         else
397                 Con_Printf("usage:\nbf, or bf R G B [A [alphafade]]\n");
398 }
399
400 /*
401 ==============================================================================
402
403                                                 VIEW RENDERING
404
405 ==============================================================================
406 */
407
408 extern matrix4x4_t viewmodelmatrix_nobob;
409 extern matrix4x4_t viewmodelmatrix_withbob;
410
411 #include "cl_collision.h"
412 #include "csprogs.h"
413
414 /*
415 ==================
416 V_CalcRefdef
417
418 ==================
419 */
420 #if 0
421 static vec3_t eyeboxmins = {-16, -16, -24};
422 static vec3_t eyeboxmaxs = { 16,  16,  32};
423 #endif
424
425 static vec_t lowpass(vec_t value, vec_t frac, vec_t *store)
426 {
427         frac = bound(0, frac, 1);
428         return (*store = *store * (1 - frac) + value * frac);
429 }
430
431 static vec_t lowpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
432 {
433         lowpass(value, frac, store);
434         return (*store = bound(value - limit, *store, value + limit));
435 }
436
437 static vec_t highpass(vec_t value, vec_t frac, vec_t *store)
438 {
439         return value - lowpass(value, frac, store);
440 }
441
442 static vec_t highpass_limited(vec_t value, vec_t frac, vec_t limit, vec_t *store)
443 {
444         return value - lowpass_limited(value, frac, limit, store);
445 }
446
447 static void lowpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
448 {
449         out[0] = lowpass(value[0], fracx, &store[0]);
450         out[1] = lowpass(value[1], fracy, &store[1]);
451         out[2] = lowpass(value[2], fracz, &store[2]);
452 }
453
454 static void highpass3(vec3_t value, vec_t fracx, vec_t fracy, vec_t fracz, vec3_t store, vec3_t out)
455 {
456         out[0] = highpass(value[0], fracx, &store[0]);
457         out[1] = highpass(value[1], fracy, &store[1]);
458         out[2] = highpass(value[2], fracz, &store[2]);
459 }
460
461 static void highpass3_limited(vec3_t value, vec_t fracx, vec_t limitx, vec_t fracy, vec_t limity, vec_t fracz, vec_t limitz, vec3_t store, vec3_t out)
462 {
463         out[0] = highpass_limited(value[0], fracx, limitx, &store[0]);
464         out[1] = highpass_limited(value[1], fracy, limity, &store[1]);
465         out[2] = highpass_limited(value[2], fracz, limitz, &store[2]);
466 }
467
468 /*
469  * State:
470  *   cl.bob2_smooth
471  *   cl.bobfall_speed
472  *   cl.bobfall_swing
473  *   cl.gunangles_adjustment_highpass
474  *   cl.gunangles_adjustment_lowpass
475  *   cl.gunangles_highpass
476  *   cl.gunangles_prev
477  *   cl.gunorg_adjustment_highpass
478  *   cl.gunorg_adjustment_lowpass
479  *   cl.gunorg_highpass
480  *   cl.gunorg_prev
481  *   cl.hitgroundtime
482  *   cl.lastongroundtime
483  *   cl.oldongrounbd
484  *   cl.stairsmoothtime
485  *   cl.stairsmoothz
486  *   cl.calcrefdef_prevtime
487  * Extra input:
488  *   cl.movecmd[0].time
489  *   cl.movevars_stepheight
490  *   cl.movevars_timescale
491  *   cl.oldtime
492  *   cl.punchangle
493  *   cl.punchvector
494  *   cl.qw_intermission_angles
495  *   cl.qw_intermission_origin
496  *   cl.qw_weaponkick
497  *   cls.protocol
498  *   cl.time
499  * Output:
500  *   cl.csqc_viewanglesfromengine
501  *   cl.csqc_viewmodelmatrixfromengine
502  *   cl.csqc_vieworiginfromengine
503  *   r_refdef.view.matrix
504  *   viewmodelmatrix_nobob
505  *   viewmodelmatrix_withbob
506  */
507 void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qboolean teleported, qboolean clonground, qboolean clcmdjump, float clstatsviewheight, qboolean cldead, qboolean clintermission, const vec3_t clvelocity)
508 {
509         float vieworg[3], viewangles[3], smoothtime;
510         float gunorg[3], gunangles[3];
511         matrix4x4_t tmpmatrix;
512         
513         static float viewheightavg;
514         float viewheight;       
515 #if 0
516 // begin of chase camera bounding box size for proper collisions by Alexander Zubov
517         vec3_t camboxmins = {-3, -3, -3};
518         vec3_t camboxmaxs = {3, 3, 3};
519 // end of chase camera bounding box size for proper collisions by Alexander Zubov
520 #endif
521         trace_t trace;
522
523         // react to clonground state changes (for gun bob)
524         if (clonground)
525         {
526                 if (!cl.oldonground)
527                         cl.hitgroundtime = cl.movecmd[0].time;
528                 cl.lastongroundtime = cl.movecmd[0].time;
529         }
530         cl.oldonground = clonground;
531         cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime);
532
533         VectorClear(gunangles);
534         VectorClear(gunorg);
535         viewmodelmatrix_nobob = identitymatrix;
536         viewmodelmatrix_withbob = identitymatrix;
537         r_refdef.view.matrix = identitymatrix;
538
539         // player can look around, so take the origin from the entity,
540         // and the angles from the input system
541         Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg);
542         VectorCopy(clviewangles, viewangles);
543
544         // calculate how much time has passed since the last V_CalcRefdef
545         smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
546         cl.stairsmoothtime = cl.time;
547
548         // fade damage flash
549         if (v_dmg_time > 0)
550                 v_dmg_time -= bound(0, smoothtime, 0.1);
551
552         if (clintermission)
553         {
554                 // entity is a fixed camera, just copy the matrix
555                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
556                         Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1);
557                 else
558                 {
559                         r_refdef.view.matrix = *entrendermatrix;
560                         Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight);
561                 }
562                 if (v_yshearing.value > 0)
563                         Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
564                 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
565                 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
566                 Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob);
567
568                 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
569                 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
570
571                 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
572                 Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value);
573         }
574         else
575         {
576                 // smooth stair stepping, but only if clonground and enabled
577                 if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported)
578                         cl.stairsmoothz = vieworg[2];
579                 else
580                 {
581                         if (cl.stairsmoothz < vieworg[2])
582                                 vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
583                         else if (cl.stairsmoothz > vieworg[2])
584                                 vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight);
585                 }
586
587                 // apply qw weapon recoil effect (this did not work in QW)
588                 // TODO: add a cvar to disable this
589                 viewangles[PITCH] += cl.qw_weaponkick;
590
591                 // apply the viewofs (even if chasecam is used)
592                 // Samual: Lets add smoothing for this too so that things like crouching are done with a transition.
593                 viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1);
594                 viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight;
595                 vieworg[2] += viewheightavg;
596
597                 if (chase_active.value)
598                 {
599                         // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov
600                         vec_t camback, camup, dist, campitch, forward[3], chase_dest[3];
601
602                         camback = chase_back.value;
603                         camup = chase_up.value;
604                         campitch = chase_pitchangle.value;
605
606                         AngleVectors(viewangles, forward, NULL, NULL);
607
608                         if (chase_overhead.integer)
609                         {
610 #if 1
611                                 vec3_t offset;
612                                 vec3_t bestvieworg;
613 #endif
614                                 vec3_t up;
615                                 viewangles[PITCH] = 0;
616                                 AngleVectors(viewangles, forward, NULL, up);
617                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
618                                 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
619                                 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
620                                 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
621 #if 0
622 #if 1
623                                 //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
624                                 trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
625 #else
626                                 //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
627                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, true, false, NULL, false);
628 #endif
629                                 VectorCopy(trace.endpos, vieworg);
630                                 vieworg[2] -= 8;
631 #else
632                                 // trace from first person view location to our chosen third person view location
633 #if 1
634                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
635 #else
636                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false);
637 #endif
638                                 VectorCopy(trace.endpos, bestvieworg);
639                                 offset[2] = 0;
640                                 for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
641                                 {
642                                         for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
643                                         {
644                                                 AngleVectors(viewangles, NULL, NULL, up);
645                                                 chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
646                                                 chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
647                                                 chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
648 #if 1
649                                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
650 #else
651                                                 trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false);
652 #endif
653                                                 if (bestvieworg[2] > trace.endpos[2])
654                                                         bestvieworg[2] = trace.endpos[2];
655                                         }
656                                 }
657                                 bestvieworg[2] -= 8;
658                                 VectorCopy(bestvieworg, vieworg);
659 #endif
660                                 viewangles[PITCH] = campitch;
661                         }
662                         else
663                         {
664                                 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
665                                 {
666                                         // look straight down from high above
667                                         viewangles[PITCH] = 90;
668                                         camback = 2048;
669                                         VectorSet(forward, 0, 0, -1);
670                                 }
671
672                                 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
673                                 dist = -camback - 8;
674                                 chase_dest[0] = vieworg[0] + forward[0] * dist;
675                                 chase_dest[1] = vieworg[1] + forward[1] * dist;
676                                 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
677                                 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
678                                 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
679                         }
680                 }
681                 else
682                 {
683                         // first person view from entity
684                         // angles
685                         if (cldead && v_deathtilt.integer)
686                                 viewangles[ROLL] = v_deathtiltangle.value;
687                         VectorAdd(viewangles, cl.punchangle, viewangles);
688                         viewangles[ROLL] += V_CalcRoll(clviewangles, clvelocity);
689                         if (v_dmg_time > 0)
690                         {
691                                 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
692                                 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
693                         }
694                         // origin
695                         VectorAdd(vieworg, cl.punchvector, vieworg);
696                         if (!cldead)
697                         {
698                                 double xyspeed, bob, bobfall;
699                                 double cycle; // double-precision because cl.time can be a very large number, where float would get stuttery at high time values
700                                 vec_t frametime;
701
702                                 frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale;
703
704                                 if(cl_followmodel.integer || cl_leanmodel.integer)
705                                 {
706                                         // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
707                                         if(teleported)
708                                         {
709                                                 // try to fix the first highpass; result is NOT
710                                                 // perfect! TODO find a better fix
711                                                 VectorCopy(viewangles, cl.gunangles_prev);
712                                                 VectorCopy(vieworg, cl.gunorg_prev);
713                                         }
714
715                                         // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
716                                         VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
717                                         highpass3_limited(vieworg, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_up_highpass1.value, cl_followmodel_up_limit.value, cl.gunorg_highpass, gunorg);
718                                         VectorCopy(vieworg, cl.gunorg_prev);
719                                         VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
720
721                                         // in the highpass, we _store_ the DIFFERENCE to the actual view angles...
722                                         VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
723                                         cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
724                                         cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
725                                         cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
726                                         highpass3_limited(viewangles, frametime*cl_leanmodel_up_highpass1.value, cl_leanmodel_up_limit.value, frametime*cl_leanmodel_side_highpass1.value, cl_leanmodel_side_limit.value, 0, 0, cl.gunangles_highpass, gunangles);
727                                         VectorCopy(viewangles, cl.gunangles_prev);
728                                         VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
729
730                                         // 3. calculate the RAW adjustment vectors
731                                         gunorg[0] *= -cl_followmodel_side_speed.value;
732                                         gunorg[1] *= -cl_followmodel_side_speed.value;
733                                         gunorg[2] *= -cl_followmodel_up_speed.value;
734
735                                         gunangles[PITCH] *= -cl_leanmodel_up_speed.value;
736                                         gunangles[YAW] *= -cl_leanmodel_side_speed.value;
737                                         gunangles[ROLL] = 0;
738
739                                         // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
740                                         //    trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
741                                         highpass3(gunorg, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_up_highpass.value, cl.gunorg_adjustment_highpass, gunorg);
742                                         lowpass3(gunorg, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_up_lowpass.value, cl.gunorg_adjustment_lowpass, gunorg);
743                                         // we assume here: PITCH = 0, YAW = 1, ROLL = 2
744                                         highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
745                                         lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);
746
747                                         // 5. use the adjusted vectors
748                                         VectorAdd(vieworg, gunorg, gunorg);
749                                         VectorAdd(viewangles, gunangles, gunangles);
750                                 }
751                                 else
752                                 {
753                                         // Just initialize gunorg/gunangles.
754                                         VectorCopy(vieworg, gunorg);
755                                         VectorCopy(viewangles, gunangles);
756                                 }
757
758                                 // bounded XY speed, used by several effects below
759                                 xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), cl_bob_velocity_limit.value);
760
761                                 // vertical view bobbing code
762                                 if (cl_bob.value && cl_bobcycle.value)
763                                 {
764                                         float bob_limit = cl_bob_limit.value;
765
766                                         if (cl_bob_limit_heightcheck.integer)
767                                         {
768                                                 // use traces to determine what range the view can bob in, and scale down the bob as needed
769                                                 float trace1fraction;
770                                                 float trace2fraction;
771                                                 vec3_t bob_height_check_dest;
772
773                                                 // these multipliers are expanded a bit (the actual bob sin range is from -0.4 to 1.0) to reduce nearclip issues, especially on water surfaces
774                                                 bob_height_check_dest[0] = vieworg[0];
775                                                 bob_height_check_dest[1] = vieworg[1];
776                                                 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * 1.1f;
777                                                 trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
778                                                 trace1fraction = trace.fraction;
779
780                                                 bob_height_check_dest[0] = vieworg[0];
781                                                 bob_height_check_dest[1] = vieworg[1];
782                                                 bob_height_check_dest[2] = vieworg[2] + cl_bob_limit.value * -0.5f;
783                                                 trace = CL_TraceLine(vieworg, bob_height_check_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY | (cl_bob_limit_heightcheck_dontcrosswatersurface.integer ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, MATERIALFLAGMASK_TRANSLUCENT, collision_extendmovelength.value, true, false, NULL, false, true);
784                                                 trace2fraction = trace.fraction;
785
786                                                 bob_limit *= min(trace1fraction, trace2fraction);
787                                         }
788
789                                         // LadyHavoc: this code is *weird*, but not replacable (I think it
790                                         // should be done in QC on the server, but oh well, quake is quake)
791                                         // LadyHavoc: figured out bobup: the time at which the sin is at 180
792                                         // degrees (which allows lengthening or squishing the peak or valley)
793                                         cycle = cl.time / cl_bobcycle.value;
794                                         cycle -= (int) cycle;
795                                         if (cycle < cl_bobup.value)
796                                                 cycle = sin(M_PI * cycle / cl_bobup.value);
797                                         else
798                                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
799                                         // bob is proportional to velocity in the xy plane
800                                         // (don't count Z, or jumping messes it up)
801                                         bob = xyspeed * cl_bob.value;
802                                         bob = bob*0.3 + bob*0.7*cycle;
803                                         bob = bound(-7, bob, bob_limit);
804                                         vieworg[2] += bob;
805                                         // we also need to adjust gunorg, or this appears like pushing the gun!
806                                         // In the old code, this was applied to vieworg BEFORE copying to gunorg,
807                                         // but this is not viable with the new followmodel code as that would mean
808                                         // that followmodel would work on the munged-by-bob vieworg and do feedback
809                                         gunorg[2] += bob;
810                                 }
811
812                                 // horizontal view bobbing code
813                                 if (cl_bob2.value && cl_bob2cycle.value)
814                                 {
815                                         vec3_t bob2vel;
816                                         vec3_t forward, right, up;
817                                         float side, front;
818
819                                         cycle = cl.time / cl_bob2cycle.value;
820                                         cycle -= (int) cycle;
821                                         if (cycle < 0.5)
822                                                 cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
823                                         else
824                                                 cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5);
825                                         bob = cl_bob2.value * cycle;
826
827                                         // this value slowly decreases from 1 to 0 when we stop touching the ground.
828                                         // The cycle is later multiplied with it so the view smooths back to normal
829                                         if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping
830                                                 cl.bob2_smooth = 1;
831                                         else
832                                         {
833                                                 if(cl.bob2_smooth > 0)
834                                                         cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1);
835                                                 else
836                                                         cl.bob2_smooth = 0;
837                                         }
838
839                                         // calculate the front and side of the player between the X and Y axes
840                                         AngleVectors(viewangles, forward, right, up);
841                                         // now get the speed based on those angles. The bounds should match the same value as xyspeed's
842                                         side = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, right) * cl.bob2_smooth, cl_bob_velocity_limit.value);
843                                         front = bound(-cl_bob_velocity_limit.value, DotProduct (clvelocity, forward) * cl.bob2_smooth, cl_bob_velocity_limit.value);
844                                         VectorScale(forward, bob, forward);
845                                         VectorScale(right, bob, right);
846                                         // we use side with forward and front with right, so the bobbing goes
847                                         // to the side when we walk forward and to the front when we strafe
848                                         VectorMAMAM(side, forward, front, right, 0, up, bob2vel);
849                                         vieworg[0] += bob2vel[0];
850                                         vieworg[1] += bob2vel[1];
851                                         // we also need to adjust gunorg, or this appears like pushing the gun!
852                                         // In the old code, this was applied to vieworg BEFORE copying to gunorg,
853                                         // but this is not viable with the new followmodel code as that would mean
854                                         // that followmodel would work on the munged-by-bob vieworg and do feedback
855                                         gunorg[0] += bob2vel[0];
856                                         gunorg[1] += bob2vel[1];
857                                 }
858
859                                 // fall bobbing code
860                                 // causes the view to swing down and back up when touching the ground
861                                 if (cl_bobfall.value && cl_bobfallcycle.value)
862                                 {
863                                         if (!clonground)
864                                         {
865                                                 cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
866                                                 if (clvelocity[2] < -cl_bobfallminspeed.value)
867                                                         cl.bobfall_swing = 1;
868                                                 else
869                                                         cl.bobfall_swing = 0; // TODO really?
870                                         }
871                                         else
872                                         {
873                                                 cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime);
874
875                                                 bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed;
876                                                 vieworg[2] += bobfall;
877                                                 gunorg[2] += bobfall;
878                                         }
879                                 }
880
881                                 // gun model bobbing code
882                                 if (cl_bobmodel.value)
883                                 {
884                                         // calculate for swinging gun model
885                                         // the gun bobs when running on the ground, but doesn't bob when you're in the air.
886                                         // Sajt: I tried to smooth out the transitions between bob and no bob, which works
887                                         // for the most part, but for some reason when you go through a message trigger or
888                                         // pick up an item or anything like that it will momentarily jolt the gun.
889                                         vec3_t forward, right, up;
890                                         float bspeed;
891                                         float s;
892                                         float t;
893
894                                         s = cl.time * cl_bobmodel_speed.value;
895                                         if (clonground)
896                                         {
897                                                 if (cl.time - cl.hitgroundtime < 0.2)
898                                                 {
899                                                         // just hit the ground, speed the bob back up over the next 0.2 seconds
900                                                         t = cl.time - cl.hitgroundtime;
901                                                         t = bound(0, t, 0.2);
902                                                         t *= 5;
903                                                 }
904                                                 else
905                                                         t = 1;
906                                         }
907                                         else
908                                         {
909                                                 // recently left the ground, slow the bob down over the next 0.2 seconds
910                                                 t = cl.time - cl.lastongroundtime;
911                                                 t = 0.2 - bound(0, t, 0.2);
912                                                 t *= 5;
913                                         }
914
915                                         bspeed = xyspeed * 0.01f;
916                                         AngleVectors (gunangles, forward, right, up);
917                                         bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
918                                         VectorMA (gunorg, bob, right, gunorg);
919                                         bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
920                                         VectorMA (gunorg, bob, up, gunorg);
921                                         // Classic Quake bobbing
922                                         bob = (cl_bobmodel_classic.integer ? xyspeed * cl_bob.value * 0.25 * cycle : bspeed * cl_bobmodel_forward.value * cos(s * 2) * t) * cl_viewmodel_scale.value;
923                                         VectorMA (gunorg, (cl_bobmodel_classic.integer ? (bob > 1 ? 1 : bob) : bob), forward, gunorg);
924                                 }
925                         }
926                 }
927                 // calculate a view matrix for rendering the scene
928                 if (v_idlescale.value)
929                 {
930                         viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
931                         viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
932                         viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
933                 }
934                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1);
935                 if (v_yshearing.value > 0)
936                         Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
937
938                 // calculate a viewmodel matrix for use in view-attached entities
939                 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
940                 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
941
942                 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
943                 if (v_yshearing.value > 0)
944                         Matrix4x4_QuakeToDuke3D(&viewmodelmatrix_withbob, &viewmodelmatrix_withbob, v_yshearing.value);
945
946                 VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
947                 VectorCopy(viewangles, cl.csqc_viewanglesfromengine);
948
949                 Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
950                 Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob);
951         }
952
953         cl.calcrefdef_prevtime = cl.time;
954 }
955
956 void V_CalcRefdef (void)
957 {
958         entity_t *ent;
959         qboolean cldead;
960
961         if (cls.state == ca_connected && cls.signon == SIGNONS && !cl.csqc_server2csqcentitynumber[cl.viewentity])
962         {
963                 // ent is the view entity (visible when out of body)
964                 ent = &cl.entities[cl.viewentity];
965
966                 cldead = (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && cl.stats[STAT_HEALTH] != -2342);
967                 V_CalcRefdefUsing(&ent->render.matrix, cl.viewangles, !ent->persistent.trail_allowed, cl.onground, cl.cmd.jump, cl.stats[STAT_VIEWHEIGHT], cldead, cl.intermission != 0, cl.velocity); // FIXME use a better way to detect teleport/warp than trail_allowed
968         }
969         else
970         {
971                 viewmodelmatrix_nobob = identitymatrix;
972                 viewmodelmatrix_withbob = identitymatrix;
973                 cl.csqc_viewmodelmatrixfromengine = identitymatrix;
974                 r_refdef.view.matrix = identitymatrix;
975                 VectorClear(cl.csqc_vieworiginfromengine);
976                 VectorCopy(cl.viewangles, cl.csqc_viewanglesfromengine);
977         }
978 }
979
980 void V_MakeViewIsometric(void)
981 {
982         // when using isometric view to play normal games we have to rotate the camera to make the Ortho matrix do the right thing (forward as up the screen, etc)
983         matrix4x4_t relative;
984         matrix4x4_t modifiedview;
985         matrix4x4_t modify;
986         vec3_t forward, left, up, org;
987         float t[4][4];
988
989         r_refdef.view.useperspective = false;
990         r_refdef.view.usevieworiginculling = !r_trippy.value && v_isometric_usevieworiginculling.integer;
991         r_refdef.view.frustum_y = v_isometric_verticalfov.value * cl.viewzoom;
992         r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
993         r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
994         r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
995         r_refdef.view.ortho_x = r_refdef.view.frustum_x; // used by VM_CL_R_SetView
996         r_refdef.view.ortho_y = r_refdef.view.frustum_y; // used by VM_CL_R_SetView
997
998         t[0][0] = v_isometric_xx.value;
999         t[0][1] = v_isometric_xy.value;
1000         t[0][2] = v_isometric_xz.value;
1001         t[0][3] = 0.0f;
1002         t[1][0] = v_isometric_yx.value;
1003         t[1][1] = v_isometric_yy.value;
1004         t[1][2] = v_isometric_yz.value;
1005         t[1][3] = 0.0f;
1006         t[2][0] = v_isometric_zx.value;
1007         t[2][1] = v_isometric_zy.value;
1008         t[2][2] = v_isometric_zz.value;
1009         t[2][3] = 0.0f;
1010         t[3][0] = 0.0f;
1011         t[3][1] = 0.0f;
1012         t[3][2] = 0.0f;
1013         t[3][3] = 1.0f;
1014         Matrix4x4_FromArrayFloatGL(&modify, t[0]);
1015
1016         // if the orientation is locked, extract the origin and create just a translate matrix to start with
1017         if (v_isometric_locked_orientation.integer)
1018         {
1019                 vec3_t vx, vy, vz, origin;
1020                 Matrix4x4_ToVectors(&r_refdef.view.matrix, vx, vy, vz, origin);
1021                 Matrix4x4_CreateTranslate(&r_refdef.view.matrix, origin[0], origin[1], origin[2]);
1022         }
1023
1024         Matrix4x4_Concat(&modifiedview, &r_refdef.view.matrix, &modify);
1025         Matrix4x4_CreateFromQuakeEntity(&relative, v_isometric_tx.value, v_isometric_ty.value, v_isometric_tz.value, v_isometric_rot_pitch.value, v_isometric_rot_yaw.value, v_isometric_rot_roll.value, 1.0f);
1026         Matrix4x4_Concat(&r_refdef.view.matrix, &modifiedview, &relative);
1027         Matrix4x4_ToVectors(&r_refdef.view.matrix, forward, left, up, org);
1028         VectorMAMAMAM(1.0f, org, v_isometric_relx.value, forward, v_isometric_rely.value, left, v_isometric_relz.value, up, org);
1029         Matrix4x4_FromVectors(&r_refdef.view.matrix, forward, left, up, org);
1030
1031         if (v_isometric_flipcullface.integer)
1032         {
1033                 int a = r_refdef.view.cullface_front;
1034                 r_refdef.view.cullface_front = r_refdef.view.cullface_back;
1035                 r_refdef.view.cullface_back = a;
1036         }
1037 }
1038
1039
1040 void V_FadeViewFlashs(void)
1041 {
1042         // don't flash if time steps backwards
1043         if (cl.time <= cl.oldtime)
1044                 return;
1045         // drop the damage value
1046         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_DAMAGE].alphafade;
1047         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
1048                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
1049         // drop the bonus value
1050         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_BONUS].alphafade;
1051         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
1052                 cl.cshifts[CSHIFT_BONUS].percent = 0;
1053 }
1054
1055 void V_CalcViewBlend(void)
1056 {
1057         float a2;
1058         int j;
1059         r_refdef.viewblend[0] = 0;
1060         r_refdef.viewblend[1] = 0;
1061         r_refdef.viewblend[2] = 0;
1062         r_refdef.viewblend[3] = 0;
1063         r_refdef.frustumscale_x = 1;
1064         r_refdef.frustumscale_y = 1;
1065         if (cls.state == ca_connected && cls.signon == SIGNONS)
1066         {
1067                 // set contents color
1068                 int supercontents;
1069                 vec3_t vieworigin;
1070                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
1071                 supercontents = CL_PointSuperContents(vieworigin);
1072                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
1073                 {
1074                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
1075                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
1076                         if (supercontents & SUPERCONTENTS_LAVA)
1077                         {
1078                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
1079                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1080                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1081                         }
1082                         else if (supercontents & SUPERCONTENTS_SLIME)
1083                         {
1084                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1085                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
1086                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
1087                         }
1088                         else
1089                         {
1090                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
1091                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
1092                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
1093                         }
1094                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
1095                 }
1096                 else
1097                 {
1098                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
1099                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
1100                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
1101                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
1102                 }
1103
1104                 if (gamemode != GAME_TRANSFUSION)
1105                 {
1106                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
1107                         {
1108                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1109                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
1110                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
1111                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1112                         }
1113                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
1114                         {
1115                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
1116                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1117                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1118                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
1119                         }
1120                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1121                         {
1122                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
1123                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
1124                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
1125                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
1126                         }
1127                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1128                         {
1129                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
1130                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
1131                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
1132                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
1133                         }
1134                         else
1135                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
1136                 }
1137
1138                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
1139                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
1140                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
1141                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
1142
1143                 // LadyHavoc: fixed V_CalcBlend
1144                 for (j = 0;j < NUM_CSHIFTS;j++)
1145                 {
1146                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
1147                         if (a2 > 0)
1148                         {
1149                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
1150                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
1151                         }
1152                 }
1153                 // saturate color (to avoid blending in black)
1154                 if (r_refdef.viewblend[3])
1155                 {
1156                         a2 = 1 / r_refdef.viewblend[3];
1157                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
1158                 }
1159                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0], 255.0f);
1160                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1], 255.0f);
1161                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2], 255.0f);
1162                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
1163                 if (vid.sRGB3D)
1164                 {
1165                         r_refdef.viewblend[0] = Image_LinearFloatFromsRGB(r_refdef.viewblend[0]);
1166                         r_refdef.viewblend[1] = Image_LinearFloatFromsRGB(r_refdef.viewblend[1]);
1167                         r_refdef.viewblend[2] = Image_LinearFloatFromsRGB(r_refdef.viewblend[2]);
1168                 }
1169                 else
1170                 {
1171                         r_refdef.viewblend[0] *= (1.0f/256.0f);
1172                         r_refdef.viewblend[1] *= (1.0f/256.0f);
1173                         r_refdef.viewblend[2] *= (1.0f/256.0f);
1174                 }
1175                 
1176                 // Samual: Ugly hack, I know. But it's the best we can do since
1177                 // there is no way to detect client states from the engine.
1178                 if (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && 
1179                         cl.stats[STAT_HEALTH] != -2342 && cl_deathfade.value > 0)
1180                 {
1181                         cl.deathfade += cl_deathfade.value * max(0.00001, cl.time - cl.oldtime);
1182                         cl.deathfade = bound(0.0f, cl.deathfade, 0.9f);
1183                 }
1184                 else
1185                         cl.deathfade = 0.0f;
1186
1187                 if(cl.deathfade > 0)
1188                 {
1189                         float a;
1190                         float deathfadevec[3] = {0.3f, 0.0f, 0.0f};
1191                         a = r_refdef.viewblend[3] + cl.deathfade - r_refdef.viewblend[3]*cl.deathfade;
1192                         if(a > 0)
1193                                 VectorMAM(r_refdef.viewblend[3] * (1 - cl.deathfade) / a, r_refdef.viewblend, cl.deathfade / a, deathfadevec, r_refdef.viewblend);
1194                         r_refdef.viewblend[3] = a;
1195                 }
1196         }
1197 }
1198
1199 //============================================================================
1200
1201 /*
1202 =============
1203 V_Init
1204 =============
1205 */
1206 void V_Init (void)
1207 {
1208         Cmd_AddCommand(CMD_CLIENT | CMD_CLIENT_FROM_SERVER, "v_cshift", V_cshift_f, "sets tint color of view");
1209         Cmd_AddCommand(CMD_CLIENT | CMD_CLIENT_FROM_SERVER, "bf", V_BonusFlash_f, "briefly flashes a bright color tint on view (used when items are picked up); optionally takes R G B [A [alphafade]] arguments to specify how the flash looks");
1210         Cmd_AddCommand(CMD_CLIENT, "centerview", V_StartPitchDrift_f, "gradually recenter view (stop looking up/down)");
1211
1212         Cvar_RegisterVariable (&v_centermove);
1213         Cvar_RegisterVariable (&v_centerspeed);
1214
1215         Cvar_RegisterVariable (&v_iyaw_cycle);
1216         Cvar_RegisterVariable (&v_iroll_cycle);
1217         Cvar_RegisterVariable (&v_ipitch_cycle);
1218         Cvar_RegisterVariable (&v_iyaw_level);
1219         Cvar_RegisterVariable (&v_iroll_level);
1220         Cvar_RegisterVariable (&v_ipitch_level);
1221
1222         Cvar_RegisterVariable(&v_isometric);
1223         Cvar_RegisterVariable(&v_isometric_verticalfov);
1224         Cvar_RegisterVariable(&v_isometric_xx);
1225         Cvar_RegisterVariable(&v_isometric_xy);
1226         Cvar_RegisterVariable(&v_isometric_xz);
1227         Cvar_RegisterVariable(&v_isometric_yx);
1228         Cvar_RegisterVariable(&v_isometric_yy);
1229         Cvar_RegisterVariable(&v_isometric_yz);
1230         Cvar_RegisterVariable(&v_isometric_zx);
1231         Cvar_RegisterVariable(&v_isometric_zy);
1232         Cvar_RegisterVariable(&v_isometric_zz);
1233         Cvar_RegisterVariable(&v_isometric_tx);
1234         Cvar_RegisterVariable(&v_isometric_ty);
1235         Cvar_RegisterVariable(&v_isometric_tz);
1236         Cvar_RegisterVariable(&v_isometric_rot_pitch);
1237         Cvar_RegisterVariable(&v_isometric_rot_yaw);
1238         Cvar_RegisterVariable(&v_isometric_rot_roll);
1239         Cvar_RegisterVariable(&v_isometric_relx);
1240         Cvar_RegisterVariable(&v_isometric_rely);
1241         Cvar_RegisterVariable(&v_isometric_relz);
1242         Cvar_RegisterVariable(&v_isometric_flipcullface);
1243         Cvar_RegisterVariable(&v_isometric_locked_orientation);
1244         Cvar_RegisterVariable(&v_isometric_usevieworiginculling);
1245
1246         Cvar_RegisterVariable (&v_idlescale);
1247         Cvar_RegisterVariable (&crosshair);
1248
1249         Cvar_RegisterVariable (&cl_rollspeed);
1250         Cvar_RegisterVariable (&cl_rollangle);
1251         Cvar_RegisterVariable (&cl_bob);
1252         Cvar_RegisterVariable (&cl_bobcycle);
1253         Cvar_RegisterVariable (&cl_bobup);
1254         Cvar_RegisterVariable (&cl_bob2);
1255         Cvar_RegisterVariable (&cl_bob2cycle);
1256         Cvar_RegisterVariable (&cl_bob2smooth);
1257         Cvar_RegisterVariable (&cl_bobfall);
1258         Cvar_RegisterVariable (&cl_bobfallcycle);
1259         Cvar_RegisterVariable (&cl_bobfallminspeed);
1260         Cvar_RegisterVariable (&cl_bobmodel);
1261         Cvar_RegisterVariable (&cl_bobmodel_side);
1262         Cvar_RegisterVariable (&cl_bobmodel_up);
1263         Cvar_RegisterVariable (&cl_bobmodel_forward);
1264         Cvar_RegisterVariable (&cl_bobmodel_classic);
1265         Cvar_RegisterVariable (&cl_bobmodel_speed);
1266         Cvar_RegisterVariable (&cl_bob_limit);
1267         Cvar_RegisterVariable (&cl_bob_limit_heightcheck);
1268         Cvar_RegisterVariable (&cl_bob_limit_heightcheck_dontcrosswatersurface);
1269         Cvar_RegisterVariable (&cl_bob_velocity_limit);
1270
1271         Cvar_RegisterVariable (&cl_leanmodel);
1272         Cvar_RegisterVariable (&cl_leanmodel_side_speed);
1273         Cvar_RegisterVariable (&cl_leanmodel_side_limit);
1274         Cvar_RegisterVariable (&cl_leanmodel_side_highpass1);
1275         Cvar_RegisterVariable (&cl_leanmodel_side_lowpass);
1276         Cvar_RegisterVariable (&cl_leanmodel_side_highpass);
1277         Cvar_RegisterVariable (&cl_leanmodel_up_speed);
1278         Cvar_RegisterVariable (&cl_leanmodel_up_limit);
1279         Cvar_RegisterVariable (&cl_leanmodel_up_highpass1);
1280         Cvar_RegisterVariable (&cl_leanmodel_up_lowpass);
1281         Cvar_RegisterVariable (&cl_leanmodel_up_highpass);
1282
1283         Cvar_RegisterVariable (&cl_followmodel);
1284         Cvar_RegisterVariable (&cl_followmodel_side_speed);
1285         Cvar_RegisterVariable (&cl_followmodel_side_limit);
1286         Cvar_RegisterVariable (&cl_followmodel_side_highpass1);
1287         Cvar_RegisterVariable (&cl_followmodel_side_lowpass);
1288         Cvar_RegisterVariable (&cl_followmodel_side_highpass);
1289         Cvar_RegisterVariable (&cl_followmodel_up_speed);
1290         Cvar_RegisterVariable (&cl_followmodel_up_limit);
1291         Cvar_RegisterVariable (&cl_followmodel_up_highpass1);
1292         Cvar_RegisterVariable (&cl_followmodel_up_lowpass);
1293         Cvar_RegisterVariable (&cl_followmodel_up_highpass);
1294
1295         Cvar_RegisterVariable (&cl_viewmodel_scale);
1296
1297         Cvar_RegisterVariable (&v_kicktime);
1298         Cvar_RegisterVariable (&v_kickroll);
1299         Cvar_RegisterVariable (&v_kickpitch);
1300
1301         Cvar_RegisterVariable (&cl_stairsmoothspeed);
1302         
1303         Cvar_RegisterVariable (&cl_smoothviewheight);
1304
1305         Cvar_RegisterVariable (&chase_back);
1306         Cvar_RegisterVariable (&chase_up);
1307         Cvar_RegisterVariable (&chase_active);
1308         Cvar_RegisterVariable (&chase_overhead);
1309         Cvar_RegisterVariable (&chase_pitchangle);
1310         Cvar_RegisterVariable (&chase_stevie);
1311
1312         Cvar_RegisterVariable (&v_deathtilt);
1313         Cvar_RegisterVariable (&v_deathtiltangle);
1314
1315         Cvar_RegisterVariable (&v_yshearing);
1316 }
1317