get rid of bobbing when dead
[divverent/darkplaces.git] / view.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // view.c -- player eye positioning
21
22 #include "quakedef.h"
23 #include "cl_collision.h"
24
25 /*
26
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.
31
32 */
33
34 cvar_t  cl_rollspeed = {0, "cl_rollspeed", "200"};
35 cvar_t  cl_rollangle = {0, "cl_rollangle", "2.0"};
36
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"};
40
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"};
44
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"};
51
52 cvar_t  v_idlescale = {0, "v_idlescale", "0"};
53
54 cvar_t  crosshair = {CVAR_SAVE, "crosshair", "0"};
55
56 cvar_t  v_centermove = {0, "v_centermove", "0.15"};
57 cvar_t  v_centerspeed = {0, "v_centerspeed","500"};
58
59 float   v_dmg_time, v_dmg_roll, v_dmg_pitch;
60
61
62 /*
63 ===============
64 V_CalcRoll
65
66 Used by view and sv_user
67 ===============
68 */
69 float V_CalcRoll (vec3_t angles, vec3_t velocity)
70 {
71         vec3_t  right;
72         float   sign;
73         float   side;
74         float   value;
75
76         AngleVectors (angles, NULL, right, NULL);
77         side = DotProduct (velocity, right);
78         sign = side < 0 ? -1 : 1;
79         side = fabs(side);
80
81         value = cl_rollangle.value;
82
83         if (side < cl_rollspeed.value)
84                 side = side * value / cl_rollspeed.value;
85         else
86                 side = value;
87
88         return side*sign;
89
90 }
91
92 void V_StartPitchDrift (void)
93 {
94         if (cl.laststop == cl.time)
95                 return;         // something else is keeping it from drifting
96
97         if (cl.nodrift || !cl.pitchvel)
98         {
99                 cl.pitchvel = v_centerspeed.value;
100                 cl.nodrift = false;
101                 cl.driftmove = 0;
102         }
103 }
104
105 void V_StopPitchDrift (void)
106 {
107         cl.laststop = cl.time;
108         cl.nodrift = true;
109         cl.pitchvel = 0;
110 }
111
112 /*
113 ===============
114 V_DriftPitch
115
116 Moves the client pitch angle towards cl.idealpitch sent by the server.
117
118 If the user is adjusting pitch manually, either with lookup/lookdown,
119 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
120
121 Drifting is enabled when the center view key is hit, mlook is released and
122 lookspring is non 0, or when
123 ===============
124 */
125 static void V_DriftPitch (void)
126 {
127         float           delta, move;
128
129         if (noclip_anglehack || !cl.onground || cls.demoplayback )
130         {
131                 cl.driftmove = 0;
132                 cl.pitchvel = 0;
133                 return;
134         }
135
136 // don't count small mouse motion
137         if (cl.nodrift)
138         {
139                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
140                         cl.driftmove = 0;
141                 else
142                         cl.driftmove += cl.frametime;
143
144                 if ( cl.driftmove > v_centermove.value)
145                 {
146                         V_StartPitchDrift ();
147                 }
148                 return;
149         }
150
151         delta = cl.idealpitch - cl.viewangles[PITCH];
152
153         if (!delta)
154         {
155                 cl.pitchvel = 0;
156                 return;
157         }
158
159         move = cl.frametime * cl.pitchvel;
160         cl.pitchvel += cl.frametime * v_centerspeed.value;
161
162         if (delta > 0)
163         {
164                 if (move > delta)
165                 {
166                         cl.pitchvel = 0;
167                         move = delta;
168                 }
169                 cl.viewangles[PITCH] += move;
170         }
171         else if (delta < 0)
172         {
173                 if (move > -delta)
174                 {
175                         cl.pitchvel = 0;
176                         move = -delta;
177                 }
178                 cl.viewangles[PITCH] -= move;
179         }
180 }
181
182
183 /*
184 ==============================================================================
185
186                                                 SCREEN FLASHES
187
188 ==============================================================================
189 */
190
191
192 /*
193 ===============
194 V_ParseDamage
195 ===============
196 */
197 void V_ParseDamage (void)
198 {
199         int i, armor, blood;
200         vec3_t from;
201         //vec3_t forward, right;
202         vec3_t localfrom;
203         entity_t *ent;
204         //float side;
205         float count;
206
207         armor = MSG_ReadByte ();
208         blood = MSG_ReadByte ();
209         for (i=0 ; i<3 ; i++)
210                 from[i] = MSG_ReadCoord ();
211
212         count = blood*0.5 + armor*0.5;
213         if (count < 10)
214                 count = 10;
215
216         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
217
218         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
219         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
220                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
221         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
222                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
223
224         if (armor > blood)
225         {
226                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
227                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
228                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
229         }
230         else if (armor)
231         {
232                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
233                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
234                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
235         }
236         else
237         {
238                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
239                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
240                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
241         }
242
243         // calculate view angle kicks
244         if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
245         {
246                 ent = &cl_entities[cl.viewentity];
247                 Matrix4x4_Transform(&ent->render.inversematrix, from, localfrom);
248                 VectorNormalize(localfrom);
249                 v_dmg_pitch = count * localfrom[0] * v_kickpitch.value;
250                 v_dmg_roll = count * localfrom[1] * v_kickroll.value;
251                 v_dmg_time = v_kicktime.value;
252         }
253 }
254
255 static cshift_t v_cshift;
256
257 /*
258 ==================
259 V_cshift_f
260 ==================
261 */
262 static void V_cshift_f (void)
263 {
264         v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
265         v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
266         v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
267         v_cshift.percent = atoi(Cmd_Argv(4));
268 }
269
270
271 /*
272 ==================
273 V_BonusFlash_f
274
275 When you run over an item, the server sends this command
276 ==================
277 */
278 static void V_BonusFlash_f (void)
279 {
280         cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
281         cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
282         cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
283         cl.cshifts[CSHIFT_BONUS].percent = 50;
284 }
285
286 /*
287 ==============================================================================
288
289                                                 VIEW RENDERING
290
291 ==============================================================================
292 */
293
294 #define MAXVIEWMODELS 32
295 extern int numviewmodels;
296 extern entity_t *viewmodels[MAXVIEWMODELS];
297
298 /*
299 ==================
300 V_CalcRefdef
301
302 ==================
303 */
304 void V_CalcRefdef (void)
305 {
306         float r, g, b, a, a2;
307         int j;
308         entity_t *ent;
309         if (cls.state == ca_connected && cls.signon == SIGNONS)
310         {
311                 // ent is the player model (visible when out of body)
312                 ent = &cl_entities[cl.viewentity];
313                 V_DriftPitch();
314                 if (cl.intermission)
315                 {
316                         // entity is a fixed camera
317                         VectorCopy(ent->render.origin, r_refdef.vieworg);
318                         VectorCopy(ent->render.angles, r_refdef.viewangles);
319                 }
320                 else if (chase_active.value)
321                 {
322                         // observing entity from third person
323                         VectorCopy(ent->render.origin, r_refdef.vieworg);
324                         VectorCopy(cl.viewangles, r_refdef.viewangles);
325                         Chase_Update();
326                 }
327                 else
328                 {
329                         // first person view from entity
330                         VectorCopy(ent->render.origin, r_refdef.vieworg);
331                         VectorCopy(cl.viewangles, r_refdef.viewangles);
332                         // angles
333                         if (cl.stats[STAT_HEALTH] <= 0)
334                                 r_refdef.viewangles[ROLL] = 80; // dead view angle
335                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
336                         r_refdef.viewangles[ROLL] += V_CalcRoll(cl.viewangles, cl.velocity);
337                         if (v_dmg_time > 0)
338                         {
339                                 r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
340                                 r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
341                                 v_dmg_time -= cl.frametime;
342                         }
343                         if (v_idlescale.value)
344                         {
345                                 r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
346                                 r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
347                                 r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
348                         }
349                         // origin
350                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
351                         r_refdef.vieworg[2] += cl.viewheight;
352                         if (cl.stats[STAT_HEALTH] > 0)
353                         {
354                                 if (cl_bob.value && cl_bobcycle.value)
355                                 {
356                                         double bob, cycle;
357                                         // LordHavoc: this code is *weird*, but not replacable (I think it
358                                         // should be done in QC on the server, but oh well, quake is quake)
359                                         // LordHavoc: figured out bobup: the time at which the sin is at 180
360                                         // degrees (which allows lengthening or squishing the peak or valley)
361                                         cycle = cl.time / cl_bobcycle.value;
362                                         cycle -= (int) cycle;
363                                         if (cycle < cl_bobup.value)
364                                                 cycle = sin(M_PI * cycle / cl_bobup.value);
365                                         else
366                                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
367                                         // bob is proportional to velocity in the xy plane
368                                         // (don't count Z, or jumping messes it up)
369                                         bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
370                                         bob = bob*0.3 + bob*0.7*cycle;
371                                         r_refdef.vieworg[2] += bound(-7, bob, 4);
372                                 }
373                                 // link the delayed viewmodel entities
374                                 if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY))
375                                 {
376                                         int i;
377                                         entity_t *ent;
378                                         matrix4x4_t matrix, matrix2;
379                                         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);
380                                         for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
381                                         {
382                                                 ent = viewmodels[i];
383                                                 r_refdef.entities[r_refdef.numentities++] = &ent->render;
384                                                 matrix2 = ent->render.matrix;
385                                                 Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
386                                                 Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
387                                                 CL_BoundingBoxForEntity(&ent->render);
388                                         }
389                                 }
390                         }
391                 }
392
393                 // drop the damage value
394                 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
395                 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
396                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
397
398                 // drop the bonus value
399                 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
400                 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
401                         cl.cshifts[CSHIFT_BONUS].percent = 0;
402
403                 // set contents color
404                 switch (CL_PointContents(r_refdef.vieworg))
405                 {
406                 case CONTENTS_EMPTY:
407                 case CONTENTS_SOLID:
408                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
409                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
410                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
411                         cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
412                         break;
413                 case CONTENTS_LAVA:
414                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
415                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
416                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
417                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
418                         break;
419                 case CONTENTS_SLIME:
420                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
421                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
422                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
423                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
424                         break;
425                 default:
426                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
427                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
428                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
429                         cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
430                 }
431
432                 if (cl.items & IT_QUAD)
433                 {
434                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
435                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
436                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
437                         cl.cshifts[CSHIFT_POWERUP].percent = 30;
438                 }
439                 else if (cl.items & IT_SUIT)
440                 {
441                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
442                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
443                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
444                         cl.cshifts[CSHIFT_POWERUP].percent = 20;
445                 }
446                 else if (cl.items & IT_INVISIBILITY)
447                 {
448                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
449                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
450                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
451                         cl.cshifts[CSHIFT_POWERUP].percent = 100;
452                 }
453                 else if (cl.items & IT_INVULNERABILITY)
454                 {
455                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
456                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
457                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
458                         cl.cshifts[CSHIFT_POWERUP].percent = 30;
459                 }
460                 else
461                         cl.cshifts[CSHIFT_POWERUP].percent = 0;
462
463                 // LordHavoc: fixed V_CalcBlend
464                 r = 0;
465                 g = 0;
466                 b = 0;
467                 a = 0;
468
469                 for (j=0 ; j<NUM_CSHIFTS ; j++)
470                 {
471                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
472
473                         if (a2 < 0)
474                                 continue;
475                         if (a2 > 1)
476                                 a2 = 1;
477                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
478                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
479                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
480                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
481                 }
482                 // saturate color (to avoid blending in black)
483                 if (a)
484                 {
485                         a2 = 1 / a;
486                         r *= a2;
487                         g *= a2;
488                         b *= a2;
489                 }
490
491                 r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
492                 r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
493                 r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
494                 r_refdef.viewblend[3] = bound(0, a              , 1);
495         }
496         else
497         {
498                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
499                 cl.cshifts[CSHIFT_BONUS].percent = 0;
500                 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
501                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
502                 r_refdef.viewblend[0] = 0;
503                 r_refdef.viewblend[1] = 0;
504                 r_refdef.viewblend[2] = 0;
505                 r_refdef.viewblend[3] = 0;
506         }
507 }
508
509 //============================================================================
510
511 /*
512 =============
513 V_Init
514 =============
515 */
516 void V_Init (void)
517 {
518         Cmd_AddCommand ("v_cshift", V_cshift_f);
519         Cmd_AddCommand ("bf", V_BonusFlash_f);
520         Cmd_AddCommand ("centerview", V_StartPitchDrift);
521
522         Cvar_RegisterVariable (&v_centermove);
523         Cvar_RegisterVariable (&v_centerspeed);
524
525         Cvar_RegisterVariable (&v_iyaw_cycle);
526         Cvar_RegisterVariable (&v_iroll_cycle);
527         Cvar_RegisterVariable (&v_ipitch_cycle);
528         Cvar_RegisterVariable (&v_iyaw_level);
529         Cvar_RegisterVariable (&v_iroll_level);
530         Cvar_RegisterVariable (&v_ipitch_level);
531
532         Cvar_RegisterVariable (&v_idlescale);
533         Cvar_RegisterVariable (&crosshair);
534
535         Cvar_RegisterVariable (&cl_rollspeed);
536         Cvar_RegisterVariable (&cl_rollangle);
537         Cvar_RegisterVariable (&cl_bob);
538         Cvar_RegisterVariable (&cl_bobcycle);
539         Cvar_RegisterVariable (&cl_bobup);
540
541         Cvar_RegisterVariable (&v_kicktime);
542         Cvar_RegisterVariable (&v_kickroll);
543         Cvar_RegisterVariable (&v_kickpitch);
544 }
545