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 static 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 #define MAXVIEWMODELS 32
295 extern int numviewmodels;
296 extern entity_t *viewmodels[MAXVIEWMODELS];
304 void V_CalcRefdef (void)
306 float r, g, b, a, a2;
309 if (cls.state == ca_connected && cls.signon == SIGNONS)
311 // ent is the player model (visible when out of body)
312 ent = &cl_entities[cl.viewentity];
316 // entity is a fixed camera
317 VectorCopy(ent->render.origin, r_refdef.vieworg);
318 VectorCopy(ent->render.angles, r_refdef.viewangles);
320 else if (chase_active.value)
322 // observing entity from third person
323 VectorCopy(ent->render.origin, r_refdef.vieworg);
324 VectorCopy(cl.viewangles, r_refdef.viewangles);
329 // first person view from entity
330 VectorCopy(ent->render.origin, r_refdef.vieworg);
331 VectorCopy(cl.viewangles, r_refdef.viewangles);
333 if (cl.stats[STAT_HEALTH] <= 0)
334 r_refdef.viewangles[ROLL] = 80; // dead view angle
335 VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
336 r_refdef.viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
339 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
340 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
341 v_dmg_time -= cl.frametime;
343 if (v_idlescale.value)
345 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
346 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
347 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
350 VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
351 r_refdef.vieworg[2] += cl.viewheight;
352 if (cl.stats[STAT_HEALTH] > 0)
354 if (cl_bob.value && cl_bobcycle.value)
357 // LordHavoc: this code is *weird*, but not replacable (I think it
358 // should be done in QC on the server, but oh well, quake is quake)
359 // LordHavoc: figured out bobup: the time at which the sin is at 180
360 // degrees (which allows lengthening or squishing the peak or valley)
361 cycle = cl.time / cl_bobcycle.value;
362 cycle -= (int) cycle;
363 if (cycle < cl_bobup.value)
364 cycle = sin(M_PI * cycle / cl_bobup.value);
366 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
367 // bob is proportional to velocity in the xy plane
368 // (don't count Z, or jumping messes it up)
369 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
370 bob = bob*0.3 + bob*0.7*cycle;
371 r_refdef.vieworg[2] += bound(-7, bob, 4);
373 // link the delayed viewmodel entities
374 if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY))
378 matrix4x4_t matrix, matrix2;
379 Matrix4x4_CreateFromQuakeEntity(&matrix, 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);
380 for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
383 r_refdef.entities[r_refdef.numentities++] = &ent->render;
384 matrix2 = ent->render.matrix;
385 Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
386 Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
387 CL_BoundingBoxForEntity(&ent->render);
393 // drop the damage value
394 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
395 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
396 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
398 // drop the bonus value
399 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
400 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
401 cl.cshifts[CSHIFT_BONUS].percent = 0;
403 // set contents color
404 switch (CL_PointContents(r_refdef.vieworg))
408 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
409 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
410 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
411 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
414 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
415 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
416 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
417 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
420 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
421 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
422 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
423 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
426 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
427 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
428 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
429 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
432 if (cl.items & IT_QUAD)
434 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
435 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
436 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
437 cl.cshifts[CSHIFT_POWERUP].percent = 30;
439 else if (cl.items & IT_SUIT)
441 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
442 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
443 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
444 cl.cshifts[CSHIFT_POWERUP].percent = 20;
446 else if (cl.items & IT_INVISIBILITY)
448 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
449 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
450 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
451 cl.cshifts[CSHIFT_POWERUP].percent = 100;
453 else if (cl.items & IT_INVULNERABILITY)
455 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
456 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
457 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
458 cl.cshifts[CSHIFT_POWERUP].percent = 30;
461 cl.cshifts[CSHIFT_POWERUP].percent = 0;
463 // LordHavoc: fixed V_CalcBlend
469 for (j=0 ; j<NUM_CSHIFTS ; j++)
471 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
477 r += (cl.cshifts[j].destcolor[0]-r) * a2;
478 g += (cl.cshifts[j].destcolor[1]-g) * a2;
479 b += (cl.cshifts[j].destcolor[2]-b) * a2;
480 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply... took a while to find it on the web
482 // saturate color (to avoid blending in black)
491 r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
492 r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
493 r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
494 r_refdef.viewblend[3] = bound(0, a , 1);
498 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
499 cl.cshifts[CSHIFT_BONUS].percent = 0;
500 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
501 cl.cshifts[CSHIFT_POWERUP].percent = 0;
502 r_refdef.viewblend[0] = 0;
503 r_refdef.viewblend[1] = 0;
504 r_refdef.viewblend[2] = 0;
505 r_refdef.viewblend[3] = 0;
509 //============================================================================
518 Cmd_AddCommand ("v_cshift", V_cshift_f);
519 Cmd_AddCommand ("bf", V_BonusFlash_f);
520 Cmd_AddCommand ("centerview", V_StartPitchDrift);
522 Cvar_RegisterVariable (&v_centermove);
523 Cvar_RegisterVariable (&v_centerspeed);
525 Cvar_RegisterVariable (&v_iyaw_cycle);
526 Cvar_RegisterVariable (&v_iroll_cycle);
527 Cvar_RegisterVariable (&v_ipitch_cycle);
528 Cvar_RegisterVariable (&v_iyaw_level);
529 Cvar_RegisterVariable (&v_iroll_level);
530 Cvar_RegisterVariable (&v_ipitch_level);
532 Cvar_RegisterVariable (&v_idlescale);
533 Cvar_RegisterVariable (&crosshair);
535 Cvar_RegisterVariable (&cl_rollspeed);
536 Cvar_RegisterVariable (&cl_rollangle);
537 Cvar_RegisterVariable (&cl_bob);
538 Cvar_RegisterVariable (&cl_bobcycle);
539 Cvar_RegisterVariable (&cl_bobup);
541 Cvar_RegisterVariable (&v_kicktime);
542 Cvar_RegisterVariable (&v_kickroll);
543 Cvar_RegisterVariable (&v_kickpitch);