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 float v_dmg_time, v_dmg_roll, v_dmg_pitch;
68 Used by view and sv_user
71 float V_CalcRoll (vec3_t angles, vec3_t velocity)
78 AngleVectors (angles, NULL, right, NULL);
79 side = DotProduct (velocity, right);
80 sign = side < 0 ? -1 : 1;
83 value = cl_rollangle.value;
85 if (side < cl_rollspeed.value)
86 side = side * value / cl_rollspeed.value;
94 void V_StartPitchDrift (void)
96 if (cl.laststop == cl.time)
97 return; // something else is keeping it from drifting
99 if (cl.nodrift || !cl.pitchvel)
101 cl.pitchvel = v_centerspeed.value;
107 void V_StopPitchDrift (void)
109 cl.laststop = cl.time;
118 Moves the client pitch angle towards cl.idealpitch sent by the server.
120 If the user is adjusting pitch manually, either with lookup/lookdown,
121 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
123 Drifting is enabled when the center view key is hit, mlook is released and
124 lookspring is non 0, or when
127 void V_DriftPitch (void)
131 if (noclip_anglehack || !cl.onground || cls.demoplayback )
138 // don't count small mouse motion
141 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
144 cl.driftmove += cl.frametime;
146 if ( cl.driftmove > v_centermove.value)
148 V_StartPitchDrift ();
153 delta = cl.idealpitch - cl.viewangles[PITCH];
161 move = cl.frametime * cl.pitchvel;
162 cl.pitchvel += cl.frametime * v_centerspeed.value;
171 cl.viewangles[PITCH] += move;
180 cl.viewangles[PITCH] -= move;
186 ==============================================================================
190 ==============================================================================
199 void V_ParseDamage (void)
203 //vec3_t forward, right;
209 armor = MSG_ReadByte ();
210 blood = MSG_ReadByte ();
211 for (i=0 ; i<3 ; i++)
212 from[i] = MSG_ReadCoord ();
214 count = blood*0.5 + armor*0.5;
218 cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame
220 cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
221 if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
222 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
223 if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
224 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
228 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
229 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
230 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
234 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
235 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
236 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
240 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
241 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
242 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
245 // calculate view angle kicks
246 if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
248 ent = &cl_entities[cl.viewentity];
249 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
250 VectorNormalize(localfrom);
251 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
252 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
253 v_dmg_time = v_kicktime.value;
257 static cshift_t v_cshift;
264 static void V_cshift_f (void)
266 v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
267 v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
268 v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
269 v_cshift.percent = atoi(Cmd_Argv(4));
277 When you run over an item, the server sends this command
280 static void V_BonusFlash_f (void)
282 cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
283 cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
284 cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
285 cl.cshifts[CSHIFT_BONUS].percent = 50;
289 ==============================================================================
293 ==============================================================================
296 extern matrix4x4_t viewmodelmatrix;
304 void V_CalcRefdef (void)
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];
312 VectorCopy(ent->render.origin, r_refdef.vieworg);
313 VectorCopy(cl.viewangles, r_refdef.viewangles);
314 if (oldz < ent->render.origin[2])
316 if (cl.time > cl.oldtime)
317 oldz += (cl.time - cl.oldtime) * cl_stairsmoothspeed.value;
318 oldz -= ent->render.origin[2];
319 oldz = bound(-16, oldz, 0);
320 r_refdef.vieworg[2] += oldz;
321 oldz += ent->render.origin[2];
324 oldz = ent->render.origin[2];
327 // entity is a fixed camera
328 VectorCopy(ent->render.angles, r_refdef.viewangles);
330 else if (chase_active.value)
332 // observing entity from third person
337 // first person view from entity
339 if (cl.stats[STAT_HEALTH] <= 0)
340 r_refdef.viewangles[ROLL] = 80; // dead view angle
341 VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
342 r_refdef.viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
345 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
346 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
347 v_dmg_time -= cl.frametime;
349 if (v_idlescale.value)
351 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
352 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
353 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
356 VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
357 r_refdef.vieworg[2] += cl.viewheight;
358 if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
361 // LordHavoc: this code is *weird*, but not replacable (I think it
362 // should be done in QC on the server, but oh well, quake is quake)
363 // LordHavoc: figured out bobup: the time at which the sin is at 180
364 // degrees (which allows lengthening or squishing the peak or valley)
365 cycle = cl.time / cl_bobcycle.value;
366 cycle -= (int) cycle;
367 if (cycle < cl_bobup.value)
368 cycle = sin(M_PI * cycle / cl_bobup.value);
370 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
371 // bob is proportional to velocity in the xy plane
372 // (don't count Z, or jumping messes it up)
373 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
374 bob = bob*0.3 + bob*0.7*cycle;
375 r_refdef.vieworg[2] += bound(-7, bob, 4);
378 // calculate a viewmodel matrix for use in view-attached entities
379 Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, 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);
382 Matrix4x4_CreateIdentity(&viewmodelmatrix);
385 void V_FadeViewFlashs(void)
387 // drop the damage value
388 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
389 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
390 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
391 // drop the bonus value
392 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
393 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
394 cl.cshifts[CSHIFT_BONUS].percent = 0;
397 void V_CalcViewBlend(void)
401 r_refdef.viewblend[0] = 0;
402 r_refdef.viewblend[1] = 0;
403 r_refdef.viewblend[2] = 0;
404 r_refdef.viewblend[3] = 0;
405 if (cls.state == ca_connected && cls.signon == SIGNONS)
407 // set contents color
408 switch (CL_PointQ1Contents(r_refdef.vieworg))
412 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
413 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
414 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
415 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
418 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
419 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
420 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
421 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
424 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
425 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
426 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
427 cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
430 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
431 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
432 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
433 cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
436 if (cl.items & IT_QUAD)
438 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
439 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
440 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
441 cl.cshifts[CSHIFT_POWERUP].percent = 30;
443 else if (cl.items & IT_SUIT)
445 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
446 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
447 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
448 cl.cshifts[CSHIFT_POWERUP].percent = 20;
450 else if (cl.items & IT_INVISIBILITY)
452 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
453 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
454 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
455 cl.cshifts[CSHIFT_POWERUP].percent = 100;
457 else if (cl.items & IT_INVULNERABILITY)
459 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
460 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
461 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
462 cl.cshifts[CSHIFT_POWERUP].percent = 30;
465 cl.cshifts[CSHIFT_POWERUP].percent = 0;
467 // LordHavoc: fixed V_CalcBlend
468 for (j = 0;j < NUM_CSHIFTS;j++)
470 a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
473 VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
474 r_refdef.viewblend[3] = 1 - (1 - r_refdef.viewblend[3]) * (1 - a2); // correct alpha multiply... took a while to find it on the web
477 // saturate color (to avoid blending in black)
478 if (r_refdef.viewblend[3])
480 a2 = 1 / r_refdef.viewblend[3];
481 VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
484 r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
485 r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
486 r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
487 r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3] , 1.0f);
491 //============================================================================
500 Cmd_AddCommand ("v_cshift", V_cshift_f);
501 Cmd_AddCommand ("bf", V_BonusFlash_f);
502 Cmd_AddCommand ("centerview", V_StartPitchDrift);
504 Cvar_RegisterVariable (&v_centermove);
505 Cvar_RegisterVariable (&v_centerspeed);
507 Cvar_RegisterVariable (&v_iyaw_cycle);
508 Cvar_RegisterVariable (&v_iroll_cycle);
509 Cvar_RegisterVariable (&v_ipitch_cycle);
510 Cvar_RegisterVariable (&v_iyaw_level);
511 Cvar_RegisterVariable (&v_iroll_level);
512 Cvar_RegisterVariable (&v_ipitch_level);
514 Cvar_RegisterVariable (&v_idlescale);
515 Cvar_RegisterVariable (&crosshair);
517 Cvar_RegisterVariable (&cl_rollspeed);
518 Cvar_RegisterVariable (&cl_rollangle);
519 Cvar_RegisterVariable (&cl_bob);
520 Cvar_RegisterVariable (&cl_bobcycle);
521 Cvar_RegisterVariable (&cl_bobup);
523 Cvar_RegisterVariable (&v_kicktime);
524 Cvar_RegisterVariable (&v_kickroll);
525 Cvar_RegisterVariable (&v_kickpitch);
527 Cvar_RegisterVariable (&cl_stairsmoothspeed);