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 MSG_ReadVector(from, cl.protocol);
219 count = blood*0.5 + armor*0.5;
223 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
225 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
226 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
227 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
228 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
229 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
233 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
234 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
235 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
239 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
240 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
241 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
245 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
246 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
247 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
250 // calculate view angle kicks
251 if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
253 ent = &cl_entities[cl.viewentity];
254 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
255 VectorNormalize(localfrom);
256 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
257 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
258 v_dmg_time = v_kicktime.value;
262 static cshift_t v_cshift;
269 static void V_cshift_f (void)
271 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
272 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
273 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
274 v_cshift.percent = atoi(Cmd_Argv(4));
282 When you run over an item, the server sends this command
285 static void V_BonusFlash_f (void)
287 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
288 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
289 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
290 cl.cshifts[CSHIFT_BONUS].percent = 50;
294 ==============================================================================
298 ==============================================================================
301 extern matrix4x4_t viewmodelmatrix;
303 #include "cl_collision.h"
311 void V_CalcRefdef (void)
315 float vieworg[3], viewangles[3];
316 Matrix4x4_CreateIdentity(&viewmodelmatrix);
317 Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix);
318 if (cls.state == ca_connected && cls.signon == SIGNONS)
320 // ent is the view entity (visible when out of body)
321 ent = &cl_entities[cl.viewentity];
324 // entity is a fixed camera, just copy the matrix
325 Matrix4x4_Copy(&r_refdef.viewentitymatrix, &ent->render.matrix);
326 Matrix4x4_Copy(&viewmodelmatrix, &ent->render.matrix);
327 r_refdef.viewentitymatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
328 viewmodelmatrix.m[2][3] += cl.stats[STAT_VIEWHEIGHT];
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);
338 //Con_Printf("cl.onground %i oldz %f newz %f\n", cl.onground, oldz, vieworg[2]);
339 if (cl.onground && oldz < vieworg[2])
341 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
342 oldz = vieworg[2] = bound(vieworg[2] - 16, oldz, vieworg[2]);
344 else if (cl.onground && oldz > vieworg[2])
346 oldz -= (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
347 oldz = vieworg[2] = bound(vieworg[2], oldz, vieworg[2] + 16);
352 if (chase_active.value)
354 // observing entity from third person
355 vec_t camback, camup, dist, forward[3], stop[3], chase_dest[3], normal[3];
357 camback = bound(0, chase_back.value, 128);
358 if (chase_back.value != camback)
359 Cvar_SetValueQuick(&chase_back, camback);
360 camup = bound(-48, chase_up.value, 96);
361 if (chase_up.value != camup)
362 Cvar_SetValueQuick(&chase_up, camup);
364 // this + 22 is to match view_ofs for compatibility with older versions
367 if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
369 // look straight down from high above
373 AngleVectors(viewangles, forward, NULL, NULL);
375 // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
377 chase_dest[0] = vieworg[0] + forward[0] * dist;
378 chase_dest[1] = vieworg[1] + forward[1] * dist;
379 chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
380 CL_TraceLine(vieworg, chase_dest, stop, normal, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY);
381 vieworg[0] = stop[0] + forward[0] * 8 + normal[0] * 4;
382 vieworg[1] = stop[1] + forward[1] * 8 + normal[1] * 4;
383 vieworg[2] = stop[2] + forward[2] * 8 + normal[2] * 4;
387 // first person view from entity
389 if (cl.stats[STAT_HEALTH] <= 0 && gamemode != GAME_FNIGGIUM)
390 viewangles[ROLL] = 80; // dead view angle
391 VectorAdd(viewangles, cl.punchangle, viewangles);
392 viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
395 viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
396 viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
397 v_dmg_time -= cl.frametime;
400 VectorAdd(vieworg, cl.punchvector, vieworg);
401 vieworg[2] += cl.stats[STAT_VIEWHEIGHT];
402 if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
405 // LordHavoc: this code is *weird*, but not replacable (I think it
406 // should be done in QC on the server, but oh well, quake is quake)
407 // LordHavoc: figured out bobup: the time at which the sin is at 180
408 // degrees (which allows lengthening or squishing the peak or valley)
409 cycle = cl.time / cl_bobcycle.value;
410 cycle -= (int) cycle;
411 if (cycle < cl_bobup.value)
412 cycle = sin(M_PI * cycle / cl_bobup.value);
414 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
415 // bob is proportional to velocity in the xy plane
416 // (don't count Z, or jumping messes it up)
417 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
418 bob = bob*0.3 + bob*0.7*cycle;
419 vieworg[2] += bound(-7, bob, 4);
422 // calculate a view matrix for rendering the scene
423 if (v_idlescale.value)
424 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);
426 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);
427 // calculate a viewmodel matrix for use in view-attached entities
428 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 0.3);
433 void V_FadeViewFlashs(void)
435 // drop the damage value
436 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
437 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
438 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
439 // drop the bonus value
440 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
441 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
442 cl.cshifts[CSHIFT_BONUS].percent = 0;
445 void V_CalcViewBlend(void)
449 r_refdef.viewblend[0] = 0;
450 r_refdef.viewblend[1] = 0;
451 r_refdef.viewblend[2] = 0;
452 r_refdef.viewblend[3] = 0;
453 r_refdef.fovscale_x = cl.viewzoom;
454 r_refdef.fovscale_y = cl.viewzoom;
455 if (cls.state == ca_connected && cls.signon == SIGNONS && gl_polyblend.value > 0)
457 // set contents color
460 Matrix4x4_OriginFromMatrix(&r_refdef.viewentitymatrix, vieworigin);
461 supercontents = CL_PointSuperContents(vieworigin);
462 if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
464 r_refdef.fovscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
465 r_refdef.fovscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
466 if (supercontents & SUPERCONTENTS_LAVA)
468 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
469 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
470 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
472 else if (supercontents & SUPERCONTENTS_SLIME)
474 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
475 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
476 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
480 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
481 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
482 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
484 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
488 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
489 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
490 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
491 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
494 if (gamemode != GAME_TRANSFUSION)
496 if (cl.stats[STAT_ITEMS] & IT_QUAD)
498 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
499 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
500 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
501 cl.cshifts[CSHIFT_POWERUP].percent = 30;
503 else if (cl.stats[STAT_ITEMS] & IT_SUIT)
505 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
506 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
507 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
508 cl.cshifts[CSHIFT_POWERUP].percent = 20;
510 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
512 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
513 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
514 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
515 cl.cshifts[CSHIFT_POWERUP].percent = 100;
517 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
519 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
520 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
521 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
522 cl.cshifts[CSHIFT_POWERUP].percent = 30;
525 cl.cshifts[CSHIFT_POWERUP].percent = 0;
528 cl.cshifts[CSHIFT_VCSHIFT].destcolor[0] = v_cshift.destcolor[0];
529 cl.cshifts[CSHIFT_VCSHIFT].destcolor[1] = v_cshift.destcolor[1];
530 cl.cshifts[CSHIFT_VCSHIFT].destcolor[2] = v_cshift.destcolor[2];
531 cl.cshifts[CSHIFT_VCSHIFT].percent = v_cshift.percent;
533 // LordHavoc: fixed V_CalcBlend
534 for (j = 0;j < NUM_CSHIFTS;j++)
536 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
539 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
540 r_refdef.viewblend[3] = (1 - (1 - r_refdef.viewblend[3]) * (1 - a2)); // correct alpha multiply... took a while to find it on the web
543 // saturate color (to avoid blending in black)
544 if (r_refdef.viewblend[3])
546 a2 = 1 / r_refdef.viewblend[3];
547 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
550 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
551 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
552 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
553 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] * gl_polyblend.value, 1.0f);
557 //============================================================================
566 Cmd_AddCommand ("v_cshift", V_cshift_f);
567 Cmd_AddCommand ("bf", V_BonusFlash_f);
568 Cmd_AddCommand ("centerview", V_StartPitchDrift);
570 Cvar_RegisterVariable (&v_centermove);
571 Cvar_RegisterVariable (&v_centerspeed);
573 Cvar_RegisterVariable (&v_iyaw_cycle);
574 Cvar_RegisterVariable (&v_iroll_cycle);
575 Cvar_RegisterVariable (&v_ipitch_cycle);
576 Cvar_RegisterVariable (&v_iyaw_level);
577 Cvar_RegisterVariable (&v_iroll_level);
578 Cvar_RegisterVariable (&v_ipitch_level);
580 Cvar_RegisterVariable (&v_idlescale);
581 Cvar_RegisterVariable (&crosshair);
583 Cvar_RegisterVariable (&cl_rollspeed);
584 Cvar_RegisterVariable (&cl_rollangle);
585 Cvar_RegisterVariable (&cl_bob);
586 Cvar_RegisterVariable (&cl_bobcycle);
587 Cvar_RegisterVariable (&cl_bobup);
589 Cvar_RegisterVariable (&v_kicktime);
590 Cvar_RegisterVariable (&v_kickroll);
591 Cvar_RegisterVariable (&v_kickpitch);
593 Cvar_RegisterVariable (&cl_stairsmoothspeed);
595 Cvar_RegisterVariable (&chase_back);
596 Cvar_RegisterVariable (&chase_up);
597 Cvar_RegisterVariable (&chase_active);
598 if (gamemode == GAME_GOODVSBAD2)
599 Cvar_RegisterVariable (&chase_stevie);