]> git.xonotic.org Git - xonotic/darkplaces.git/blob - view.c
got rid of buildnumber.c and buildnum program, now uses builddate.c (touched each...
[xonotic/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 //      if (cl.inwater)
82 //              value *= 6;
83
84         if (side < cl_rollspeed.value)
85                 side = side * value / cl_rollspeed.value;
86         else
87                 side = value;
88
89         return side*sign;
90
91 }
92
93 static float V_CalcBob (void)
94 {
95         double bob, cycle;
96
97         // LordHavoc: easy case
98         if (cl_bob.value == 0)
99                 return 0;
100         if (cl_bobcycle.value == 0)
101                 return 0;
102
103         // LordHavoc: FIXME: this code is *weird*, redesign it sometime
104         cycle = cl.time  / cl_bobcycle.value;
105         cycle -= (int) cycle;
106         if (cycle < cl_bobup.value)
107                 cycle = M_PI * cycle / cl_bobup.value;
108         else
109                 cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
110
111         // bob is proportional to velocity in the xy plane
112         // (don't count Z, or jumping messes it up)
113
114         bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
115         //Con_Printf ("speed: %5.1f\n", Length(cl.velocity));
116         bob = bob*0.3 + bob*0.7*sin(cycle);
117         bob = bound(-7, bob, 4);
118         return bob;
119
120 }
121
122 void V_StartPitchDrift (void)
123 {
124 #if 1
125         if (cl.laststop == cl.time)
126         {
127                 return;         // something else is keeping it from drifting
128         }
129 #endif
130         if (cl.nodrift || !cl.pitchvel)
131         {
132                 cl.pitchvel = v_centerspeed.value;
133                 cl.nodrift = false;
134                 cl.driftmove = 0;
135         }
136 }
137
138 void V_StopPitchDrift (void)
139 {
140         cl.laststop = cl.time;
141         cl.nodrift = true;
142         cl.pitchvel = 0;
143 }
144
145 /*
146 ===============
147 V_DriftPitch
148
149 Moves the client pitch angle towards cl.idealpitch sent by the server.
150
151 If the user is adjusting pitch manually, either with lookup/lookdown,
152 mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
153
154 Drifting is enabled when the center view key is hit, mlook is released and
155 lookspring is non 0, or when
156 ===============
157 */
158 static void V_DriftPitch (void)
159 {
160         float           delta, move;
161
162         if (noclip_anglehack || !cl.onground || cls.demoplayback )
163         {
164                 cl.driftmove = 0;
165                 cl.pitchvel = 0;
166                 return;
167         }
168
169 // don't count small mouse motion
170         if (cl.nodrift)
171         {
172                 if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
173                         cl.driftmove = 0;
174                 else
175                         cl.driftmove += cl.frametime;
176
177                 if ( cl.driftmove > v_centermove.value)
178                 {
179                         V_StartPitchDrift ();
180                 }
181                 return;
182         }
183
184         delta = cl.idealpitch - cl.viewangles[PITCH];
185
186         if (!delta)
187         {
188                 cl.pitchvel = 0;
189                 return;
190         }
191
192         move = cl.frametime * cl.pitchvel;
193         cl.pitchvel += cl.frametime * v_centerspeed.value;
194
195 //Con_Printf ("move: %f (%f)\n", move, cl.frametime);
196
197         if (delta > 0)
198         {
199                 if (move > delta)
200                 {
201                         cl.pitchvel = 0;
202                         move = delta;
203                 }
204                 cl.viewangles[PITCH] += move;
205         }
206         else if (delta < 0)
207         {
208                 if (move > -delta)
209                 {
210                         cl.pitchvel = 0;
211                         move = -delta;
212                 }
213                 cl.viewangles[PITCH] -= move;
214         }
215 }
216
217
218
219
220
221 /*
222 ==============================================================================
223
224                                                 SCREEN FLASHES
225
226 ==============================================================================
227 */
228
229
230 /*
231 ===============
232 V_ParseDamage
233 ===============
234 */
235 void V_ParseDamage (void)
236 {
237         int i, armor, blood;
238         vec3_t from, forward, right;
239         entity_t *ent;
240         float side, count;
241
242         armor = MSG_ReadByte ();
243         blood = MSG_ReadByte ();
244         for (i=0 ; i<3 ; i++)
245                 from[i] = MSG_ReadCoord ();
246
247         count = blood*0.5 + armor*0.5;
248         if (count < 10)
249                 count = 10;
250
251         cl.faceanimtime = cl.time + 0.2;                // put sbar face into pain frame
252
253         cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
254         if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
255                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
256         if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
257                 cl.cshifts[CSHIFT_DAMAGE].percent = 150;
258
259         if (armor > blood)
260         {
261                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
262                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
263                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
264         }
265         else if (armor)
266         {
267                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
268                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
269                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
270         }
271         else
272         {
273                 cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
274                 cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
275                 cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
276         }
277
278 //
279 // calculate view angle kicks
280 //
281         ent = &cl_entities[cl.viewentity];
282
283         VectorSubtract (from, ent->render.origin, from);
284         VectorNormalize (from);
285
286         AngleVectors (ent->render.angles, forward, right, NULL);
287
288         side = DotProduct (from, right);
289         v_dmg_roll = count*side*v_kickroll.value;
290
291         side = DotProduct (from, forward);
292         v_dmg_pitch = count*side*v_kickpitch.value;
293
294         v_dmg_time = v_kicktime.value;
295 }
296
297 static cshift_t v_cshift;
298
299 /*
300 ==================
301 V_cshift_f
302 ==================
303 */
304 static void V_cshift_f (void)
305 {
306         v_cshift.destcolor[0] = atoi(Cmd_Argv(1));
307         v_cshift.destcolor[1] = atoi(Cmd_Argv(2));
308         v_cshift.destcolor[2] = atoi(Cmd_Argv(3));
309         v_cshift.percent = atoi(Cmd_Argv(4));
310 }
311
312
313 /*
314 ==================
315 V_BonusFlash_f
316
317 When you run over an item, the server sends this command
318 ==================
319 */
320 static void V_BonusFlash_f (void)
321 {
322         cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
323         cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
324         cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
325         cl.cshifts[CSHIFT_BONUS].percent = 50;
326 }
327
328 /*
329 =============
330 V_UpdateBlends
331 =============
332 */
333 void V_UpdateBlends (void)
334 {
335         float   r, g, b, a, a2;
336         int             j;
337
338         if (cl.worldmodel == NULL)
339         {
340                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
341                 cl.cshifts[CSHIFT_BONUS].percent = 0;
342                 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
343                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
344                 r_refdef.viewblend[0] = 0;
345                 r_refdef.viewblend[1] = 0;
346                 r_refdef.viewblend[2] = 0;
347                 r_refdef.viewblend[3] = 0;
348                 return;
349         }
350
351         // drop the damage value
352         cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
353         if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
354                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
355
356         // drop the bonus value
357         cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
358         if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
359                 cl.cshifts[CSHIFT_BONUS].percent = 0;
360
361         // set contents color
362         switch (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel)->contents)
363         {
364         case CONTENTS_EMPTY:
365         case CONTENTS_SOLID:
366                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
367                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
368                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
369                 cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
370                 break;
371         case CONTENTS_LAVA:
372                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
373                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
374                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
375                 cl.cshifts[CSHIFT_CONTENTS].percent = 150;
376                 break;
377         case CONTENTS_SLIME:
378                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
379                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
380                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
381                 cl.cshifts[CSHIFT_CONTENTS].percent = 150;
382                 break;
383         default:
384                 cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
385                 cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
386                 cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
387                 cl.cshifts[CSHIFT_CONTENTS].percent = 128;
388         }
389
390         if (cl.items & IT_QUAD)
391         {
392                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
393                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
394                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
395                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
396         }
397         else if (cl.items & IT_SUIT)
398         {
399                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
400                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
401                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
402                 cl.cshifts[CSHIFT_POWERUP].percent = 20;
403         }
404         else if (cl.items & IT_INVISIBILITY)
405         {
406                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
407                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
408                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
409                 cl.cshifts[CSHIFT_POWERUP].percent = 100;
410         }
411         else if (cl.items & IT_INVULNERABILITY)
412         {
413                 cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
414                 cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
415                 cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
416                 cl.cshifts[CSHIFT_POWERUP].percent = 30;
417         }
418         else
419                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
420
421         // LordHavoc: fixed V_CalcBlend
422         r = 0;
423         g = 0;
424         b = 0;
425         a = 0;
426
427         for (j=0 ; j<NUM_CSHIFTS ; j++)
428         {
429                 a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
430
431                 if (a2 < 0)
432                         continue;
433                 if (a2 > 1)
434                         a2 = 1;
435                 r += (cl.cshifts[j].destcolor[0]-r) * a2;
436                 g += (cl.cshifts[j].destcolor[1]-g) * a2;
437                 b += (cl.cshifts[j].destcolor[2]-b) * a2;
438                 a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
439         }
440         // saturate color (to avoid blending in black)
441         if (a)
442         {
443                 a2 = 1 / a;
444                 r *= a2;
445                 g *= a2;
446                 b *= a2;
447         }
448
449         r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
450         r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
451         r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
452         r_refdef.viewblend[3] = bound(0, a              , 1);
453 }
454
455 /*
456 ==============================================================================
457
458                                                 VIEW RENDERING
459
460 ==============================================================================
461 */
462
463 /*
464 ==============
465 V_AddIdle
466
467 Idle swaying
468 ==============
469 */
470 static void V_AddIdle (float idle)
471 {
472         r_refdef.viewangles[ROLL] += idle * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
473         r_refdef.viewangles[PITCH] += idle * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
474         r_refdef.viewangles[YAW] += idle * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
475 }
476
477
478 /*
479 ==================
480 V_CalcRefdef
481
482 ==================
483 */
484 void V_CalcRefdef (void)
485 {
486         entity_t        *ent, *view;
487         vec3_t          forward;
488         vec3_t          angles;
489         float           bob;
490         float           side;
491
492         if (cls.state != ca_connected || !cl.worldmodel)
493                 return;
494
495         // ent is the player model (visible when out of body)
496         ent = &cl_entities[cl.viewentity];
497         // view is the weapon model (only visible from inside body)
498         view = &cl.viewent;
499
500         V_DriftPitch ();
501
502         VectorCopy (ent->render.origin, r_refdef.vieworg);
503         if (!intimerefresh)
504                 VectorCopy (cl.viewangles, r_refdef.viewangles);
505
506         if (cl.intermission)
507         {
508                 view->render.model = NULL;
509                 V_AddIdle (1);
510         }
511         else if (chase_active.value)
512         {
513                 Chase_Update ();
514                 V_AddIdle (v_idlescale.value);
515         }
516         else
517         {
518                 side = V_CalcRoll (cl_entities[cl.viewentity].render.angles, cl.velocity);
519                 r_refdef.viewangles[ROLL] += side;
520
521                 if (v_dmg_time > 0)
522                 {
523                         r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
524                         r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
525                         v_dmg_time -= cl.frametime;
526                 }
527
528                 if (cl.stats[STAT_HEALTH] <= 0)
529                         r_refdef.viewangles[ROLL] = 80; // dead view angle
530
531                 V_AddIdle (v_idlescale.value);
532
533                 // offsets
534                 angles[PITCH] = -ent->render.angles[PITCH];     // because entity pitches are actually backward
535                 angles[YAW] = ent->render.angles[YAW];
536                 angles[ROLL] = ent->render.angles[ROLL];
537
538                 AngleVectors (angles, forward, NULL, NULL);
539
540                 bob = V_CalcBob ();
541
542                 r_refdef.vieworg[2] += cl.viewheight + bob;
543
544                 // set up gun
545                 view->state_current.modelindex = cl.stats[STAT_WEAPON];
546                 view->state_current.frame = cl.stats[STAT_WEAPONFRAME];
547                 view->render.origin[0] = ent->render.origin[0] + bob * 0.4 * forward[0];
548                 view->render.origin[1] = ent->render.origin[1] + bob * 0.4 * forward[1];
549                 view->render.origin[2] = ent->render.origin[2] + bob * 0.4 * forward[2] + cl.viewheight + bob;
550                 view->render.angles[PITCH] = -r_refdef.viewangles[PITCH] - v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
551                 view->render.angles[YAW] = r_refdef.viewangles[YAW] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
552                 view->render.angles[ROLL] = r_refdef.viewangles[ROLL] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
553                 // FIXME: this setup code is somewhat evil (CL_LerpUpdate should be private?)
554                 CL_LerpUpdate(view);
555                 view->render.colormap = -1; // no special coloring
556                 view->render.alpha = ent->render.alpha; // LordHavoc: if the player is transparent, so is the gun
557                 view->render.effects = ent->render.effects;
558                 view->render.scale = 1;
559
560                 // LordHavoc: origin view kick added
561                 if (!intimerefresh)
562                 {
563                         VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
564                         VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
565                 }
566
567                 // copy to refdef
568                 r_refdef.viewent = view->render;
569         }
570 }
571
572 //============================================================================
573
574 /*
575 =============
576 V_Init
577 =============
578 */
579 void V_Init (void)
580 {
581         Cmd_AddCommand ("v_cshift", V_cshift_f);
582         Cmd_AddCommand ("bf", V_BonusFlash_f);
583         Cmd_AddCommand ("centerview", V_StartPitchDrift);
584
585         Cvar_RegisterVariable (&v_centermove);
586         Cvar_RegisterVariable (&v_centerspeed);
587
588         Cvar_RegisterVariable (&v_iyaw_cycle);
589         Cvar_RegisterVariable (&v_iroll_cycle);
590         Cvar_RegisterVariable (&v_ipitch_cycle);
591         Cvar_RegisterVariable (&v_iyaw_level);
592         Cvar_RegisterVariable (&v_iroll_level);
593         Cvar_RegisterVariable (&v_ipitch_level);
594
595         Cvar_RegisterVariable (&v_idlescale);
596         Cvar_RegisterVariable (&crosshair);
597
598         Cvar_RegisterVariable (&cl_rollspeed);
599         Cvar_RegisterVariable (&cl_rollangle);
600         Cvar_RegisterVariable (&cl_bob);
601         Cvar_RegisterVariable (&cl_bobcycle);
602         Cvar_RegisterVariable (&cl_bobup);
603
604         Cvar_RegisterVariable (&v_kicktime);
605         Cvar_RegisterVariable (&v_kickroll);
606         Cvar_RegisterVariable (&v_kickpitch);
607 }
608
609