moved all type-specific model fields to respective structures (alias, sprite, brush)
[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_bob.value && cl_bobcycle.value)
353                         {
354                                 double bob, cycle;
355                                 // LordHavoc: this code is *weird*, but not replacable (I think it
356                                 // should be done in QC on the server, but oh well, quake is quake)
357                                 // LordHavoc: figured out bobup: the time at which the sin is at 180
358                                 // degrees (which allows lengthening or squishing the peak or valley)
359                                 cycle = cl.time / cl_bobcycle.value;
360                                 cycle -= (int) cycle;
361                                 if (cycle < cl_bobup.value)
362                                         cycle = sin(M_PI * cycle / cl_bobup.value);
363                                 else
364                                         cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
365                                 // bob is proportional to velocity in the xy plane
366                                 // (don't count Z, or jumping messes it up)
367                                 bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
368                                 bob = bob*0.3 + bob*0.7*cycle;
369                                 r_refdef.vieworg[2] += bound(-7, bob, 4);
370                         }
371                         // link the delayed viewmodel entities
372                         if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY) && cl.stats[STAT_HEALTH] > 0)
373                         {
374                                 int i;
375                                 entity_t *ent;
376                                 matrix4x4_t matrix, matrix2;
377                                 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);
378                                 for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
379                                 {
380                                         ent = viewmodels[i];
381                                         r_refdef.entities[r_refdef.numentities++] = &ent->render;
382                                         matrix2 = ent->render.matrix;
383                                         Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
384                                         Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
385                                         CL_BoundingBoxForEntity(&ent->render);
386                                 }
387                         }
388                 }
389
390                 // drop the damage value
391                 cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
392                 if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
393                         cl.cshifts[CSHIFT_DAMAGE].percent = 0;
394
395                 // drop the bonus value
396                 cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
397                 if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
398                         cl.cshifts[CSHIFT_BONUS].percent = 0;
399
400                 // set contents color
401                 switch (CL_PointContents(r_refdef.vieworg))
402                 {
403                 case CONTENTS_EMPTY:
404                 case CONTENTS_SOLID:
405                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = v_cshift.destcolor[0];
406                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = v_cshift.destcolor[1];
407                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = v_cshift.destcolor[2];
408                         cl.cshifts[CSHIFT_CONTENTS].percent = v_cshift.percent;
409                         break;
410                 case CONTENTS_LAVA:
411                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 255;
412                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
413                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;
414                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
415                         break;
416                 case CONTENTS_SLIME:
417                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
418                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 25;
419                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 5;
420                         cl.cshifts[CSHIFT_CONTENTS].percent = 150 >> 1;
421                         break;
422                 default:
423                         cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 130;
424                         cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 80;
425                         cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 50;
426                         cl.cshifts[CSHIFT_CONTENTS].percent = 128 >> 1;
427                 }
428
429                 if (cl.items & IT_QUAD)
430                 {
431                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
432                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
433                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
434                         cl.cshifts[CSHIFT_POWERUP].percent = 30;
435                 }
436                 else if (cl.items & IT_SUIT)
437                 {
438                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
439                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
440                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
441                         cl.cshifts[CSHIFT_POWERUP].percent = 20;
442                 }
443                 else if (cl.items & IT_INVISIBILITY)
444                 {
445                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
446                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
447                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
448                         cl.cshifts[CSHIFT_POWERUP].percent = 100;
449                 }
450                 else if (cl.items & IT_INVULNERABILITY)
451                 {
452                         cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
453                         cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
454                         cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
455                         cl.cshifts[CSHIFT_POWERUP].percent = 30;
456                 }
457                 else
458                         cl.cshifts[CSHIFT_POWERUP].percent = 0;
459
460                 // LordHavoc: fixed V_CalcBlend
461                 r = 0;
462                 g = 0;
463                 b = 0;
464                 a = 0;
465
466                 for (j=0 ; j<NUM_CSHIFTS ; j++)
467                 {
468                         a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
469
470                         if (a2 < 0)
471                                 continue;
472                         if (a2 > 1)
473                                 a2 = 1;
474                         r += (cl.cshifts[j].destcolor[0]-r) * a2;
475                         g += (cl.cshifts[j].destcolor[1]-g) * a2;
476                         b += (cl.cshifts[j].destcolor[2]-b) * a2;
477                         a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
478                 }
479                 // saturate color (to avoid blending in black)
480                 if (a)
481                 {
482                         a2 = 1 / a;
483                         r *= a2;
484                         g *= a2;
485                         b *= a2;
486                 }
487
488                 r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
489                 r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
490                 r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
491                 r_refdef.viewblend[3] = bound(0, a              , 1);
492         }
493         else
494         {
495                 cl.cshifts[CSHIFT_DAMAGE].percent = 0;
496                 cl.cshifts[CSHIFT_BONUS].percent = 0;
497                 cl.cshifts[CSHIFT_CONTENTS].percent = 0;
498                 cl.cshifts[CSHIFT_POWERUP].percent = 0;
499                 r_refdef.viewblend[0] = 0;
500                 r_refdef.viewblend[1] = 0;
501                 r_refdef.viewblend[2] = 0;
502                 r_refdef.viewblend[3] = 0;
503         }
504 }
505
506 //============================================================================
507
508 /*
509 =============
510 V_Init
511 =============
512 */
513 void V_Init (void)
514 {
515         Cmd_AddCommand ("v_cshift", V_cshift_f);
516         Cmd_AddCommand ("bf", V_BonusFlash_f);
517         Cmd_AddCommand ("centerview", V_StartPitchDrift);
518
519         Cvar_RegisterVariable (&v_centermove);
520         Cvar_RegisterVariable (&v_centerspeed);
521
522         Cvar_RegisterVariable (&v_iyaw_cycle);
523         Cvar_RegisterVariable (&v_iroll_cycle);
524         Cvar_RegisterVariable (&v_ipitch_cycle);
525         Cvar_RegisterVariable (&v_iyaw_level);
526         Cvar_RegisterVariable (&v_iroll_level);
527         Cvar_RegisterVariable (&v_ipitch_level);
528
529         Cvar_RegisterVariable (&v_idlescale);
530         Cvar_RegisterVariable (&crosshair);
531
532         Cvar_RegisterVariable (&cl_rollspeed);
533         Cvar_RegisterVariable (&cl_rollangle);
534         Cvar_RegisterVariable (&cl_bob);
535         Cvar_RegisterVariable (&cl_bobcycle);
536         Cvar_RegisterVariable (&cl_bobup);
537
538         Cvar_RegisterVariable (&v_kicktime);
539         Cvar_RegisterVariable (&v_kickroll);
540         Cvar_RegisterVariable (&v_kickpitch);
541 }
542