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_bob.value && cl_bobcycle.value)
355 // LordHavoc: this code is *weird*, but not replacable (I think it
356 // should be done in QC on the server, but oh well, quake is quake)
357 // LordHavoc: figured out bobup: the time at which the sin is at 180
358 // degrees (which allows lengthening or squishing the peak or valley)
359 cycle = cl.time / cl_bobcycle.value;
360 cycle -= (int) cycle;
361 if (cycle < cl_bobup.value)
362 cycle = sin(M_PI * cycle / cl_bobup.value);
364 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
365 // bob is proportional to velocity in the xy plane
366 // (don't count Z, or jumping messes it up)
367 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
368 bob = bob*0.3 + bob*0.7*cycle;
369 r_refdef.vieworg[2] += bound(-7, bob, 4);
371 // link the delayed viewmodel entities
372 if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY) && cl.stats[STAT_HEALTH] > 0)
376 matrix4x4_t matrix, matrix2;
377 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);
378 for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
381 r_refdef.entities[r_refdef.numentities++] = &ent->render;
382 matrix2 = ent->render.matrix;
383 Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
384 Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
385 CL_BoundingBoxForEntity(&ent->render);
390 // drop the damage value
391 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
392 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
393 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
395 // drop the bonus value
396 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
397 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
398 cl.cshifts[CSHIFT_BONUS].percent = 0;
400 // set contents color
401 switch (CL_PointContents(r_refdef.vieworg))
405 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
406 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
407 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
408 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
411 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
412 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
413 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
414 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
417 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
418 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
419 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
420 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
423 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
424 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
425 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
426 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
429 if (cl.items & IT_QUAD)
431 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
432 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
433 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
434 cl.cshifts[CSHIFT_POWERUP].percent = 30;
436 else if (cl.items & IT_SUIT)
438 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
439 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
440 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
441 cl.cshifts[CSHIFT_POWERUP].percent = 20;
443 else if (cl.items & IT_INVISIBILITY)
445 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
446 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
447 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
448 cl.cshifts[CSHIFT_POWERUP].percent = 100;
450 else if (cl.items & IT_INVULNERABILITY)
452 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
453 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
454 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
455 cl.cshifts[CSHIFT_POWERUP].percent = 30;
458 cl.cshifts[CSHIFT_POWERUP].percent = 0;
460 // LordHavoc: fixed V_CalcBlend
466 for (j=0 ; j<NUM_CSHIFTS ; j++)
468 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
474 r += (cl.cshifts[j].destcolor[0]-r) * a2;
475 g += (cl.cshifts[j].destcolor[1]-g) * a2;
476 b += (cl.cshifts[j].destcolor[2]-b) * a2;
477 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply... took a while to find it on the web
479 // saturate color (to avoid blending in black)
488 r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
489 r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
490 r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
491 r_refdef.viewblend[3] = bound(0, a , 1);
495 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
496 cl.cshifts[CSHIFT_BONUS].percent = 0;
497 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
498 cl.cshifts[CSHIFT_POWERUP].percent = 0;
499 r_refdef.viewblend[0] = 0;
500 r_refdef.viewblend[1] = 0;
501 r_refdef.viewblend[2] = 0;
502 r_refdef.viewblend[3] = 0;
506 //============================================================================
515 Cmd_AddCommand ("v_cshift", V_cshift_f);
516 Cmd_AddCommand ("bf", V_BonusFlash_f);
517 Cmd_AddCommand ("centerview", V_StartPitchDrift);
519 Cvar_RegisterVariable (&v_centermove);
520 Cvar_RegisterVariable (&v_centerspeed);
522 Cvar_RegisterVariable (&v_iyaw_cycle);
523 Cvar_RegisterVariable (&v_iroll_cycle);
524 Cvar_RegisterVariable (&v_ipitch_cycle);
525 Cvar_RegisterVariable (&v_iyaw_level);
526 Cvar_RegisterVariable (&v_iroll_level);
527 Cvar_RegisterVariable (&v_ipitch_level);
529 Cvar_RegisterVariable (&v_idlescale);
530 Cvar_RegisterVariable (&crosshair);
532 Cvar_RegisterVariable (&cl_rollspeed);
533 Cvar_RegisterVariable (&cl_rollangle);
534 Cvar_RegisterVariable (&cl_bob);
535 Cvar_RegisterVariable (&cl_bobcycle);
536 Cvar_RegisterVariable (&cl_bobup);
538 Cvar_RegisterVariable (&v_kicktime);
539 Cvar_RegisterVariable (&v_kickroll);
540 Cvar_RegisterVariable (&v_kickpitch);