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
26 The view is allowed to move slightly from it's true position for bobbing,
27 but if it exceeds 8 pixels linear distance (spherical, not box), the list of
28 entities sent from the server may not include everything in the pvs, especially
29 when crossing a water boudnary.
33 cvar_t cl_rollspeed = {0, "cl_rollspeed", "200"};
34 cvar_t cl_rollangle = {0, "cl_rollangle", "2.0"};
36 cvar_t cl_bob = {0, "cl_bob","0.02"};
37 cvar_t cl_bobcycle = {0, "cl_bobcycle","0.6"};
38 cvar_t cl_bobup = {0, "cl_bobup","0.5"};
40 cvar_t v_kicktime = {0, "v_kicktime", "0.5"};
41 cvar_t v_kickroll = {0, "v_kickroll", "0.6"};
42 cvar_t v_kickpitch = {0, "v_kickpitch", "0.6"};
44 cvar_t v_iyaw_cycle = {0, "v_iyaw_cycle", "2"};
45 cvar_t v_iroll_cycle = {0, "v_iroll_cycle", "0.5"};
46 cvar_t v_ipitch_cycle = {0, "v_ipitch_cycle", "1"};
47 cvar_t v_iyaw_level = {0, "v_iyaw_level", "0.3"};
48 cvar_t v_iroll_level = {0, "v_iroll_level", "0.1"};
49 cvar_t v_ipitch_level = {0, "v_ipitch_level", "0.3"};
51 cvar_t v_idlescale = {0, "v_idlescale", "0"};
53 cvar_t crosshair = {CVAR_SAVE, "crosshair", "0"};
55 cvar_t v_centermove = {0, "v_centermove", "0.15"};
56 cvar_t v_centerspeed = {0, "v_centerspeed","500"};
58 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
65 Used by view and sv_user
68 float V_CalcRoll (vec3_t angles, vec3_t velocity)
75 AngleVectors (angles, NULL, right, NULL);
76 side = DotProduct (velocity, right);
77 sign = side < 0 ? -1 : 1;
80 value = cl_rollangle.value;
82 if (side < cl_rollspeed.value)
83 side = side * value / cl_rollspeed.value;
91 void V_StartPitchDrift (void)
93 if (cl.laststop == cl.time)
94 return; // something else is keeping it from drifting
96 if (cl.nodrift || !cl.pitchvel)
98 cl.pitchvel = v_centerspeed.value;
104 void V_StopPitchDrift (void)
106 cl.laststop = cl.time;
115 Moves the client pitch angle towards cl.idealpitch sent by the server.
117 If the user is adjusting pitch manually, either with lookup/lookdown,
118 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
120 Drifting is enabled when the center view key is hit, mlook is released and
121 lookspring is non 0, or when
124 static void V_DriftPitch (void)
128 if (noclip_anglehack || !cl.onground || cls.demoplayback )
135 // don't count small mouse motion
138 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
141 cl.driftmove += cl.frametime;
143 if ( cl.driftmove > v_centermove.value)
145 V_StartPitchDrift ();
150 delta = cl.idealpitch - cl.viewangles[PITCH];
158 move = cl.frametime * cl.pitchvel;
159 cl.pitchvel += cl.frametime * v_centerspeed.value;
168 cl.viewangles[PITCH] += move;
177 cl.viewangles[PITCH] -= move;
183 ==============================================================================
187 ==============================================================================
196 void V_ParseDamage (void)
200 //vec3_t forward, right;
206 armor = MSG_ReadByte ();
207 blood = MSG_ReadByte ();
208 for (i=0 ; i<3 ; i++)
209 from[i] = MSG_ReadCoord ();
211 count = blood*0.5 + armor*0.5;
215 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
217 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
218 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
219 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
220 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
221 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
225 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
226 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
227 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
231 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
232 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
233 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
237 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
238 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
239 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
242 // calculate view angle kicks
243 if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
245 ent = &cl_entities[cl.viewentity];
246 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
247 VectorNormalize(localfrom);
248 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
249 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
250 v_dmg_time = v_kicktime.value;
254 static cshift_t v_cshift;
261 static void V_cshift_f (void)
263 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
264 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
265 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
266 v_cshift.percent = atoi(Cmd_Argv(4));
274 When you run over an item, the server sends this command
277 static void V_BonusFlash_f (void)
279 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
280 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
281 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
282 cl.cshifts[CSHIFT_BONUS].percent = 50;
286 ==============================================================================
290 ==============================================================================
293 #define MAXVIEWMODELS 32
294 extern int numviewmodels;
295 extern entity_t *viewmodels[MAXVIEWMODELS];
303 void V_CalcRefdef (void)
305 float r, g, b, a, a2;
308 if (cls.state == ca_connected && cls.signon == SIGNONS)
310 // ent is the player model (visible when out of body)
311 ent = &cl_entities[cl.viewentity];
315 // entity is a fixed camera
316 VectorCopy(ent->render.origin, r_refdef.vieworg);
317 VectorCopy(ent->render.angles, r_refdef.viewangles);
319 else if (chase_active.value)
321 // observing entity from third person
322 VectorCopy(ent->render.origin, r_refdef.vieworg);
323 VectorCopy(cl.viewangles, r_refdef.viewangles);
328 // first person view from entity
329 VectorCopy(ent->render.origin, r_refdef.vieworg);
330 VectorCopy(cl.viewangles, r_refdef.viewangles);
332 if (cl.stats[STAT_HEALTH] <= 0)
333 r_refdef.viewangles[ROLL] = 80; // dead view angle
334 VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
335 r_refdef.viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
338 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
339 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
340 v_dmg_time -= cl.frametime;
342 if (v_idlescale.value)
344 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
345 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
346 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
349 VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
350 r_refdef.vieworg[2] += cl.viewheight;
351 if (cl_bob.value && cl_bobcycle.value)
354 // LordHavoc: this code is *weird*, but not replacable (I think it
355 // should be done in QC on the server, but oh well, quake is quake)
356 // LordHavoc: figured out bobup: the time at which the sin is at 180
357 // degrees (which allows lengthening or squishing the peak or valley)
358 cycle = cl.time / cl_bobcycle.value;
359 cycle -= (int) cycle;
360 if (cycle < cl_bobup.value)
361 cycle = sin(M_PI * cycle / cl_bobup.value);
363 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
364 // bob is proportional to velocity in the xy plane
365 // (don't count Z, or jumping messes it up)
366 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
367 bob = bob*0.3 + bob*0.7*cycle;
368 r_refdef.vieworg[2] += bound(-7, bob, 4);
370 // link the delayed viewmodel entities
371 if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY) && cl.stats[STAT_HEALTH] > 0)
375 matrix4x4_t matrix, matrix2;
376 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);
377 for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
380 r_refdef.entities[r_refdef.numentities++] = &ent->render;
381 matrix2 = ent->render.matrix;
382 Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
383 Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
384 CL_BoundingBoxForEntity(&ent->render);
389 // drop the damage value
390 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
391 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
392 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
394 // drop the bonus value
395 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
396 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
397 cl.cshifts[CSHIFT_BONUS].percent = 0;
399 // set contents color
400 switch (cl.worldmodel ? cl.worldmodel->PointContents(cl.worldmodel, r_refdef.vieworg) : CONTENTS_EMPTY)
404 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
405 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
406 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
407 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
410 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
411 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
412 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
413 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
416 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
417 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
418 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
419 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
422 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
423 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
424 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
425 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
428 if (cl.items & IT_QUAD)
430 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
431 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
432 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
433 cl.cshifts[CSHIFT_POWERUP].percent = 30;
435 else if (cl.items & IT_SUIT)
437 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
438 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
439 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
440 cl.cshifts[CSHIFT_POWERUP].percent = 20;
442 else if (cl.items & IT_INVISIBILITY)
444 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
445 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
446 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
447 cl.cshifts[CSHIFT_POWERUP].percent = 100;
449 else if (cl.items & IT_INVULNERABILITY)
451 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
452 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
453 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
454 cl.cshifts[CSHIFT_POWERUP].percent = 30;
457 cl.cshifts[CSHIFT_POWERUP].percent = 0;
459 // LordHavoc: fixed V_CalcBlend
465 for (j=0 ; j<NUM_CSHIFTS ; j++)
467 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
473 r += (cl.cshifts[j].destcolor[0]-r) * a2;
474 g += (cl.cshifts[j].destcolor[1]-g) * a2;
475 b += (cl.cshifts[j].destcolor[2]-b) * a2;
476 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply... took a while to find it on the web
478 // saturate color (to avoid blending in black)
487 r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
488 r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
489 r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
490 r_refdef.viewblend[3] = bound(0, a , 1);
494 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
495 cl.cshifts[CSHIFT_BONUS].percent = 0;
496 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
497 cl.cshifts[CSHIFT_POWERUP].percent = 0;
498 r_refdef.viewblend[0] = 0;
499 r_refdef.viewblend[1] = 0;
500 r_refdef.viewblend[2] = 0;
501 r_refdef.viewblend[3] = 0;
505 //============================================================================
514 Cmd_AddCommand ("v_cshift", V_cshift_f);
515 Cmd_AddCommand ("bf", V_BonusFlash_f);
516 Cmd_AddCommand ("centerview", V_StartPitchDrift);
518 Cvar_RegisterVariable (&v_centermove);
519 Cvar_RegisterVariable (&v_centerspeed);
521 Cvar_RegisterVariable (&v_iyaw_cycle);
522 Cvar_RegisterVariable (&v_iroll_cycle);
523 Cvar_RegisterVariable (&v_ipitch_cycle);
524 Cvar_RegisterVariable (&v_iyaw_level);
525 Cvar_RegisterVariable (&v_iroll_level);
526 Cvar_RegisterVariable (&v_ipitch_level);
528 Cvar_RegisterVariable (&v_idlescale);
529 Cvar_RegisterVariable (&crosshair);
531 Cvar_RegisterVariable (&cl_rollspeed);
532 Cvar_RegisterVariable (&cl_rollangle);
533 Cvar_RegisterVariable (&cl_bob);
534 Cvar_RegisterVariable (&cl_bobcycle);
535 Cvar_RegisterVariable (&cl_bobup);
537 Cvar_RegisterVariable (&v_kicktime);
538 Cvar_RegisterVariable (&v_kickroll);
539 Cvar_RegisterVariable (&v_kickpitch);