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