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 cvar_t v_deathtilt = {0, "v_deathtilt", "1"};
69 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
76 Used by view and sv_user
79 float V_CalcRoll (vec3_t angles, vec3_t velocity)
86 AngleVectors (angles, NULL, right, NULL);
87 side = DotProduct (velocity, right);
88 sign = side < 0 ? -1 : 1;
91 value = cl_rollangle.value;
93 if (side < cl_rollspeed.value)
94 side = side * value / cl_rollspeed.value;
102 void V_StartPitchDrift (void)
104 if (cl.laststop == cl.time)
105 return; // something else is keeping it from drifting
107 if (cl.nodrift || !cl.pitchvel)
109 cl.pitchvel = v_centerspeed.value;
115 void V_StopPitchDrift (void)
117 cl.laststop = cl.time;
126 Moves the client pitch angle towards cl.idealpitch sent by the server.
128 If the user is adjusting pitch manually, either with lookup/lookdown,
129 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
131 Drifting is enabled when the center view key is hit, mlook is released and
132 lookspring is non 0, or when
135 void V_DriftPitch (void)
139 if (noclip_anglehack || !cl.onground || cls.demoplayback )
146 // don't count small mouse motion
149 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
152 cl.driftmove += cl.frametime;
154 if ( cl.driftmove > v_centermove.value)
156 V_StartPitchDrift ();
161 delta = cl.idealpitch - cl.viewangles[PITCH];
169 move = cl.frametime * cl.pitchvel;
170 cl.pitchvel += cl.frametime * v_centerspeed.value;
179 cl.viewangles[PITCH] += move;
188 cl.viewangles[PITCH] -= move;
194 ==============================================================================
198 ==============================================================================
207 void V_ParseDamage (void)
211 //vec3_t forward, right;
217 armor = MSG_ReadByte ();
218 blood = MSG_ReadByte ();
219 MSG_ReadVector(from, cl.protocol);
221 count = blood*0.5 + armor*0.5;
225 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
227 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
228 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
229 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
230 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
231 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
235 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
236 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
237 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
241 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
242 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
243 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
247 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
248 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
249 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
252 // calculate view angle kicks
253 if (cl_entities[cl.viewentity].state_current.active)
255 ent = &cl_entities[cl.viewentity];
256 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
257 VectorNormalize(localfrom);
258 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
259 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
260 v_dmg_time = v_kicktime.value;
264 static cshift_t v_cshift;
271 static void V_cshift_f (void)
273 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
274 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
275 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
276 v_cshift.percent = atoi(Cmd_Argv(4));
284 When you run over an item, the server sends this command
287 static void V_BonusFlash_f (void)
289 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
290 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
291 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
292 cl.cshifts[CSHIFT_BONUS].percent = 50;
296 ==============================================================================
300 ==============================================================================
303 extern matrix4x4_t viewmodelmatrix;
305 #include "cl_collision.h"
313 void V_CalcRefdef (void)
317 float vieworg[3], viewangles[3];
319 Matrix4x4_CreateIdentity(&viewmodelmatrix);
320 Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
321 if (cls.state == ca_connected && cls.signon == SIGNONS)
323 // ent is the view entity (visible when out of body)
324 ent = &cl_entities[cl.viewentity];
327 // entity is a fixed camera, just copy the matrix
328 Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix);
329 Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix);
330 r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
331 viewmodelmatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
335 // player can look around, so take the origin from the entity,
336 // and the angles from the input system
337 Matrix4x4_OriginFromMatrix(&ent->render.matrix, vieworg);
338 VectorCopy(cl.viewangles, viewangles);
341 //Con_Printf("cl.onground %i oldz %f newz %f\n", cl.onground, oldz, vieworg[2]);
342 if (cl.onground && oldz < vieworg[2])
344 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
345 oldz = vieworg[2] = bound(vieworg[2] - 16, oldz, vieworg[2]);
347 else if (cl.onground && oldz > vieworg[2])
349 oldz -= (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
350 oldz = vieworg[2] = bound(vieworg[2], oldz, vieworg[2] + 16);
355 if (chase_active.value)
357 // observing entity from third person
358 vec_t camback, camup, dist, forward[3], chase_dest[3];
360 camback = bound(0, chase_back.value, 128);
361 if (chase_back.value != camback)
362 Cvar_SetValueQuick(&chase_back, camback);
363 camup = bound(-48, chase_up.value, 96);
364 if (chase_up.value != camup)
365 Cvar_SetValueQuick(&chase_up, camup);
367 // this + 22 is to match view_ofs for compatibility with older versions
370 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
372 // look straight down from high above
376 AngleVectors(viewangles, forward, NULL, NULL);
378 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
380 chase_dest[0] = vieworg[0] + forward[0] * dist;
381 chase_dest[1] = vieworg[1] + forward[1] * dist;
382 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
383 trace = CL_TraceBox(vieworg, vec3_origin, vec3_origin, chase_dest, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, false);
384 VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
388 // first person view from entity
390 if (cl.stats[STAT_HEALTH] <= 0 && v_deathtilt.integer)
391 viewangles[ROLL] = 80; // dead view angle
392 VectorAdd(viewangles, cl.punchangle, viewangles);
393 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.movement_velocity);
396 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
397 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
398 v_dmg_time -= cl.frametime;
401 VectorAdd(vieworg, cl.punchvector, vieworg);
402 vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
403 if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
406 // LordHavoc: this code is *weird*, but not replacable (I think it
407 // should be done in QC on the server, but oh well, quake is quake)
408 // LordHavoc: figured out bobup: the time at which the sin is at 180
409 // degrees (which allows lengthening or squishing the peak or valley)
410 cycle = cl.time / cl_bobcycle.value;
411 cycle -= (int) cycle;
412 if (cycle < cl_bobup.value)
413 cycle = sin(M_PI * cycle / cl_bobup.value);
415 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
416 // bob is proportional to velocity in the xy plane
417 // (don't count Z, or jumping messes it up)
418 bob = sqrt(cl.movement_velocity[0]*cl.movement_velocity[0] + cl.movement_velocity[1]*cl.movement_velocity[1]) * cl_bob.value;
419 bob = bob*0.3 + bob*0.7*cycle;
420 vieworg[2] += bound(-7, bob, 4);
423 // calculate a view matrix for rendering the scene
424 if (v_idlescale.value)
425 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);
427 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);
428 // calculate a viewmodel matrix for use in view-attached entities
429 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
434 void V_FadeViewFlashs(void)
436 // drop the damage value
437 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
438 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
439 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
440 // drop the bonus value
441 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
442 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
443 cl.cshifts[CSHIFT_BONUS].percent = 0;
446 void V_CalcViewBlend(void)
450 r_refdef.viewblend[0] = 0;
451 r_refdef.viewblend[1] = 0;
452 r_refdef.viewblend[2] = 0;
453 r_refdef.viewblend[3] = 0;
454 r_refdef.fovscale_x = cl.viewzoom;
455 r_refdef.fovscale_y = cl.viewzoom;
456 if (cls.state == ca_connected && cls.signon == SIGNONS && gl_polyblend.value > 0)
458 // set contents color
461 Matrix4x4_OriginFromMatrix(&r_refdef.viewentitymatrix, vieworigin);
462 supercontents = CL_PointSuperContents(vieworigin);
463 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
465 r_refdef.fovscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
466 r_refdef.fovscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
467 if (supercontents & SUPERCONTENTS_LAVA)
469 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
470 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
471 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
473 else if (supercontents & SUPERCONTENTS_SLIME)
475 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
476 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
477 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
481 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
482 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
483 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
485 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
489 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
490 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
491 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
492 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
495 if (gamemode != GAME_TRANSFUSION)
497 if (cl.stats[STAT_ITEMS] & IT_QUAD)
499 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
500 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
501 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
502 cl.cshifts[CSHIFT_POWERUP].percent = 30;
504 else if (cl.stats[STAT_ITEMS] & IT_SUIT)
506 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
507 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
508 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
509 cl.cshifts[CSHIFT_POWERUP].percent = 20;
511 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
513 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
514 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
515 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
516 cl.cshifts[CSHIFT_POWERUP].percent = 100;
518 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
520 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
521 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
522 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
523 cl.cshifts[CSHIFT_POWERUP].percent = 30;
526 cl.cshifts[CSHIFT_POWERUP].percent = 0;
529 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
530 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
531 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
532 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
534 // LordHavoc: fixed V_CalcBlend
535 for (j = 0;j < NUM_CSHIFTS;j++)
537 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
540 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
541 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply... took a while to find it on the web
544 // saturate color (to avoid blending in black)
545 if (r_refdef.viewblend[3])
547 a2 = 1 / r_refdef.viewblend[3];
548 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
551 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
552 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
553 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
554 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
558 //============================================================================
567 Cmd_AddCommand ("v_cshift", V_cshift_f);
568 Cmd_AddCommand ("bf", V_BonusFlash_f);
569 Cmd_AddCommand ("centerview", V_StartPitchDrift);
571 Cvar_RegisterVariable (&v_centermove);
572 Cvar_RegisterVariable (&v_centerspeed);
574 Cvar_RegisterVariable (&v_iyaw_cycle);
575 Cvar_RegisterVariable (&v_iroll_cycle);
576 Cvar_RegisterVariable (&v_ipitch_cycle);
577 Cvar_RegisterVariable (&v_iyaw_level);
578 Cvar_RegisterVariable (&v_iroll_level);
579 Cvar_RegisterVariable (&v_ipitch_level);
581 Cvar_RegisterVariable (&v_idlescale);
582 Cvar_RegisterVariable (&crosshair);
584 Cvar_RegisterVariable (&cl_rollspeed);
585 Cvar_RegisterVariable (&cl_rollangle);
586 Cvar_RegisterVariable (&cl_bob);
587 Cvar_RegisterVariable (&cl_bobcycle);
588 Cvar_RegisterVariable (&cl_bobup);
590 Cvar_RegisterVariable (&v_kicktime);
591 Cvar_RegisterVariable (&v_kickroll);
592 Cvar_RegisterVariable (&v_kickpitch);
594 Cvar_RegisterVariable (&cl_stairsmoothspeed);
596 Cvar_RegisterVariable (&chase_back);
597 Cvar_RegisterVariable (&chase_up);
598 Cvar_RegisterVariable (&chase_active);
599 if (gamemode == GAME_GOODVSBAD2)
600 Cvar_RegisterVariable (&chase_stevie);
602 Cvar_RegisterVariable (&v_deathtilt);