2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // view.c -- player eye positioning
23 #include "cl_collision.h"
27 The view is allowed to move slightly from it's true position for bobbing,
28 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
29 entities sent from the server may not include everything in the pvs, especially
30 when crossing a water boudnary.
34 cvar_t cl_rollspeed = {0, "cl_rollspeed", "200"};
35 cvar_t cl_rollangle = {0, "cl_rollangle", "2.0"};
37 cvar_t cl_bob = {0, "cl_bob","0.02"};
38 cvar_t cl_bobcycle = {0, "cl_bobcycle","0.6"};
39 cvar_t cl_bobup = {0, "cl_bobup","0.5"};
41 cvar_t v_kicktime = {0, "v_kicktime", "0.5"};
42 cvar_t v_kickroll = {0, "v_kickroll", "0.6"};
43 cvar_t v_kickpitch = {0, "v_kickpitch", "0.6"};
45 cvar_t v_iyaw_cycle = {0, "v_iyaw_cycle", "2"};
46 cvar_t v_iroll_cycle = {0, "v_iroll_cycle", "0.5"};
47 cvar_t v_ipitch_cycle = {0, "v_ipitch_cycle", "1"};
48 cvar_t v_iyaw_level = {0, "v_iyaw_level", "0.3"};
49 cvar_t v_iroll_level = {0, "v_iroll_level", "0.1"};
50 cvar_t v_ipitch_level = {0, "v_ipitch_level", "0.3"};
52 cvar_t v_idlescale = {0, "v_idlescale", "0"};
54 cvar_t crosshair = {CVAR_SAVE, "crosshair", "0"};
56 cvar_t v_centermove = {0, "v_centermove", "0.15"};
57 cvar_t v_centerspeed = {0, "v_centerspeed","500"};
59 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
66 Used by view and sv_user
69 float V_CalcRoll (vec3_t angles, vec3_t velocity)
76 AngleVectors (angles, NULL, right, NULL);
77 side = DotProduct (velocity, right);
78 sign = side < 0 ? -1 : 1;
81 value = cl_rollangle.value;
83 if (side < cl_rollspeed.value)
84 side = side * value / cl_rollspeed.value;
92 void V_StartPitchDrift (void)
94 if (cl.laststop == cl.time)
95 return; // something else is keeping it from drifting
97 if (cl.nodrift || !cl.pitchvel)
99 cl.pitchvel = v_centerspeed.value;
105 void V_StopPitchDrift (void)
107 cl.laststop = cl.time;
116 Moves the client pitch angle towards cl.idealpitch sent by the server.
118 If the user is adjusting pitch manually, either with lookup/lookdown,
119 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
121 Drifting is enabled when the center view key is hit, mlook is released and
122 lookspring is non 0, or when
125 void V_DriftPitch (void)
129 if (noclip_anglehack || !cl.onground || cls.demoplayback )
136 // don't count small mouse motion
139 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
142 cl.driftmove += cl.frametime;
144 if ( cl.driftmove > v_centermove.value)
146 V_StartPitchDrift ();
151 delta = cl.idealpitch - cl.viewangles[PITCH];
159 move = cl.frametime * cl.pitchvel;
160 cl.pitchvel += cl.frametime * v_centerspeed.value;
169 cl.viewangles[PITCH] += move;
178 cl.viewangles[PITCH] -= move;
184 ==============================================================================
188 ==============================================================================
197 void V_ParseDamage (void)
201 //vec3_t forward, right;
207 armor = MSG_ReadByte ();
208 blood = MSG_ReadByte ();
209 for (i=0 ; i<3 ; i++)
210 from[i] = MSG_ReadCoord ();
212 count = blood*0.5 + armor*0.5;
216 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
218 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
219 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
220 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
221 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
222 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
226 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
227 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
228 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
232 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
233 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
234 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
238 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
239 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
240 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
243 // calculate view angle kicks
244 if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
246 ent = &cl_entities[cl.viewentity];
247 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
248 VectorNormalize(localfrom);
249 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
250 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
251 v_dmg_time = v_kicktime.value;
255 static cshift_t v_cshift;
262 static void V_cshift_f (void)
264 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
265 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
266 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
267 v_cshift.percent = atoi(Cmd_Argv(4));
275 When you run over an item, the server sends this command
278 static void V_BonusFlash_f (void)
280 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
281 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
282 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
283 cl.cshifts[CSHIFT_BONUS].percent = 50;
287 ==============================================================================
291 ==============================================================================
294 extern matrix4x4_t viewmodelmatrix;
302 void V_CalcRefdef (void)
305 if (cls.state == ca_connected && cls.signon == SIGNONS)
307 // ent is the player model (visible when out of body)
308 ent = &cl_entities[cl.viewentity];
311 // entity is a fixed camera
312 VectorCopy(ent->render.origin, r_refdef.vieworg);
313 VectorCopy(ent->render.angles, r_refdef.viewangles);
315 else if (chase_active.value)
317 // observing entity from third person
318 VectorCopy(ent->render.origin, r_refdef.vieworg);
319 VectorCopy(cl.viewangles, r_refdef.viewangles);
324 // first person view from entity
325 VectorCopy(ent->render.origin, r_refdef.vieworg);
326 VectorCopy(cl.viewangles, r_refdef.viewangles);
328 if (cl.stats[STAT_HEALTH] <= 0)
329 r_refdef.viewangles[ROLL] = 80; // dead view angle
330 VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
331 r_refdef.viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
334 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
335 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
336 v_dmg_time -= cl.frametime;
338 if (v_idlescale.value)
340 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
341 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
342 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
345 VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
346 r_refdef.vieworg[2] += cl.viewheight;
347 if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
350 // LordHavoc: this code is *weird*, but not replacable (I think it
351 // should be done in QC on the server, but oh well, quake is quake)
352 // LordHavoc: figured out bobup: the time at which the sin is at 180
353 // degrees (which allows lengthening or squishing the peak or valley)
354 cycle = cl.time / cl_bobcycle.value;
355 cycle -= (int) cycle;
356 if (cycle < cl_bobup.value)
357 cycle = sin(M_PI * cycle / cl_bobup.value);
359 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
360 // bob is proportional to velocity in the xy plane
361 // (don't count Z, or jumping messes it up)
362 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
363 bob = bob*0.3 + bob*0.7*cycle;
364 r_refdef.vieworg[2] += bound(-7, bob, 4);
367 // calculate a viewmodel matrix for use in view-attached entities
368 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value, r_refdef.viewangles[1] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value, r_refdef.viewangles[2] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 0.3);
371 Matrix4x4_CreateIdentity(&viewmodelmatrix);
374 void V_FadeViewFlashs(void)
376 // drop the damage value
377 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
378 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
379 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
380 // drop the bonus value
381 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
382 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
383 cl.cshifts[CSHIFT_BONUS].percent = 0;
386 void V_CalcViewBlend(void)
390 r_refdef.viewblend[0] = 0;
391 r_refdef.viewblend[1] = 0;
392 r_refdef.viewblend[2] = 0;
393 r_refdef.viewblend[3] = 0;
394 if (cls.state == ca_connected && cls.signon == SIGNONS)
396 // set contents color
397 switch (CL_PointContents(r_refdef.vieworg))
401 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
402 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
403 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
404 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
407 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
408 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
409 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
410 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
413 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
414 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
415 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
416 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
419 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
420 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
421 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
422 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
425 if (cl.items & IT_QUAD)
427 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
428 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
429 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
430 cl.cshifts[CSHIFT_POWERUP].percent = 30;
432 else if (cl.items & IT_SUIT)
434 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
435 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
436 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
437 cl.cshifts[CSHIFT_POWERUP].percent = 20;
439 else if (cl.items & IT_INVISIBILITY)
441 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
442 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
443 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
444 cl.cshifts[CSHIFT_POWERUP].percent = 100;
446 else if (cl.items & IT_INVULNERABILITY)
448 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
449 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
450 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
451 cl.cshifts[CSHIFT_POWERUP].percent = 30;
454 cl.cshifts[CSHIFT_POWERUP].percent = 0;
456 // LordHavoc: fixed V_CalcBlend
457 for (j = 0;j < NUM_CSHIFTS;j++)
459 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
462 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
463 r_refdef.viewblend[3] = 1 - (1 - r_refdef.viewblend[3]) * (1 - a2); // correct alpha multiply... took a while to find it on the web
466 // saturate color (to avoid blending in black)
467 if (r_refdef.viewblend[3])
469 a2 = 1 / r_refdef.viewblend[3];
470 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
473 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
474 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
475 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
476 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] , 1.0f);
480 //============================================================================
489 Cmd_AddCommand ("v_cshift", V_cshift_f);
490 Cmd_AddCommand ("bf", V_BonusFlash_f);
491 Cmd_AddCommand ("centerview", V_StartPitchDrift);
493 Cvar_RegisterVariable (&v_centermove);
494 Cvar_RegisterVariable (&v_centerspeed);
496 Cvar_RegisterVariable (&v_iyaw_cycle);
497 Cvar_RegisterVariable (&v_iroll_cycle);
498 Cvar_RegisterVariable (&v_ipitch_cycle);
499 Cvar_RegisterVariable (&v_iyaw_level);
500 Cvar_RegisterVariable (&v_iroll_level);
501 Cvar_RegisterVariable (&v_ipitch_level);
503 Cvar_RegisterVariable (&v_idlescale);
504 Cvar_RegisterVariable (&crosshair);
506 Cvar_RegisterVariable (&cl_rollspeed);
507 Cvar_RegisterVariable (&cl_rollangle);
508 Cvar_RegisterVariable (&cl_bob);
509 Cvar_RegisterVariable (&cl_bobcycle);
510 Cvar_RegisterVariable (&cl_bobup);
512 Cvar_RegisterVariable (&v_kicktime);
513 Cvar_RegisterVariable (&v_kickroll);
514 Cvar_RegisterVariable (&v_kickpitch);