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 cvar_t cl_stairsmoothspeed = {CVAR_SAVE, "cl_stairsmoothspeed", "160"};
61 cvar_t chase_back = {CVAR_SAVE, "chase_back", "48"};
62 cvar_t chase_up = {CVAR_SAVE, "chase_up", "24"};
63 cvar_t chase_active = {CVAR_SAVE, "chase_active", "0"};
65 cvar_t chase_stevie = {0, "chase_stevie", "0"};
67 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
74 Used by view and sv_user
77 float V_CalcRoll (vec3_t angles, vec3_t velocity)
84 AngleVectors (angles, NULL, right, NULL);
85 side = DotProduct (velocity, right);
86 sign = side < 0 ? -1 : 1;
89 value = cl_rollangle.value;
91 if (side < cl_rollspeed.value)
92 side = side * value / cl_rollspeed.value;
100 void V_StartPitchDrift (void)
102 if (cl.laststop == cl.time)
103 return; // something else is keeping it from drifting
105 if (cl.nodrift || !cl.pitchvel)
107 cl.pitchvel = v_centerspeed.value;
113 void V_StopPitchDrift (void)
115 cl.laststop = cl.time;
124 Moves the client pitch angle towards cl.idealpitch sent by the server.
126 If the user is adjusting pitch manually, either with lookup/lookdown,
127 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
129 Drifting is enabled when the center view key is hit, mlook is released and
130 lookspring is non 0, or when
133 void V_DriftPitch (void)
137 if (noclip_anglehack || !cl.onground || cls.demoplayback )
144 // don't count small mouse motion
147 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
150 cl.driftmove += cl.frametime;
152 if ( cl.driftmove > v_centermove.value)
154 V_StartPitchDrift ();
159 delta = cl.idealpitch - cl.viewangles[PITCH];
167 move = cl.frametime * cl.pitchvel;
168 cl.pitchvel += cl.frametime * v_centerspeed.value;
177 cl.viewangles[PITCH] += move;
186 cl.viewangles[PITCH] -= move;
192 ==============================================================================
196 ==============================================================================
205 void V_ParseDamage (void)
209 //vec3_t forward, right;
215 armor = MSG_ReadByte ();
216 blood = MSG_ReadByte ();
217 for (i=0 ; i<3 ; i++)
218 from[i] = MSG_ReadCoord ();
220 count = blood*0.5 + armor*0.5;
224 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
226 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
227 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
228 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
229 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
230 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
234 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
235 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
236 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
240 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
241 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
242 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
246 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
247 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
248 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
251 // calculate view angle kicks
252 if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
254 ent = &cl_entities[cl.viewentity];
255 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
256 VectorNormalize(localfrom);
257 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
258 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
259 v_dmg_time = v_kicktime.value;
263 static cshift_t v_cshift;
270 static void V_cshift_f (void)
272 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
273 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
274 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
275 v_cshift.percent = atoi(Cmd_Argv(4));
283 When you run over an item, the server sends this command
286 static void V_BonusFlash_f (void)
288 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
289 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
290 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
291 cl.cshifts[CSHIFT_BONUS].percent = 50;
295 ==============================================================================
299 ==============================================================================
302 extern matrix4x4_t viewmodelmatrix;
304 #include "cl_collision.h"
312 extern float timerefreshangle;
313 void V_CalcRefdef (void)
317 float vieworg[3], viewangles[3], newz;
318 Matrix4x4_CreateIdentity(&viewmodelmatrix);
319 Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
320 if (cls.state == ca_connected && cls.signon == SIGNONS)
322 // ent is the view entity (visible when out of body)
323 ent = &cl_entities[cl.viewentity];
326 // entity is a fixed camera, just copy the matrix
327 Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix);
328 Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix);
332 // player can look around, so take the origin from the entity,
333 // and the angles from the input system
334 Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
335 VectorCopy(cl.viewangles, viewangles);
340 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
341 oldz = bound(-16, oldz, 0);
345 if (chase_active.value)
347 // observing entity from third person
348 vec_t camback, camup, dist, forward[3], stop[3], chase_dest[3], normal[3];
350 camback = bound(0, chase_back.value, 128);
351 if (chase_back.value != camback)
352 Cvar_SetValueQuick(&chase_back, camback);
353 camup = bound(-48, chase_up.value, 96);
354 if (chase_up.value != camup)
355 Cvar_SetValueQuick(&chase_up, camup);
357 // this + 22 is to match view_ofs for compatibility with older versions
360 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
362 // look straight down from high above
366 AngleVectors(viewangles, forward, NULL, NULL);
368 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
370 chase_dest[0] = vieworg[0] + forward[0] * dist;
371 chase_dest[1] = vieworg[1] + forward[1] * dist;
372 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
373 CL_TraceLine(vieworg, chase_dest, stop, normal, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY);
374 vieworg[0] = stop[0] + forward[0] * 8 + normal[0] * 4;
375 vieworg[1] = stop[1] + forward[1] * 8 + normal[1] * 4;
376 vieworg[2] = stop[2] + forward[2] * 8 + normal[2] * 4;
380 // first person view from entity
382 if (cl.stats[STAT_HEALTH] <= 0)
383 viewangles[ROLL] = 80; // dead view angle
384 VectorAdd(viewangles, cl.punchangle, viewangles);
385 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
388 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
389 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
390 v_dmg_time -= cl.frametime;
393 VectorAdd(vieworg, cl.punchvector, vieworg);
394 vieworg[2] += cl.viewheight;
395 if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
398 // LordHavoc: this code is *weird*, but not replacable (I think it
399 // should be done in QC on the server, but oh well, quake is quake)
400 // LordHavoc: figured out bobup: the time at which the sin is at 180
401 // degrees (which allows lengthening or squishing the peak or valley)
402 cycle = cl.time / cl_bobcycle.value;
403 cycle -= (int) cycle;
404 if (cycle < cl_bobup.value)
405 cycle = sin(M_PI * cycle / cl_bobup.value);
407 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
408 // bob is proportional to velocity in the xy plane
409 // (don't count Z, or jumping messes it up)
410 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
411 bob = bob*0.3 + bob*0.7*cycle;
412 vieworg[2] += bound(-7, bob, 4);
415 // calculate a view matrix for rendering the scene
416 if (v_idlescale.value)
417 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, 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);
419 Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, 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);
420 // calculate a viewmodel matrix for use in view-attached entities
421 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
426 void V_FadeViewFlashs(void)
428 // drop the damage value
429 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
430 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
431 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
432 // drop the bonus value
433 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
434 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
435 cl.cshifts[CSHIFT_BONUS].percent = 0;
438 void V_CalcViewBlend(void)
442 r_refdef.viewblend[0] = 0;
443 r_refdef.viewblend[1] = 0;
444 r_refdef.viewblend[2] = 0;
445 r_refdef.viewblend[3] = 0;
446 if (cls.state == ca_connected && cls.signon == SIGNONS)
448 // set contents color
449 switch (CL_PointQ1Contents(r_vieworigin))
453 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
454 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
455 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
456 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
459 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
460 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
461 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
462 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
465 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
466 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
467 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
468 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
471 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
472 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
473 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
474 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
477 if (gamemode != GAME_TRANSFUSION)
479 if (cl.items & IT_QUAD)
481 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
482 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
483 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
484 cl.cshifts[CSHIFT_POWERUP].percent = 30;
486 else if (cl.items & IT_SUIT)
488 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
489 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
490 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
491 cl.cshifts[CSHIFT_POWERUP].percent = 20;
493 else if (cl.items & IT_INVISIBILITY)
495 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
496 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
497 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
498 cl.cshifts[CSHIFT_POWERUP].percent = 100;
500 else if (cl.items & IT_INVULNERABILITY)
502 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
503 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
504 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
505 cl.cshifts[CSHIFT_POWERUP].percent = 30;
508 cl.cshifts[CSHIFT_POWERUP].percent = 0;
511 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
512 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
513 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
514 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
516 // LordHavoc: fixed V_CalcBlend
517 for (j = 0;j < NUM_CSHIFTS;j++)
519 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
522 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
523 r_refdef.viewblend[3] = 1 - (1 - r_refdef.viewblend[3]) * (1 - a2); // correct alpha multiply... took a while to find it on the web
526 // saturate color (to avoid blending in black)
527 if (r_refdef.viewblend[3])
529 a2 = 1 / r_refdef.viewblend[3];
530 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
533 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
534 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
535 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
536 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] , 1.0f);
540 //============================================================================
549 Cmd_AddCommand ("v_cshift", V_cshift_f);
550 Cmd_AddCommand ("bf", V_BonusFlash_f);
551 Cmd_AddCommand ("centerview", V_StartPitchDrift);
553 Cvar_RegisterVariable (&v_centermove);
554 Cvar_RegisterVariable (&v_centerspeed);
556 Cvar_RegisterVariable (&v_iyaw_cycle);
557 Cvar_RegisterVariable (&v_iroll_cycle);
558 Cvar_RegisterVariable (&v_ipitch_cycle);
559 Cvar_RegisterVariable (&v_iyaw_level);
560 Cvar_RegisterVariable (&v_iroll_level);
561 Cvar_RegisterVariable (&v_ipitch_level);
563 Cvar_RegisterVariable (&v_idlescale);
564 Cvar_RegisterVariable (&crosshair);
566 Cvar_RegisterVariable (&cl_rollspeed);
567 Cvar_RegisterVariable (&cl_rollangle);
568 Cvar_RegisterVariable (&cl_bob);
569 Cvar_RegisterVariable (&cl_bobcycle);
570 Cvar_RegisterVariable (&cl_bobup);
572 Cvar_RegisterVariable (&v_kicktime);
573 Cvar_RegisterVariable (&v_kickroll);
574 Cvar_RegisterVariable (&v_kickpitch);
576 Cvar_RegisterVariable (&cl_stairsmoothspeed);
578 Cvar_RegisterVariable (&chase_back);
579 Cvar_RegisterVariable (&chase_up);
580 Cvar_RegisterVariable (&chase_active);
581 if (gamemode == GAME_GOODVSBAD2)
582 Cvar_RegisterVariable (&chase_stevie);