]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
Part 2 of the viewmodel effects. Gunmodel now "follows" the player, causing it to...
[divverent/darkplaces.git] / view.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // view.c -- player eye positioning
21
22 #include "quakedef.h"
23 #include "cl_collision.h"
24
25 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                                         gunorg_follow[0] += (vieworg[0] - gunorg_follow[0]) * 5 * ef_speed;
659                                 if(gunorg_follow[0] > vieworg[0])
660                                         gunorg_follow[0] -= (gunorg_follow[0] - vieworg[0]) * 5 * ef_speed;
661
662                                 if(gunorg_follow[1] < vieworg[1])
663                                         gunorg_follow[1] += (vieworg[1] - gunorg_follow[1]) * 5 * ef_speed;
664                                 if(gunorg_follow[1] > vieworg[1])
665                                         gunorg_follow[1] -= (gunorg_follow[1] - vieworg[1]) * 5 * ef_speed;
666
667                                 if(gunorg_follow[2] < vieworg[2])
668                                         gunorg_follow[2] += (vieworg[2] - gunorg_follow[2]) * 5 * ef_speed;
669                                 if(gunorg_follow[2] > vieworg[2])
670                                         gunorg_follow[2] -= (gunorg_follow[2] - vieworg[2]) * 5 * ef_speed;
671
672                                 VectorCopy(gunorg_follow, gunorg);
673                                 }
674                         }
675                         // calculate a view matrix for rendering the scene
676                         if (v_idlescale.value)
677                                 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);
678                         else
679                                 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);
680                         // calculate a viewmodel matrix for use in view-attached entities
681                         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
682                         VectorCopy(vieworg, cl.csqc_origin);
683                         VectorCopy(viewangles, cl.csqc_angles);
684                 }
685         }
686 }
687
688 void V_FadeViewFlashs(void)
689 {
690         // don't flash if time steps backwards
691         if (cl.time <= cl.oldtime)
692                 return;
693         // drop the damage value
694         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_DAMAGE].alphafade;
695         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
696                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
697         // drop the bonus value
698         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*cl.cshifts[CSHIFT_BONUS].alphafade;
699         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
700                 cl.cshifts[CSHIFT_BONUS].percent = 0;
701 }
702
703 void V_CalcViewBlend(void)
704 {
705         float a2;
706         int j;
707         r_refdef.viewblend[0] = 0;
708         r_refdef.viewblend[1] = 0;
709         r_refdef.viewblend[2] = 0;
710         r_refdef.viewblend[3] = 0;
711         r_refdef.frustumscale_x = 1;
712         r_refdef.frustumscale_y = 1;
713         if (cls.state == ca_connected && cls.signon == SIGNONS)
714         {
715                 // set contents color
716                 int supercontents;
717                 vec3_t vieworigin;
718                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, vieworigin);
719                 supercontents = CL_PointSuperContents(vieworigin);
720                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
721                 {
722                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
723                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
724                         if (supercontents & SUPERCONTENTS_LAVA)
725                         {
726                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
727                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
728                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
729                         }
730                         else if (supercontents & SUPERCONTENTS_SLIME)
731                         {
732                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
733                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
734                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
735                         }
736                         else
737                         {
738                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
739                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
740                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
741                         }
742                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
743                 }
744                 else
745                 {
746                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
747                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
748                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
749                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
750                 }
751
752                 if (gamemode != GAME_TRANSFUSION)
753                 {
754                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
755                         {
756                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
757                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
758                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
759                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
760                         }
761                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
762                         {
763                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
764                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
765                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
766                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
767                         }
768                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
769                         {
770                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
771                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
772                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
773                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
774                         }
775                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
776                         {
777                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
778                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
779                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
780                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
781                         }
782                         else
783                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
784                 }
785
786                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
787                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
788                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
789                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
790
791                 // LordHavoc: fixed V_CalcBlend
792                 for (j = 0;j < NUM_CSHIFTS;j++)
793                 {
794                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
795                         if (a2 > 0)
796                         {
797                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
798                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
799                         }
800                 }
801                 // saturate color (to avoid blending in black)
802                 if (r_refdef.viewblend[3])
803                 {
804                         a2 = 1 / r_refdef.viewblend[3];
805                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
806                 }
807                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
808                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
809                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
810                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
811                 
812                 // Samual: Ugly hack, I know. But it's the best we can do since
813                 // there is no way to detect client states from the engine.
814                 if (cl.stats[STAT_HEALTH] <= 0 && cl.stats[STAT_HEALTH] != -666 && 
815                         cl.stats[STAT_HEALTH] != -2342 && cl_deathfade.value > 0)
816                 {
817                         cl.deathfade += cl_deathfade.value * max(0.00001, cl.time - cl.oldtime);
818                         cl.deathfade = bound(0.0f, cl.deathfade, 0.9f);
819                 }
820                 else
821                         cl.deathfade = 0.0f;
822
823                 if(cl.deathfade > 0)
824                 {
825                         float a;
826                         float deathfadevec[3] = {0.3f, 0.0f, 0.0f};
827                         a = r_refdef.viewblend[3] + cl.deathfade - r_refdef.viewblend[3]*cl.deathfade;
828                         if(a > 0)
829                                 VectorMAM(r_refdef.viewblend[3] * (1 - cl.deathfade) / a, r_refdef.viewblend, cl.deathfade / a, deathfadevec, r_refdef.viewblend);
830                         r_refdef.viewblend[3] = a;
831                 }
832         }
833 }
834
835 //============================================================================
836
837 /*
838 =============
839 V_Init
840 =============
841 */
842 void V_Init (void)
843 {
844         Cmd_AddCommand ("v_cshift", V_cshift_f, "sets tint color of view");
845         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");
846         Cmd_AddCommand ("centerview", V_StartPitchDrift, "gradually recenter view (stop looking up/down)");
847
848         Cvar_RegisterVariable (&v_centermove);
849         Cvar_RegisterVariable (&v_centerspeed);
850
851         Cvar_RegisterVariable (&v_iyaw_cycle);
852         Cvar_RegisterVariable (&v_iroll_cycle);
853         Cvar_RegisterVariable (&v_ipitch_cycle);
854         Cvar_RegisterVariable (&v_iyaw_level);
855         Cvar_RegisterVariable (&v_iroll_level);
856         Cvar_RegisterVariable (&v_ipitch_level);
857
858         Cvar_RegisterVariable (&v_idlescale);
859         Cvar_RegisterVariable (&crosshair);
860
861         Cvar_RegisterVariable (&cl_rollspeed);
862         Cvar_RegisterVariable (&cl_rollangle);
863         Cvar_RegisterVariable (&cl_bob);
864         Cvar_RegisterVariable (&cl_bobcycle);
865         Cvar_RegisterVariable (&cl_bobup);
866         Cvar_RegisterVariable (&cl_bobmodel);
867         Cvar_RegisterVariable (&cl_bobmodel_side);
868         Cvar_RegisterVariable (&cl_bobmodel_up);
869         Cvar_RegisterVariable (&cl_bobmodel_speed);
870
871         Cvar_RegisterVariable (&cl_leanmodel_side);
872         Cvar_RegisterVariable (&cl_leanmodel_side_speed);
873         Cvar_RegisterVariable (&cl_leanmodel_side_limit);
874         Cvar_RegisterVariable (&cl_leanmodel_up);
875         Cvar_RegisterVariable (&cl_leanmodel_up_speed);
876         Cvar_RegisterVariable (&cl_leanmodel_up_limit);
877
878         Cvar_RegisterVariable (&cl_viewmodel_scale);
879
880         Cvar_RegisterVariable (&v_kicktime);
881         Cvar_RegisterVariable (&v_kickroll);
882         Cvar_RegisterVariable (&v_kickpitch);
883
884         Cvar_RegisterVariable (&cl_stairsmoothspeed);
885
886         Cvar_RegisterVariable (&chase_back);
887         Cvar_RegisterVariable (&chase_up);
888         Cvar_RegisterVariable (&chase_active);
889         Cvar_RegisterVariable (&chase_overhead);
890         Cvar_RegisterVariable (&chase_pitchangle);
891         if (gamemode == GAME_GOODVSBAD2)
892                 Cvar_RegisterVariable (&chase_stevie);
893
894         Cvar_RegisterVariable (&v_deathtilt);
895         Cvar_RegisterVariable (&v_deathtiltangle);
896 }
897