]> icculus.org git repositories - divverent/darkplaces.git/blob - view.c
use strlcpy() instead of strcpy()
[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_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"};
49
50 cvar_t v_kicktime = {0, "v_kicktime", "0.5", "how long a view kick from damage lasts"};
51 cvar_t v_kickroll = {0, "v_kickroll", "0.6", "how much a view kick from damage rolls your view"};
52 cvar_t v_kickpitch = {0, "v_kickpitch", "0.6", "how much a view kick from damage pitches your view"};
53
54 cvar_t v_iyaw_cycle = {0, "v_iyaw_cycle", "2", "v_idlescale yaw speed"};
55 cvar_t v_iroll_cycle = {0, "v_iroll_cycle", "0.5", "v_idlescale roll speed"};
56 cvar_t v_ipitch_cycle = {0, "v_ipitch_cycle", "1", "v_idlescale pitch speed"};
57 cvar_t v_iyaw_level = {0, "v_iyaw_level", "0.3", "v_idlescale yaw amount"};
58 cvar_t v_iroll_level = {0, "v_iroll_level", "0.1", "v_idlescale roll amount"};
59 cvar_t v_ipitch_level = {0, "v_ipitch_level", "0.3", "v_idlescale pitch amount"};
60
61 cvar_t v_idlescale = {0, "v_idlescale", "0", "how much of the quake 'drunken view' effect to use"};
62
63 cvar_t crosshair = {CVAR_SAVE, "crosshair", "0", "selects crosshair to use (0 is none)"};
64
65 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)"};
66 cvar_t v_centerspeed = {0, "v_centerspeed","500", "how fast the view centers itself"};
67
68 cvar_t cl_stairsmoothspeed = {CVAR_SAVE, "cl_stairsmoothspeed", "160", "how fast your view moves upward/downward when running up/down stairs"};
69
70 cvar_t chase_back = {CVAR_SAVE, "chase_back", "48", "chase cam distance from the player"};
71 cvar_t chase_up = {CVAR_SAVE, "chase_up", "24", "chase cam distance from the player"};
72 cvar_t chase_active = {CVAR_SAVE, "chase_active", "0", "enables chase cam"};
73 cvar_t chase_overhead = {CVAR_SAVE, "chase_overhead", "0", "chase cam looks straight down if this is not zero"};
74 // GAME_GOODVSBAD2
75 cvar_t chase_stevie = {0, "chase_stevie", "0", "chase cam view from above (used only by GoodVsBad2)"};
76
77 cvar_t v_deathtilt = {0, "v_deathtilt", "1", "whether to use sideways view when dead"};
78 cvar_t v_deathtiltangle = {0, "v_deathtiltangle", "80", "what roll angle to use when tilting the view while dead"};
79
80 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
81
82
83 /*
84 ===============
85 V_CalcRoll
86
87 Used by view and sv_user
88 ===============
89 */
90 float V_CalcRoll (vec3_t angles, vec3_t velocity)
91 {
92         vec3_t  right;
93         float   sign;
94         float   side;
95         float   value;
96
97         AngleVectors (angles, NULL, right, NULL);
98         side = DotProduct (velocity, right);
99         sign = side < 0 ? -1 : 1;
100         side = fabs(side);
101
102         value = cl_rollangle.value;
103
104         if (side < cl_rollspeed.value)
105                 side = side * value / cl_rollspeed.value;
106         else
107                 side = value;
108
109         return side*sign;
110
111 }
112
113 void V_StartPitchDrift (void)
114 {
115         if (cl.laststop == cl.time)
116                 return;         // something else is keeping it from drifting
117
118         if (cl.nodrift || !cl.pitchvel)
119         {
120                 cl.pitchvel = v_centerspeed.value;
121                 cl.nodrift = false;
122                 cl.driftmove = 0;
123         }
124 }
125
126 void V_StopPitchDrift (void)
127 {
128         cl.laststop = cl.time;
129         cl.nodrift = true;
130         cl.pitchvel = 0;
131 }
132
133 /*
134 ===============
135 V_DriftPitch
136
137 Moves the client pitch angle towards cl.idealpitch sent by the server.
138
139 If the user is adjusting pitch manually, either with lookup/lookdown,
140 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
141
142 Drifting is enabled when the center view key is hit, mlook is released and
143 lookspring is non 0, or when
144 ===============
145 */
146 void V_DriftPitch (void)
147 {
148         float           delta, move;
149
150         if (noclip_anglehack || !cl.onground || cls.demoplayback )
151         {
152                 cl.driftmove = 0;
153                 cl.pitchvel = 0;
154                 return;
155         }
156
157 // don't count small mouse motion
158         if (cl.nodrift)
159         {
160                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
161                         cl.driftmove = 0;
162                 else
163                         cl.driftmove += cl.realframetime;
164
165                 if ( cl.driftmove > v_centermove.value)
166                 {
167                         V_StartPitchDrift ();
168                 }
169                 return;
170         }
171
172         delta = cl.idealpitch - cl.viewangles[PITCH];
173
174         if (!delta)
175         {
176                 cl.pitchvel = 0;
177                 return;
178         }
179
180         move = cl.realframetime * cl.pitchvel;
181         cl.pitchvel += cl.realframetime * v_centerspeed.value;
182
183         if (delta > 0)
184         {
185                 if (move > delta)
186                 {
187                         cl.pitchvel = 0;
188                         move = delta;
189                 }
190                 cl.viewangles[PITCH] += move;
191         }
192         else if (delta < 0)
193         {
194                 if (move > -delta)
195                 {
196                         cl.pitchvel = 0;
197                         move = -delta;
198                 }
199                 cl.viewangles[PITCH] -= move;
200         }
201 }
202
203
204 /*
205 ==============================================================================
206
207                                                 SCREEN FLASHES
208
209 ==============================================================================
210 */
211
212
213 /*
214 ===============
215 V_ParseDamage
216 ===============
217 */
218 void V_ParseDamage (void)
219 {
220         int armor, blood;
221         vec3_t from;
222         //vec3_t forward, right;
223         vec3_t localfrom;
224         entity_t *ent;
225         //float side;
226         float count;
227
228         armor = MSG_ReadByte ();
229         blood = MSG_ReadByte ();
230         MSG_ReadVector(from, cls.protocol);
231
232         // Send the Dmg Globals to CSQC
233         CL_VM_UpdateDmgGlobals(blood, armor, from);
234
235         count = blood*0.5 + armor*0.5;
236         if (count < 10)
237                 count = 10;
238
239         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
240
241         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
242         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
243                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
244         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
245                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
246
247         if (armor > blood)
248         {
249                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
250                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
251                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
252         }
253         else if (armor)
254         {
255                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
256                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
257                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
258         }
259         else
260         {
261                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
262                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
263                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
264         }
265
266         // calculate view angle kicks
267         if (cl.entities[cl.viewentity].state_current.active)
268         {
269                 ent = &cl.entities[cl.viewentity];
270                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
271                 VectorNormalize(localfrom);
272                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
273                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
274                 v_dmg_time = v_kicktime.value;
275         }
276 }
277
278 static cshift_t v_cshift;
279
280 /*
281 ==================
282 V_cshift_f
283 ==================
284 */
285 static void V_cshift_f (void)
286 {
287         v_cshift.destcolor[0] = atof(Cmd_Argv(1));
288         v_cshift.destcolor[1] = atof(Cmd_Argv(2));
289         v_cshift.destcolor[2] = atof(Cmd_Argv(3));
290         v_cshift.percent = atof(Cmd_Argv(4));
291 }
292
293
294 /*
295 ==================
296 V_BonusFlash_f
297
298 When you run over an item, the server sends this command
299 ==================
300 */
301 static void V_BonusFlash_f (void)
302 {
303         cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
304         cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
305         cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
306         cl.cshifts[CSHIFT_BONUS].percent = 50;
307 }
308
309 /*
310 ==============================================================================
311
312                                                 VIEW RENDERING
313
314 ==============================================================================
315 */
316
317 extern matrix4x4_t viewmodelmatrix;
318
319 #include "cl_collision.h"
320 #include "csprogs.h"
321
322 /*
323 ==================
324 V_CalcRefdef
325
326 ==================
327 */
328 #if 0
329 static vec3_t eyeboxmins = {-16, -16, -24};
330 static vec3_t eyeboxmaxs = { 16,  16,  32};
331 #endif
332 void V_CalcRefdef (void)
333 {
334         entity_t *ent;
335         float vieworg[3], gunorg[3], viewangles[3], smoothtime;
336         trace_t trace;
337         VectorClear(gunorg);
338         viewmodelmatrix = identitymatrix;
339         r_view.matrix = identitymatrix;
340         if (cls.state == ca_connected && cls.signon == SIGNONS)
341         {
342                 // ent is the view entity (visible when out of body)
343                 ent = &cl.entities[cl.viewentity];
344                 // player can look around, so take the origin from the entity,
345                 // and the angles from the input system
346                 Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
347                 VectorCopy(cl.viewangles, viewangles);
348
349                 // calculate how much time has passed since the last V_CalcRefdef
350                 smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
351                 cl.stairsmoothtime = cl.time;
352
353                 // fade damage flash
354                 if (v_dmg_time > 0)
355                         v_dmg_time -= bound(0, smoothtime, 0.1);
356
357                 if (cl.intermission)
358                 {
359                         // entity is a fixed camera, just copy the matrix
360                         if (cls.protocol == PROTOCOL_QUAKEWORLD)
361                                 Matrix4x4_CreateFromQuakeEntity(&r_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);
362                         else
363                         {
364                                 r_view.matrix = ent->render.matrix;
365                                 Matrix4x4_AdjustOrigin(&r_view.matrix, 0, 0, cl.stats[STAT_VIEWHEIGHT]);
366                         }
367                         viewmodelmatrix = r_view.matrix;
368                 }
369                 else
370                 {
371                         // smooth stair stepping, but only if onground and enabled
372                         if (!cl.onground || cl_stairsmoothspeed.value <= 0)
373                                 cl.stairsmoothz = vieworg[2];
374                         else
375                         {
376                                 if (cl.stairsmoothz < vieworg[2])
377                                         vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - 16, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
378                                 else if (cl.stairsmoothz > vieworg[2])
379                                         vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + 16);
380                         }
381
382                         // apply qw weapon recoil effect (this did not work in QW)
383                         // TODO: add a cvar to disable this
384                         viewangles[PITCH] += cl.qw_weaponkick;
385
386                         if (chase_active.value)
387                         {
388                                 // observing entity from third person
389                                 vec_t camback, camup, dist, forward[3], chase_dest[3];
390
391                                 camback = chase_back.value;
392                                 camup = chase_up.value;
393
394                                 // this + 22 is to match view_ofs for compatibility with older versions
395                                 camup += 22;
396
397                                 AngleVectors(viewangles, forward, NULL, NULL);
398
399                                 if (chase_overhead.integer)
400                                 {
401 #if 1
402                                         vec3_t offset;
403                                         vec3_t bestvieworg;
404 #endif
405                                         vec3_t up;
406                                         viewangles[PITCH] = 0;
407                                         AngleVectors(viewangles, forward, NULL, up);
408                                         // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
409                                         chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
410                                         chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
411                                         chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
412 #if 0
413                                         //trace = CL_Move(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
414                                         trace = CL_Move(vieworg, vec3_origin, vec3_origin, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
415                                         VectorCopy(trace.endpos, vieworg);
416                                         vieworg[2] -= 8;
417 #else
418                                         trace = CL_Move(vieworg, vec3_origin, vec3_origin, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
419                                         VectorCopy(trace.endpos, bestvieworg);
420                                         offset[2] = 0;
421                                         for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
422                                         {
423                                                 for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
424                                                 {
425                                                         AngleVectors(viewangles, NULL, NULL, up);
426                                                         chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
427                                                         chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
428                                                         chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
429                                                         trace = CL_Move(vieworg, vec3_origin, vec3_origin, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
430                                                         if (bestvieworg[2] > trace.endpos[2])
431                                                                 bestvieworg[2] = trace.endpos[2];
432                                                 }
433                                         }
434                                         bestvieworg[2] -= 8;
435                                         VectorCopy(bestvieworg, vieworg);
436 #endif
437                                         viewangles[PITCH] = 90;
438                                 }
439                                 else
440                                 {
441                                         if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
442                                         {
443                                                 // look straight down from high above
444                                                 viewangles[PITCH] = 90;
445                                                 camback = 2048;
446                                                 VectorSet(forward, 0, 0, -1);
447                                         }
448
449                                         // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
450                                         dist = -camback - 8;
451                                         chase_dest[0] = vieworg[0] + forward[0] * dist;
452                                         chase_dest[1] = vieworg[1] + forward[1] * dist;
453                                         chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
454                                         trace = CL_Move(vieworg, vec3_origin, vec3_origin, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
455                                         VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
456                                 }
457                         }
458                         else
459                         {
460                                 // first person view from entity
461                                 // angles
462                                 if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
463                                         viewangles[ROLL] = v_deathtiltangle.value;
464                                 VectorAdd(viewangles, cl.punchangle, viewangles);
465                                 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.movement_velocity);
466                                 if (v_dmg_time > 0)
467                                 {
468                                         viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
469                                         viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
470                                 }
471                                 // origin
472                                 VectorAdd(vieworg, cl.punchvector, vieworg);
473                                 vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
474                                 if (cl.stats[STAT_HEALTH] > 0)
475                                 {
476                                         double xyspeed, bob;
477
478                                         xyspeed = sqrt(cl.movement_velocity[0]*cl.movement_velocity[0] + cl.movement_velocity[1]*cl.movement_velocity[1]);
479                                         if (cl_bob.value && cl_bobcycle.value)
480                                         {
481                                                 float cycle;
482                                                 // LordHavoc: this code is *weird*, but not replacable (I think it
483                                                 // should be done in QC on the server, but oh well, quake is quake)
484                                                 // LordHavoc: figured out bobup: the time at which the sin is at 180
485                                                 // degrees (which allows lengthening or squishing the peak or valley)
486                                                 cycle = cl.time / cl_bobcycle.value;
487                                                 cycle -= (int) cycle;
488                                                 if (cycle < cl_bobup.value)
489                                                         cycle = sin(M_PI * cycle / cl_bobup.value);
490                                                 else
491                                                         cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
492                                                 // bob is proportional to velocity in the xy plane
493                                                 // (don't count Z, or jumping messes it up)
494                                                 bob = xyspeed * cl_bob.value;
495                                                 bob = bob*0.3 + bob*0.7*cycle;
496                                                 vieworg[2] += bound(-7, bob, 4);
497                                         }
498
499                                         VectorCopy(vieworg, gunorg);
500
501                                         if (cl_bob.value && cl_bobmodel.value)
502                                         {
503                                                 // calculate for swinging gun model
504                                                 // the gun bobs when running on the ground, but doesn't bob when you're in the air.
505                                                 // Sajt: I tried to smooth out the transitions between bob and no bob, which works
506                                                 // for the most part, but for some reason when you go through a message trigger or
507                                                 // pick up an item or anything like that it will momentarily jolt the gun.
508                                                 vec3_t forward, right, up;
509                                                 float bspeed;
510                                                 float s;
511                                                 float t;
512
513                                                 s = cl.time * cl_bobmodel_speed.value;
514                                                 if (cl.onground)
515                                                 {
516                                                         if (cl.time - cl.hitgroundtime < 0.2)
517                                                         {
518                                                                 // just hit the ground, speed the bob back up over the next 0.2 seconds
519                                                                 t = cl.time - cl.hitgroundtime;
520                                                                 t = bound(0, t, 0.2);
521                                                                 t *= 5;
522                                                         }
523                                                         else
524                                                                 t = 1;
525                                                 }
526                                                 else
527                                                 {
528                                                         // recently left the ground, slow the bob down over the next 0.2 seconds
529                                                         t = cl.time - cl.lastongroundtime;
530                                                         t = 0.2 - bound(0, t, 0.2);
531                                                         t *= 5;
532                                                 }
533
534                                                 bspeed = bound (0, xyspeed, 400) * 0.01f;
535                                                 AngleVectors (viewangles, forward, right, up);
536                                                 bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
537                                                 VectorMA (gunorg, bob, right, gunorg);
538                                                 bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
539                                                 VectorMA (gunorg, bob, up, gunorg);
540                                         }
541                                 }
542                         }
543                         // calculate a view matrix for rendering the scene
544                         if (v_idlescale.value)
545                                 Matrix4x4_CreateFromQuakeEntity(&r_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);
546                         else
547                                 Matrix4x4_CreateFromQuakeEntity(&r_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);
548                         // calculate a viewmodel matrix for use in view-attached entities
549                         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], viewangles[0], viewangles[1], viewangles[2], cl_viewmodel_scale.value);
550                         VectorCopy(vieworg, cl.csqc_origin);
551                         VectorCopy(viewangles, cl.csqc_angles);
552                         Matrix4x4_Invert_Simple(&r_view.inverse_matrix, &r_view.matrix);
553                 }
554         }
555 }
556
557 void V_FadeViewFlashs(void)
558 {
559         // don't flash if time steps backwards
560         if (cl.time <= cl.oldtime)
561                 return;
562         // drop the damage value
563         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
564         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
565                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
566         // drop the bonus value
567         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
568         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
569                 cl.cshifts[CSHIFT_BONUS].percent = 0;
570 }
571
572 void V_CalcViewBlend(void)
573 {
574         float a2;
575         int j;
576         r_refdef.viewblend[0] = 0;
577         r_refdef.viewblend[1] = 0;
578         r_refdef.viewblend[2] = 0;
579         r_refdef.viewblend[3] = 0;
580         r_refdef.frustumscale_x = 1;
581         r_refdef.frustumscale_y = 1;
582         if (cls.state == ca_connected && cls.signon == SIGNONS)
583         {
584                 // set contents color
585                 int supercontents;
586                 vec3_t vieworigin;
587                 Matrix4x4_OriginFromMatrix(&r_view.matrix, vieworigin);
588                 supercontents = CL_PointSuperContents(vieworigin);
589                 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
590                 {
591                         r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
592                         r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
593                         if (supercontents & SUPERCONTENTS_LAVA)
594                         {
595                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
596                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
597                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
598                         }
599                         else if (supercontents & SUPERCONTENTS_SLIME)
600                         {
601                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
602                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
603                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
604                         }
605                         else
606                         {
607                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
608                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
609                                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
610                         }
611                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 * 0.5;
612                 }
613                 else
614                 {
615                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
616                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
617                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
618                         cl.cshifts[CSHIFT_CONTENTS].percent = 0;
619                 }
620
621                 if (gamemode != GAME_TRANSFUSION)
622                 {
623                         if (cl.stats[STAT_ITEMS] & IT_QUAD)
624                         {
625                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
626                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
627                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
628                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
629                         }
630                         else if (cl.stats[STAT_ITEMS] & IT_SUIT)
631                         {
632                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
633                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
634                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
635                                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
636                         }
637                         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
638                         {
639                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
640                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
641                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
642                                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
643                         }
644                         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
645                         {
646                                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
647                                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
648                                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
649                                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
650                         }
651                         else
652                                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
653                 }
654
655                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
656                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
657                 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
658                 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
659
660                 // LordHavoc: fixed V_CalcBlend
661                 for (j = 0;j < NUM_CSHIFTS;j++)
662                 {
663                         a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
664                         if (a2 > 0)
665                         {
666                                 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
667                                 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply...  took a while to find it on the web
668                         }
669                 }
670                 // saturate color (to avoid blending in black)
671                 if (r_refdef.viewblend[3])
672                 {
673                         a2 = 1 / r_refdef.viewblend[3];
674                         VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
675                 }
676
677                 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
678                 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
679                 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
680                 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
681         }
682 }
683
684 //============================================================================
685
686 /*
687 =============
688 V_Init
689 =============
690 */
691 void V_Init (void)
692 {
693         Cmd_AddCommand ("v_cshift", V_cshift_f, "sets tint color of view");
694         Cmd_AddCommand ("bf", V_BonusFlash_f, "briefly flashes a bright color tint on view (used when items are picked up)");
695         Cmd_AddCommand ("centerview", V_StartPitchDrift, "gradually recenter view (stop looking up/down)");
696
697         Cvar_RegisterVariable (&v_centermove);
698         Cvar_RegisterVariable (&v_centerspeed);
699
700         Cvar_RegisterVariable (&v_iyaw_cycle);
701         Cvar_RegisterVariable (&v_iroll_cycle);
702         Cvar_RegisterVariable (&v_ipitch_cycle);
703         Cvar_RegisterVariable (&v_iyaw_level);
704         Cvar_RegisterVariable (&v_iroll_level);
705         Cvar_RegisterVariable (&v_ipitch_level);
706
707         Cvar_RegisterVariable (&v_idlescale);
708         Cvar_RegisterVariable (&crosshair);
709
710         Cvar_RegisterVariable (&cl_rollspeed);
711         Cvar_RegisterVariable (&cl_rollangle);
712         Cvar_RegisterVariable (&cl_bob);
713         Cvar_RegisterVariable (&cl_bobcycle);
714         Cvar_RegisterVariable (&cl_bobup);
715         Cvar_RegisterVariable (&cl_bobmodel);
716         Cvar_RegisterVariable (&cl_bobmodel_side);
717         Cvar_RegisterVariable (&cl_bobmodel_up);
718         Cvar_RegisterVariable (&cl_bobmodel_speed);
719
720         Cvar_RegisterVariable (&cl_viewmodel_scale);
721
722         Cvar_RegisterVariable (&v_kicktime);
723         Cvar_RegisterVariable (&v_kickroll);
724         Cvar_RegisterVariable (&v_kickpitch);
725
726         Cvar_RegisterVariable (&cl_stairsmoothspeed);
727
728         Cvar_RegisterVariable (&chase_back);
729         Cvar_RegisterVariable (&chase_up);
730         Cvar_RegisterVariable (&chase_active);
731         Cvar_RegisterVariable (&chase_overhead);
732         if (gamemode == GAME_GOODVSBAD2)
733                 Cvar_RegisterVariable (&chase_stevie);
734
735         Cvar_RegisterVariable (&v_deathtilt);
736         Cvar_RegisterVariable (&v_deathtiltangle);
737 }
738