]> git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
39c4be9c29009accc70ecbf5946081858ac9158b
[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
25 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin);
26
27 /*
28
29 The view is allowed to move slightly from it's true position for bobbing,
30 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
31 entities sent from the server may not include everything in the pvs, especially
32 when crossing a water boudnary.
33
34 */
35
36 cvar_t cl_rollspeed = {0, "cl_rollspeed", "200", "how much strafing is necessary to tilt the view"};
37 cvar_t cl_rollangle = {0, "cl_rollangle", "2.0", "how much to tilt the view when strafing"};
38
39 cvar_t cl_bob = {CVAR_SAVE, "cl_bob","0.02", "view bobbing amount"};
40 cvar_t cl_bobcycle = {CVAR_SAVE, "cl_bobcycle","0.6", "view bobbing speed"};
41 cvar_t cl_bobup = {CVAR_SAVE, "cl_bobup","0.5", "view bobbing adjustment that makes the up or down swing of the bob last longer"};
42
43 cvar_t cl_bobmodel = {CVAR_SAVE, "cl_bobmodel", "1", "enables gun bobbing"};
44 cvar_t cl_bobmodel_side = {CVAR_SAVE, "cl_bobmodel_side", "0.15", "gun bobbing sideways sway amount"};
45 cvar_t cl_bobmodel_up = {CVAR_SAVE, "cl_bobmodel_up", "0.06", "gun bobbing upward movement amount"};
46 cvar_t cl_bobmodel_speed = {CVAR_SAVE, "cl_bobmodel_speed", "7", "gun bobbing speed"};
47
48 cvar_t cl_leanmodel_side = {CVAR_SAVE, "cl_leanmodel_side", "1", "enables gun leaning sideways"};
49 cvar_t cl_leanmodel_side_speed = {CVAR_SAVE, "cl_leanmodel_side_speed", "2.5", "gun leaning sideways speed"};
50 cvar_t cl_leanmodel_side_limit = {CVAR_SAVE, "cl_leanmodel_side_limit", "7.5", "gun leaning sideways limit"};
51 cvar_t cl_leanmodel_up = {CVAR_SAVE, "cl_leanmodel_up", "1", "enables gun leaning upward"};
52 cvar_t cl_leanmodel_up_speed = {CVAR_SAVE, "cl_leanmodel_up_speed", "2", "gun leaning upward speed"};
53 cvar_t cl_leanmodel_up_limit = {CVAR_SAVE, "cl_leanmodel_up_limit", "5", "gun leaning upward limit"};
54
55 cvar_t cl_viewmodel_scale = {0, "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"};
56
57 cvar_t v_kicktime = {0, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
58 cvar_t v_kickroll = {0, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
59 cvar_t v_kickpitch = {0, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
60
61 cvar_t v_iyaw_cycle = {0, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
62 cvar_t v_iroll_cycle = {0, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
63 cvar_t v_ipitch_cycle = {0, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
64 cvar_t v_iyaw_level = {0, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
65 cvar_t v_iroll_level = {0, "v_iroll_level", "0.1", "v_idlescale roll amount"};
66 cvar_t v_ipitch_level = {0, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
67
68 cvar_t v_idlescale = {0, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
69
70 cvar_t crosshair = {CVAR_SAVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
71
72 cvar_t v_centermove = {0, "v_centermove", "0.15", "how long before the view begins to center itself (if freelook/+mlook/+jlook/+klook are off)"};
73 cvar_t v_centerspeed = {0, "v_centerspeed","500", "how fast the view centers itself"};
74
75 cvar_t cl_stairsmoothspeed = {CVAR_SAVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
76
77 cvar_t chase_back = {CVAR_SAVE, "chase_back", "48", "chase cam distance from the player"};
78 cvar_t chase_up = {CVAR_SAVE, "chase_up", "24", "chase cam distance from the player"};
79 cvar_t chase_active = {CVAR_SAVE, "chase_active", "0", "enables chase cam"};
80 cvar_t chase_overhead = {CVAR_SAVE, "chase_overhead", "0", "chase cam looks straight down if this is not zero"};
81 // GAME_GOODVSBAD2
82 cvar_t chase_stevie = {0, "chase_stevie", "0", "chase cam view from above (used only by GoodVsBad2)"};
83
84 cvar_t v_deathtilt = {0, "v_deathtilt", "1", "whether to use sideways view when dead"};
85 cvar_t v_deathtiltangle = {0, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
86
87 // Prophecy camera pitchangle by Alexander "motorsep" Zubov
88 cvar_t chase_pitchangle = {CVAR_SAVE, "chase_pitchangle", "55", "chase cam pitch angle"};
89
90 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
91
92
93 /*
94 ===============
95 V_CalcRoll
96
97 Used by view and sv_user
98 ===============
99 */
100 float V_CalcRoll (vec3_t angles, vec3_t velocity)
101 {
102         vec3_t  right;
103         float   sign;
104         float   side;
105         float   value;
106
107         AngleVectors (angles, NULL, right, NULL);
108         side = DotProduct (velocity, right);
109         sign = side < 0 ? -1 : 1;
110         side = fabs(side);
111
112         value = cl_rollangle.value;
113
114         if (side < cl_rollspeed.value)
115                 side = side * value / cl_rollspeed.value;
116         else
117                 side = value;
118
119         return side*sign;
120
121 }
122
123 void V_StartPitchDrift (void)
124 {
125         if (cl.laststop == cl.time)
126                 return;         // something else is keeping it from drifting
127
128         if (cl.nodrift || !cl.pitchvel)
129         {
130                 cl.pitchvel = v_centerspeed.value;
131                 cl.nodrift = false;
132                 cl.driftmove = 0;
133         }
134 }
135
136 void V_StopPitchDrift (void)
137 {
138         cl.laststop = cl.time;
139         cl.nodrift = true;
140         cl.pitchvel = 0;
141 }
142
143 /*
144 ===============
145 V_DriftPitch
146
147 Moves the client pitch angle towards cl.idealpitch sent by the server.
148
149 If the user is adjusting pitch manually, either with lookup/lookdown,
150 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
151
152 Drifting is enabled when the center view key is hit, mlook is released and
153 lookspring is non 0, or when
154 ===============
155 */
156 void V_DriftPitch (void)
157 {
158         float           delta, move;
159
160         if (noclip_anglehack || !cl.onground || cls.demoplayback )
161         {
162                 cl.driftmove = 0;
163                 cl.pitchvel = 0;
164                 return;
165         }
166
167 // don't count small mouse motion
168         if (cl.nodrift)
169         {
170                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
171                         cl.driftmove = 0;
172                 else
173                         cl.driftmove += cl.realframetime;
174
175                 if ( cl.driftmove > v_centermove.value)
176                 {
177                         V_StartPitchDrift ();
178                 }
179                 return;
180         }
181
182         delta = cl.idealpitch - cl.viewangles[PITCH];
183
184         if (!delta)
185         {
186                 cl.pitchvel = 0;
187                 return;
188         }
189
190         move = cl.realframetime * cl.pitchvel;
191         cl.pitchvel += cl.realframetime * v_centerspeed.value;
192
193         if (delta > 0)
194         {
195                 if (move > delta)
196                 {
197                         cl.pitchvel = 0;
198                         move = delta;
199                 }
200                 cl.viewangles[PITCH] += move;
201         }
202         else if (delta < 0)
203         {
204                 if (move > -delta)
205                 {
206                         cl.pitchvel = 0;
207                         move = -delta;
208                 }
209                 cl.viewangles[PITCH] -= move;
210         }
211 }
212
213
214 /*
215 ==============================================================================
216
217                                                 SCREEN FLASHES
218
219 ==============================================================================
220 */
221
222
223 /*
224 ===============
225 V_ParseDamage
226 ===============
227 */
228 void V_ParseDamage (void)
229 {
230         int armor, blood;
231         vec3_t from;
232         //vec3_t forward, right;
233         vec3_t localfrom;
234         entity_t *ent;
235         //float side;
236         float count;
237
238         armor = MSG_ReadByte ();
239         blood = MSG_ReadByte ();
240         MSG_ReadVector(from, cls.protocol);
241
242         // Send the Dmg Globals to CSQC
243         CL_VM_UpdateDmgGlobals(blood, armor, from);
244
245         count = blood*0.5 + armor*0.5;
246         if (count < 10)
247                 count = 10;
248
249         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
250
251         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
252         cl.cshifts[CSHIFT_DAMAGE].alphafade = 150;
253         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
254                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
255         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
256                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
257
258         if (armor > blood)
259         {
260                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
261                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
262                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
263         }
264         else if (armor)
265         {
266                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
267                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
268                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
269         }
270         else
271         {
272                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
273                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
274                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
275         }
276
277         // calculate view angle kicks
278         if (cl.entities[cl.viewentity].state_current.active)
279         {
280                 ent = &cl.entities[cl.viewentity];
281                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
282                 VectorNormalize(localfrom);
283                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
284                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
285                 v_dmg_time = v_kicktime.value;
286         }
287 }
288
289 static cshift_t v_cshift;
290
291 /*
292 ==================
293 V_cshift_f
294 ==================
295 */
296 static void V_cshift_f (void)
297 {
298         v_cshift.destcolor[0] = atof(Cmd_Argv(1));
299         v_cshift.destcolor[1] = atof(Cmd_Argv(2));
300         v_cshift.destcolor[2] = atof(Cmd_Argv(3));
301         v_cshift.percent = atof(Cmd_Argv(4));
302 }
303
304
305 /*
306 ==================
307 V_BonusFlash_f
308
309 When you run over an item, the server sends this command
310 ==================
311 */
312 static void V_BonusFlash_f (void)
313 {
314         if(Cmd_Argc() == 1)
315         {
316                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
317                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
318                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
319                 cl.cshifts[CSHIFT_BONUS].percent = 50;
320                 cl.cshifts[CSHIFT_BONUS].alphafade = 100;
321         }
322         else if(Cmd_Argc() >= 4 && Cmd_Argc() <= 6)
323         {
324                 cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1)) * 255;
325                 cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2)) * 255;
326                 cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3)) * 255;
327                 if(Cmd_Argc() >= 5)
328                         cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4)) * 255; // yes, these are HEXADECIMAL percent ;)
329                 else
330                         cl.cshifts[CSHIFT_BONUS].percent = 50;
331                 if(Cmd_Argc() >= 6)
332                         cl.cshifts[CSHIFT_BONUS].alphafade = atof(Cmd_Argv(5)) * 255;
333                 else
334                         cl.cshifts[CSHIFT_BONUS].alphafade = 100;
335         }
336         else
337                 Con_Printf("usage:\nbf, or bf R G B [A [alphafade]]\n");
338 }
339
340 /*
341 ==============================================================================
342
343                                                 VIEW RENDERING
344
345 ==============================================================================
346 */
347
348 extern matrix4x4_t viewmodelmatrix;
349
350 #include "cl_collision.h"
351 #include "csprogs.h"
352
353 /*
354 ==================
355 V_CalcRefdef
356
357 ==================
358 */
359 #if 0
360 static vec3_t eyeboxmins = {-16, -16, -24};
361 static vec3_t eyeboxmaxs = { 16,  16,  32};
362 #endif
363 float viewmodel_push_x, viewmodel_push_y;
364 vec3_t gunorg_follow;
365 void V_CalcRefdef (void)
366 {
367         entity_t *ent;
368         float vieworg[3], gunorg[3], viewangles[3], gunangles[3], smoothtime;
369 #if 0
370 // begin of chase camera bounding box size for proper collisions by Alexander Zubov
371         vec3_t camboxmins = {-3, -3, -3};
372         vec3_t camboxmaxs = {3, 3, 3};
373 // end of chase camera bounding box size for proper collisions by Alexander Zubov
374 #endif
375         trace_t trace;
376         VectorClear(gunorg);
377         viewmodelmatrix = identitymatrix;
378         r_refdef.view.matrix = identitymatrix;
379         if (cls.state == ca_connected && cls.signon == SIGNONS)
380         {
381                 // ent is the view entity (visible when out of body)
382                 ent = &cl.entities[cl.viewentity];
383                 // player can look around, so take the origin from the entity,
384                 // and the angles from the input system
385                 Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
386                 VectorCopy(cl.viewangles, viewangles);
387
388                 // calculate how much time has passed since the last V_CalcRefdef
389                 smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
390                 cl.stairsmoothtime = cl.time;
391
392                 // fade damage flash
393                 if (v_dmg_time > 0)
394                         v_dmg_time -= bound(0, smoothtime, 0.1);
395
396                 if (cl.intermission)
397                 {
398                         // entity is a fixed camera, just copy the matrix
399                         if (cls.protocol == PROTOCOL_QUAKEWORLD)
400                                 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);
401                         else
402                         {
403                                 r_refdef.view.matrix = ent->render.matrix;
404                                 Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, cl.stats[STAT_VIEWHEIGHT]);
405                         }
406                         viewmodelmatrix = r_refdef.view.matrix;
407                 }
408                 else
409                 {
410                         // smooth stair stepping, but only if onground and enabled
411                         if (!cl.onground || cl_stairsmoothspeed.value <= 0)
412                                 cl.stairsmoothz = vieworg[2];
413                         else
414                         {
415                                 if (cl.stairsmoothz < vieworg[2])
416                                         vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - 16, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
417                                 else if (cl.stairsmoothz > vieworg[2])
418                                         vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + 16);
419                         }
420
421                         // apply qw weapon recoil effect (this did not work in QW)
422                         // TODO: add a cvar to disable this
423                         viewangles[PITCH] += cl.qw_weaponkick;
424
425                         // apply the viewofs (even if chasecam is used)
426                         vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
427
428                         if (chase_active.value)
429                         {
430                                 // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov
431                                 vec_t camback, camup, dist, campitch, forward[3], chase_dest[3];
432
433                                 camback = chase_back.value;
434                                 camup = chase_up.value;
435                                 campitch = chase_pitchangle.value;
436
437                                 AngleVectors(viewangles, forward, NULL, NULL);
438
439                                 if (chase_overhead.integer)
440                                 {
441 #if 1
442                                         vec3_t offset;
443                                         vec3_t bestvieworg;
444 #endif
445                                         vec3_t up;
446                                         viewangles[PITCH] = 0;
447                                         AngleVectors(viewangles, forward, NULL, up);
448                                         // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
449                                         chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
450                                         chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
451                                         chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
452 #if 0
453 #if 1
454                                         //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
455                                         trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
456 #else
457                                         //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
458                                         trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
459 #endif
460                                         VectorCopy(trace.endpos, vieworg);
461                                         vieworg[2] -= 8;
462 #else
463                                         // trace from first person view location to our chosen third person view location
464 #if 1
465                                         trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
466 #else
467                                         trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
468 #endif
469                                         VectorCopy(trace.endpos, bestvieworg);
470                                         offset[2] = 0;
471                                         for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
472                                         {
473                                                 for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
474                                                 {
475                                                         AngleVectors(viewangles, NULL, NULL, up);
476                                                         chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
477                                                         chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
478                                                         chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
479 #if 1
480                                                         trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
481 #else
482                                                         trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
483 #endif
484                                                         if (bestvieworg[2] > trace.endpos[2])
485                                                                 bestvieworg[2] = trace.endpos[2];
486                                                 }
487                                         }
488                                         bestvieworg[2] -= 8;
489                                         VectorCopy(bestvieworg, vieworg);
490 #endif
491                                         viewangles[PITCH] = campitch;
492                                 }
493                                 else
494                                 {
495                                         if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
496                                         {
497                                                 // look straight down from high above
498                                                 viewangles[PITCH] = 90;
499                                                 camback = 2048;
500                                                 VectorSet(forward, 0, 0, -1);
501                                         }
502
503                                         // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
504                                         dist = -camback - 8;
505                                         chase_dest[0] = vieworg[0] + forward[0] * dist;
506                                         chase_dest[1] = vieworg[1] + forward[1] * dist;
507                                         chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
508                                         trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
509                                         VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
510                                 }
511                         }
512                         else
513                         {
514                                 // first person view from entity
515                                 // angles
516                                 if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
517                                         viewangles[ROLL] = v_deathtiltangle.value;
518                                 VectorAdd(viewangles, cl.punchangle, viewangles);
519                                 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.movement_velocity);
520                                 if (v_dmg_time > 0)
521                                 {
522                                         viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
523                                         viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
524                                 }
525                                 // origin
526                                 VectorAdd(vieworg, cl.punchvector, vieworg);
527                                 if (cl.stats[STAT_HEALTH] > 0)
528                                 {
529                                         double xyspeed, bob;
530
531                                         xyspeed = sqrt(cl.movement_velocity[0]*cl.movement_velocity[0] + cl.movement_velocity[1]*cl.movement_velocity[1]);
532                                         if (cl_bob.value && cl_bobcycle.value)
533                                         {
534                                                 float cycle;
535                                                 // LordHavoc: this code is *weird*, but not replacable (I think it
536                                                 // should be done in QC on the server, but oh well, quake is quake)
537                                                 // LordHavoc: figured out bobup: the time at which the sin is at 180
538                                                 // degrees (which allows lengthening or squishing the peak or valley)
539                                                 cycle = cl.time / cl_bobcycle.value;
540                                                 cycle -= (int) cycle;
541                                                 if (cycle < cl_bobup.value)
542                                                         cycle = sin(M_PI * cycle / cl_bobup.value);
543                                                 else
544                                                         cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
545                                                 // bob is proportional to velocity in the xy plane
546                                                 // (don't count Z, or jumping messes it up)
547                                                 bob = xyspeed * cl_bob.value;
548                                                 bob = bob*0.3 + bob*0.7*cycle;
549                                                 vieworg[2] += bound(-7, bob, 4);
550                                         }
551
552                                         if (cl_bob.value && cl_bobmodel.value)
553                                         {
554                                                 // calculate for swinging gun model
555                                                 // the gun bobs when running on the ground, but doesn't bob when you're in the air.
556                                                 // Sajt: I tried to smooth out the transitions between bob and no bob, which works
557                                                 // for the most part, but for some reason when you go through a message trigger or
558                                                 // pick up an item or anything like that it will momentarily jolt the gun.
559                                                 vec3_t forward, right, up;
560                                                 float bspeed;
561                                                 float s;
562                                                 float t;
563
564                                                 s = cl.time * cl_bobmodel_speed.value;
565                                                 if (cl.onground)
566                                                 {
567                                                         if (cl.time - cl.hitgroundtime < 0.2)
568                                                         {
569                                                                 // just hit the ground, speed the bob back up over the next 0.2 seconds
570                                                                 t = cl.time - cl.hitgroundtime;
571                                                                 t = bound(0, t, 0.2);
572                                                                 t *= 5;
573                                                         }
574                                                         else
575                                                                 t = 1;
576                                                 }
577                                                 else
578                                                 {
579                                                         // recently left the ground, slow the bob down over the next 0.2 seconds
580                                                         t = cl.time - cl.lastongroundtime;
581                                                         t = 0.2 - bound(0, t, 0.2);
582                                                         t *= 5;
583                                                 }
584
585                                                 bspeed = bound (0, xyspeed, 400) * 0.01f;
586                                                 AngleVectors (gunangles, forward, right, up);
587                                                 bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
588                                                 VectorMA (gunorg, bob, right, gunorg);
589                                                 bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
590                                                 VectorMA (gunorg, bob, up, gunorg);
591                                         }
592
593                                         float ef_speed = cl.realframetime * cl_leanmodel_up_speed.value;
594
595                                         // gun model leaning code
596                                         
597                                         // TODO 1 (done): Fix bug where model does a 360* turn when YAW jumps around the 0 - 360 rotation border
598                                         // TODO 2 (done): Implement limits (weapon model must not lean past a certain limit)
599                                         // TODO 3 (done): Cvar everything once the first TODOs are ready
600
601                                         if(cl_leanmodel_up.value && cl_leanmodel_up_speed.value * ef_speed < 1) // bad things happen if this goes over 1, so prevent the effect
602                                         {
603                                                 // prevent the gun from doing a 360* rotation when going around the 0 <-> 360 border
604                                                 if(cl.viewangles[PITCH] - viewmodel_push_x >= 180)
605                                                         viewmodel_push_x += 360;
606                                                 if(viewmodel_push_x - cl.viewangles[PITCH] >= 180)
607                                                         viewmodel_push_x -= 360;
608
609                                                 if(viewmodel_push_x < cl.viewangles[PITCH])
610                                                 {
611                                                         if(cl.viewangles[PITCH] - viewmodel_push_x > cl_leanmodel_up_limit.value)
612                                                                 viewmodel_push_x = cl.viewangles[PITCH] - cl_leanmodel_up_limit.value;
613                                                         else
614                                                                 viewmodel_push_x += (cl.viewangles[PITCH] - viewmodel_push_x) * cl_leanmodel_up_speed.value * ef_speed;
615                                                 }
616                                                 if(viewmodel_push_x > cl.viewangles[PITCH])
617                                                 {
618                                                         if(viewmodel_push_x - cl.viewangles[PITCH] > cl_leanmodel_up_limit.value)
619                                                                 viewmodel_push_x = cl.viewangles[PITCH] + cl_leanmodel_up_limit.value;
620                                                         else
621                                                                 viewmodel_push_x -= (viewmodel_push_x - cl.viewangles[PITCH]) * cl_leanmodel_up_speed.value * ef_speed;
622                                                 }
623                                         }
624                                         else
625                                                 viewmodel_push_x = cl.viewangles[PITCH];
626
627                                         if(cl_leanmodel_side.value && cl_leanmodel_side_speed.value * ef_speed < 1) // bad things happen if this goes over 1, so prevent the effect
628                                         {
629                                                 // prevent the gun from doing a 360* rotation when going around the 0 <-> 360 border
630                                                 if(cl.viewangles[YAW] - viewmodel_push_y >= 180)
631                                                         viewmodel_push_y += 360;
632                                                 if(viewmodel_push_y - cl.viewangles[YAW] >= 180)
633                                                         viewmodel_push_y -= 360;
634
635                                                 if(viewmodel_push_y < cl.viewangles[YAW])
636                                                 {
637                                                         if(cl.viewangles[YAW] - viewmodel_push_y > cl_leanmodel_side_limit.value)
638                                                                 viewmodel_push_y = cl.viewangles[YAW] - cl_leanmodel_side_limit.value;
639                                                         else
640                                                                 viewmodel_push_y += (cl.viewangles[YAW] - viewmodel_push_y) * cl_leanmodel_side_speed.value * ef_speed;
641                                                 }
642                                                 if(viewmodel_push_y > cl.viewangles[YAW])
643                                                 {
644                                                         if(viewmodel_push_y - cl.viewangles[YAW] > cl_leanmodel_side_limit.value)
645                                                                 viewmodel_push_y = cl.viewangles[YAW] + cl_leanmodel_side_limit.value;
646                                                         else
647                                                                 viewmodel_push_y -= (viewmodel_push_y - cl.viewangles[YAW]) * cl_leanmodel_side_speed.value * ef_speed;
648                                                 }
649                                         }
650                                         else
651                                                 viewmodel_push_y = cl.viewangles[YAW];
652
653                                         VectorSet(gunangles, viewmodel_push_x, viewmodel_push_y, viewangles[2]);
654
655                                 // gun model following code
656
657                                 if(gunorg_follow[0] < vieworg[0])
658                                 {
659                                         if(vieworg[0] - gunorg_follow[0] > 5)
660                                                 gunorg_follow[0] = vieworg[0] - 5;
661                                         else
662                                                 gunorg_follow[0] += (vieworg[0] - gunorg_follow[0]) * 5 * ef_speed;
663                                 }
664                                 if(gunorg_follow[0] > vieworg[0])
665                                 {
666                                         if(gunorg_follow[0] - vieworg[0] > 5)
667                                                 gunorg_follow[0] = vieworg[0] + 5;
668                                         else
669                                                 gunorg_follow[0] -= (gunorg_follow[0] - vieworg[0]) * 5 * ef_speed;
670                                 }
671
672                                 if(gunorg_follow[1] < vieworg[1])
673                                 {
674                                         if(vieworg[1] - gunorg_follow[1] > 5)
675                                                 gunorg_follow[1] = vieworg[1] - 5;
676                                         else
677                                                 gunorg_follow[1] += (vieworg[1] - gunorg_follow[1]) * 5 * ef_speed;
678                                 }
679                                 if(gunorg_follow[1] > vieworg[1])
680                                 {
681                                         if(gunorg_follow[1] - vieworg[1] > 5)
682                                                 gunorg_follow[1] = vieworg[1] + 5;
683                                         else
684                                                 gunorg_follow[1] -= (gunorg_follow[1] - vieworg[1]) * 5 * ef_speed;
685                                 }
686
687                                 if(gunorg_follow[2] < vieworg[2])
688                                 {
689                                         if(vieworg[2] - gunorg_follow[2] > 5)
690                                                 gunorg_follow[2] = vieworg[2] - 5;
691                                         else
692                                                 gunorg_follow[2] += (vieworg[2] - gunorg_follow[2]) * 5 * ef_speed;
693                                 }
694                                 if(gunorg_follow[2] > vieworg[2])
695                                 {
696                                         if(gunorg_follow[2] - vieworg[2] > 5)
697                                                 gunorg_follow[2] = vieworg[2] + 5;
698                                         else
699                                                 gunorg_follow[2] -= (gunorg_follow[2] - vieworg[2]) * 5 * ef_speed;
700                                 }
701
702                                 VectorCopy(gunorg_follow, gunorg);
703                                 }
704                         }
705                         // calculate a view matrix for rendering the scene
706                         if (v_idlescale.value)
707                                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, 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);
708                         else
709                                 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, 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);
710                         // calculate a viewmodel matrix for use in view-attached entities
711                         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
712                         VectorCopy(vieworg, cl.csqc_origin);
713                         VectorCopy(viewangles, cl.csqc_angles);
714                 }
715         }
716 }
717
718 void V_FadeViewFlashs(void)
719 {
720         // don't flash if time steps backwards
721         if (cl.time <= cl.oldtime)
722                 return;
723         // drop the damage value
724         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_DAMAGE].alphafade;
725         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
726                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
727         // drop the bonus value
728         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_BONUS].alphafade;
729         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
730                 cl.cshifts[CSHIFT_BONUS].percent = 0;
731 }
732
733 void V_CalcViewBlend(void)
734 {
735         float a2;
736         int j;
737         r_refdef.viewblend[0] = 0;
738         r_refdef.viewblend[1] = 0;
739         r_refdef.viewblend[2] = 0;
740         r_refdef.viewblend[3] = 0;
741         r_refdef.frustumscale_x = 1;
742         r_refdef.frustumscale_y = 1;
743         if (cls.state == ca_connected && cls.signon == SIGNONS)
744         {
745                 // set contents color
746                 int supercontents;
747                 vec3_t vieworigin;
748                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
749                 supercontents = CL_PointSuperContents(vieworigin);
750                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
751                 {
752                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
753                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
754                         if (supercontents & SUPERCONTENTS_LAVA)
755                         {
756                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
757                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
758                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
759                         }
760                         else if (supercontents & SUPERCONTENTS_SLIME)
761                         {
762                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
763                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
764                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
765                         }
766                         else
767                         {
768                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
769                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
770                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
771                         }
772                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
773                 }
774                 else
775                 {
776                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
777                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
778                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
779                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
780                 }
781
782                 if (gamemode != GAME_TRANSFUSION)
783                 {
784                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
785                         {
786                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
787                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
788                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
789                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
790                         }
791                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
792                         {
793                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
794                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
795                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
796                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
797                         }
798                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
799                         {
800                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
801                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
802                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
803                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
804                         }
805                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
806                         {
807                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
808                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
809                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
810                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
811                         }
812                         else
813                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
814                 }
815
816                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
817                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
818                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
819                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
820
821                 // LordHavoc: fixed V_CalcBlend
822                 for (j = 0;j < NUM_CSHIFTS;j++)
823                 {
824                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
825                         if (a2 > 0)
826                         {
827                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
828                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
829                         }
830                 }
831                 // saturate color (to avoid blending in black)
832                 if (r_refdef.viewblend[3])
833                 {
834                         a2 = 1 / r_refdef.viewblend[3];
835                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
836                 }
837                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
838                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
839                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
840                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
841                 
842                 // Samual: Ugly hack, I know. But it's the best we can do since
843                 // there is no way to detect client states from the engine.
844                 if (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && 
845                         cl.stats[STAT_HEALTH] != -2342 && cl_deathfade.value > 0)
846                 {
847                         cl.deathfade += cl_deathfade.value * max(0.00001, cl.time - cl.oldtime);
848                         cl.deathfade = bound(0.0f, cl.deathfade, 0.9f);
849                 }
850                 else
851                         cl.deathfade = 0.0f;
852
853                 if(cl.deathfade > 0)
854                 {
855                         float a;
856                         float deathfadevec[3] = {0.3f, 0.0f, 0.0f};
857                         a = r_refdef.viewblend[3] + cl.deathfade - r_refdef.viewblend[3]*cl.deathfade;
858                         if(a > 0)
859                                 VectorMAM(r_refdef.viewblend[3] * (1 - cl.deathfade) / a, r_refdef.viewblend, cl.deathfade / a, deathfadevec, r_refdef.viewblend);
860                         r_refdef.viewblend[3] = a;
861                 }
862         }
863 }
864
865 //============================================================================
866
867 /*
868 =============
869 V_Init
870 =============
871 */
872 void V_Init (void)
873 {
874         Cmd_AddCommand ("v_cshift", V_cshift_f, "sets tint color of view");
875         Cmd_AddCommand ("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");
876         Cmd_AddCommand ("centerview", V_StartPitchDrift, "gradually recenter view (stop looking up/down)");
877
878         Cvar_RegisterVariable (&v_centermove);
879         Cvar_RegisterVariable (&v_centerspeed);
880
881         Cvar_RegisterVariable (&v_iyaw_cycle);
882         Cvar_RegisterVariable (&v_iroll_cycle);
883         Cvar_RegisterVariable (&v_ipitch_cycle);
884         Cvar_RegisterVariable (&v_iyaw_level);
885         Cvar_RegisterVariable (&v_iroll_level);
886         Cvar_RegisterVariable (&v_ipitch_level);
887
888         Cvar_RegisterVariable (&v_idlescale);
889         Cvar_RegisterVariable (&crosshair);
890
891         Cvar_RegisterVariable (&cl_rollspeed);
892         Cvar_RegisterVariable (&cl_rollangle);
893         Cvar_RegisterVariable (&cl_bob);
894         Cvar_RegisterVariable (&cl_bobcycle);
895         Cvar_RegisterVariable (&cl_bobup);
896         Cvar_RegisterVariable (&cl_bobmodel);
897         Cvar_RegisterVariable (&cl_bobmodel_side);
898         Cvar_RegisterVariable (&cl_bobmodel_up);
899         Cvar_RegisterVariable (&cl_bobmodel_speed);
900
901         Cvar_RegisterVariable (&cl_leanmodel_side);
902         Cvar_RegisterVariable (&cl_leanmodel_side_speed);
903         Cvar_RegisterVariable (&cl_leanmodel_side_limit);
904         Cvar_RegisterVariable (&cl_leanmodel_up);
905         Cvar_RegisterVariable (&cl_leanmodel_up_speed);
906         Cvar_RegisterVariable (&cl_leanmodel_up_limit);
907
908         Cvar_RegisterVariable (&cl_viewmodel_scale);
909
910         Cvar_RegisterVariable (&v_kicktime);
911         Cvar_RegisterVariable (&v_kickroll);
912         Cvar_RegisterVariable (&v_kickpitch);
913
914         Cvar_RegisterVariable (&cl_stairsmoothspeed);
915
916         Cvar_RegisterVariable (&chase_back);
917         Cvar_RegisterVariable (&chase_up);
918         Cvar_RegisterVariable (&chase_active);
919         Cvar_RegisterVariable (&chase_overhead);
920         Cvar_RegisterVariable (&chase_pitchangle);
921         if (gamemode == GAME_GOODVSBAD2)
922                 Cvar_RegisterVariable (&chase_stevie);
923
924         Cvar_RegisterVariable (&v_deathtilt);
925         Cvar_RegisterVariable (&v_deathtiltangle);
926 }
927