added in_pitch_min and in_pitch_max cvars to limit pitch (default: -90 to +90)
[divverent/darkplaces.git] / cl_tent.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 // cl_tent.c -- client side temporary entities
21
22 #include "quakedef.h"
23
24 int                     num_temp_entities;
25 entity_t        cl_temp_entities[MAX_TEMP_ENTITIES];
26 beam_t          cl_beams[MAX_BEAMS];
27
28 model_t         *cl_model_bolt = NULL;
29 model_t         *cl_model_bolt2 = NULL;
30 model_t         *cl_model_bolt3 = NULL;
31 model_t         *cl_model_beam = NULL;
32
33 sfx_t           *cl_sfx_wizhit;
34 sfx_t           *cl_sfx_knighthit;
35 sfx_t           *cl_sfx_tink1;
36 sfx_t           *cl_sfx_ric1;
37 sfx_t           *cl_sfx_ric2;
38 sfx_t           *cl_sfx_ric3;
39 sfx_t           *cl_sfx_r_exp3;
40
41 /*
42 =================
43 CL_ParseTEnt
44 =================
45 */
46 void CL_InitTEnts (void)
47 {
48         cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
49         cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
50         cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
51         cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
52         cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
53         cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
54         cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
55 }
56
57 /*
58 =================
59 CL_ParseBeam
60 =================
61 */
62 void CL_ParseBeam (model_t *m)
63 {
64         int             ent;
65         vec3_t  start, end;
66         beam_t  *b;
67         int             i;
68
69         ent = MSG_ReadShort ();
70         MSG_ReadVector(start);
71         MSG_ReadVector(end);
72
73 // override any beam with the same entity
74         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
75                 if (b->entity == ent)
76                 {
77                         b->entity = ent;
78                         b->model = m;
79                         b->endtime = cl.time + 0.2;
80                         VectorCopy (start, b->start);
81                         VectorCopy (end, b->end);
82                         return;
83                 }
84
85 // find a free beam
86         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
87         {
88                 if (!b->model || b->endtime < cl.time)
89                 {
90                         b->entity = ent;
91                         b->model = m;
92                         b->endtime = cl.time + 0.2;
93                         VectorCopy (start, b->start);
94                         VectorCopy (end, b->end);
95                         return;
96                 }
97         }
98         Con_Printf ("beam list overflow!\n");   
99 }
100
101
102 /*
103 =================
104 CL_ParseTEnt
105 =================
106 */
107 void CL_ParseTEnt (void)
108 {
109         int             type;
110         vec3_t  pos;
111         vec3_t  dir;
112         vec3_t  pos2;
113         vec3_t  color;
114         int             rnd;
115         int             colorStart, colorLength, count;
116         float   velspeed, radius;
117         byte *tempcolor;
118
119         type = MSG_ReadByte ();
120         switch (type)
121         {
122         case TE_WIZSPIKE:                       // spike hitting wall
123                 MSG_ReadVector(pos);
124                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
125                 CL_RunParticleEffect (pos, vec3_origin, 20, 30);
126                 S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
127                 break;
128
129         case TE_KNIGHTSPIKE:                    // spike hitting wall
130                 MSG_ReadVector(pos);
131                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
132                 CL_RunParticleEffect (pos, vec3_origin, 226, 20);
133                 S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
134                 break;
135
136         case TE_SPIKE:                  // spike hitting wall
137                 MSG_ReadVector(pos);
138                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
139                 // LordHavoc: changed to spark shower
140                 CL_SparkShower(pos, vec3_origin, 15);
141                 //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
142                 if ( rand() % 5 )
143                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
144                 else
145                 {
146                         rnd = rand() & 3;
147                         if (rnd == 1)
148                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
149                         else if (rnd == 2)
150                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
151                         else
152                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
153                 }
154                 break;
155         case TE_SPIKEQUAD:                      // quad spike hitting wall
156                 MSG_ReadVector(pos);
157                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
158                 // LordHavoc: changed to spark shower
159                 CL_SparkShower(pos, vec3_origin, 15);
160                 //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
161                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
162                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
163                 if ( rand() % 5 )
164                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
165                 else
166                 {
167                         rnd = rand() & 3;
168                         if (rnd == 1)
169                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
170                         else if (rnd == 2)
171                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
172                         else
173                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
174                 }
175                 break;
176         case TE_SUPERSPIKE:                     // super spike hitting wall
177                 MSG_ReadVector(pos);
178                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
179                 // LordHavoc: changed to dust shower
180                 CL_SparkShower(pos, vec3_origin, 30);
181                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
182                 if ( rand() % 5 )
183                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
184                 else
185                 {
186                         rnd = rand() & 3;
187                         if (rnd == 1)
188                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
189                         else if (rnd == 2)
190                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
191                         else
192                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
193                 }
194                 break;
195         case TE_SUPERSPIKEQUAD:                 // quad super spike hitting wall
196                 MSG_ReadVector(pos);
197                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
198                 // LordHavoc: changed to dust shower
199                 CL_SparkShower(pos, vec3_origin, 30);
200                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
201                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
202                 if ( rand() % 5 )
203                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
204                 else
205                 {
206                         rnd = rand() & 3;
207                         if (rnd == 1)
208                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
209                         else if (rnd == 2)
210                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
211                         else
212                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
213                 }
214                 break;
215                 // LordHavoc: added for improved blood splatters
216         case TE_BLOOD:  // blood puff
217                 MSG_ReadVector(pos);
218                 dir[0] = MSG_ReadChar ();
219                 dir[1] = MSG_ReadChar ();
220                 dir[2] = MSG_ReadChar ();
221                 count = MSG_ReadByte (); // amount of particles
222                 CL_BloodPuff(pos, dir, count);
223                 break;
224         case TE_BLOOD2: // blood puff
225                 MSG_ReadVector(pos);
226                 CL_BloodPuff(pos, vec3_origin, 10);
227                 break;
228         case TE_SPARK:  // spark shower
229                 MSG_ReadVector(pos);
230                 dir[0] = MSG_ReadChar ();
231                 dir[1] = MSG_ReadChar ();
232                 dir[2] = MSG_ReadChar ();
233                 count = MSG_ReadByte (); // amount of particles
234                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
235                 CL_SparkShower(pos, dir, count);
236                 break;
237         case TE_PLASMABURN:
238                 MSG_ReadVector(pos);
239                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
240                 CL_AllocDlight (NULL, pos, 200, 1, 1, 1, 1000, 0.2);
241                 CL_PlasmaBurn(pos);
242                 break;
243                 // LordHavoc: added for improved gore
244         case TE_BLOODSHOWER:    // vaporized body
245                 MSG_ReadVector(pos); // mins
246                 MSG_ReadVector(pos2); // maxs
247                 velspeed = MSG_ReadCoord (); // speed
248                 count = MSG_ReadShort (); // number of particles
249                 CL_BloodShower(pos, pos2, velspeed, count);
250                 break;
251         case TE_PARTICLECUBE:   // general purpose particle effect
252                 MSG_ReadVector(pos); // mins
253                 MSG_ReadVector(pos2); // maxs
254                 MSG_ReadVector(dir); // dir
255                 count = MSG_ReadShort (); // number of particles
256                 colorStart = MSG_ReadByte (); // color
257                 colorLength = MSG_ReadByte (); // gravity (1 or 0)
258                 velspeed = MSG_ReadCoord (); // randomvel
259                 CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
260                 break;
261
262         case TE_PARTICLERAIN:   // general purpose particle effect
263                 MSG_ReadVector(pos); // mins
264                 MSG_ReadVector(pos2); // maxs
265                 MSG_ReadVector(dir); // dir
266                 count = MSG_ReadShort (); // number of particles
267                 colorStart = MSG_ReadByte (); // color
268                 CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
269                 break;
270
271         case TE_PARTICLESNOW:   // general purpose particle effect
272                 MSG_ReadVector(pos); // mins
273                 MSG_ReadVector(pos2); // maxs
274                 MSG_ReadVector(dir); // dir
275                 count = MSG_ReadShort (); // number of particles
276                 colorStart = MSG_ReadByte (); // color
277                 CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
278                 break;
279
280         case TE_GUNSHOT:                        // bullet hitting wall
281                 MSG_ReadVector(pos);
282                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
283                 // LordHavoc: changed to dust shower
284                 CL_SparkShower(pos, vec3_origin, 15);
285                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
286                 break;
287
288         case TE_GUNSHOTQUAD:                    // quad bullet hitting wall
289                 MSG_ReadVector(pos);
290                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
291                 CL_SparkShower(pos, vec3_origin, 15);
292                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
293                 break;
294
295         case TE_EXPLOSION:                      // rocket explosion
296                 MSG_ReadVector(pos);
297                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
298                 CL_ParticleExplosion (pos, false);
299 //              CL_BlastParticles (pos, 120, 120);
300                 // LordHavoc: boosted color from 1.0, 0.8, 0.4 to 1.25, 1.0, 0.5
301                 CL_AllocDlight (NULL, pos, 350, 1.25f, 1.0f, 0.5f, 700, 0.5);
302                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
303                 break;
304
305         case TE_EXPLOSIONQUAD:                  // quad rocket explosion
306                 MSG_ReadVector(pos);
307                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
308                 CL_ParticleExplosion (pos, false);
309 //              CL_BlastParticles (pos, 120, 480);
310                 CL_AllocDlight (NULL, pos, 600, 0.5f, 0.4f, 1.0f, 1200, 0.5);
311                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
312                 break;
313
314                 /*
315         case TE_SMOKEEXPLOSION:                 // rocket explosion with a cloud of smoke
316                 MSG_ReadVector(pos);
317                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
318                 CL_ParticleExplosion (pos, true);
319                 CL_AllocDlight (NULL, pos, 350, 1.0f, 0.8f, 0.4f, 700, 0.5);
320                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
321                 break;
322                 */
323
324         case TE_EXPLOSION3:                             // Nehahra movie colored lighting explosion
325                 MSG_ReadVector(pos);
326                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
327                 CL_ParticleExplosion (pos, false);
328 //              CL_BlastParticles (pos, 120, 120);
329                 CL_AllocDlight (NULL, pos, 350, MSG_ReadCoord(), MSG_ReadCoord(), MSG_ReadCoord(), 700, 0.5);
330                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
331                 break;
332
333         case TE_EXPLOSIONRGB:                   // colored lighting explosion
334                 MSG_ReadVector(pos);
335                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
336                 CL_ParticleExplosion (pos, false);
337 //              CL_BlastParticles (pos, 120, 120);
338                 color[0] = MSG_ReadByte() * (1.0 / 255.0);
339                 color[1] = MSG_ReadByte() * (1.0 / 255.0);
340                 color[2] = MSG_ReadByte() * (1.0 / 255.0);
341                 CL_AllocDlight (NULL, pos, 350, color[0], color[1], color[2], 700, 0.5);
342                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
343                 break;
344
345         case TE_TAREXPLOSION:                   // tarbaby explosion
346                 MSG_ReadVector(pos);
347                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
348                 CL_BlobExplosion (pos);
349 //              CL_BlastParticles (pos, 120, 120);
350
351                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
352                 CL_AllocDlight (NULL, pos, 600, 0.8f, 0.4f, 1.0f, 1200, 0.5);
353                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
354                 break;
355
356         case TE_SMALLFLASH:
357                 MSG_ReadVector(pos);
358                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
359                 CL_AllocDlight (NULL, pos, 200, 1, 1, 1, 1000, 0.2);
360                 break;
361
362         case TE_CUSTOMFLASH:
363                 MSG_ReadVector(pos);
364                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
365                 radius = MSG_ReadByte() * 8;
366                 velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
367                 color[0] = MSG_ReadByte() * (1.0 / 255.0);
368                 color[1] = MSG_ReadByte() * (1.0 / 255.0);
369                 color[2] = MSG_ReadByte() * (1.0 / 255.0);
370                 CL_AllocDlight (NULL, pos, radius, color[0], color[1], color[2], radius / velspeed, velspeed);
371                 break;
372
373         case TE_FLAMEJET:
374                 MSG_ReadVector(pos);
375                 MSG_ReadVector(dir);
376                 count = MSG_ReadByte();
377                 CL_Flames(pos, dir, count);
378                 break;
379
380         case TE_LIGHTNING1:                             // lightning bolts
381                 if (!cl_model_bolt)
382                         cl_model_bolt = Mod_ForName("progs/bolt.mdl", true, false, false);
383                 CL_ParseBeam (cl_model_bolt);
384                 break;
385
386         case TE_LIGHTNING2:                             // lightning bolts
387                 if (!cl_model_bolt2)
388                         cl_model_bolt2 = Mod_ForName("progs/bolt2.mdl", true, false, false);
389                 CL_ParseBeam (cl_model_bolt2);
390                 break;
391
392         case TE_LIGHTNING3:                             // lightning bolts
393                 if (!cl_model_bolt3)
394                         cl_model_bolt3 = Mod_ForName("progs/bolt3.mdl", true, false, false);
395                 CL_ParseBeam (cl_model_bolt3);
396                 break;
397
398 // PGM 01/21/97
399         case TE_BEAM:                           // grappling hook beam
400                 if (!cl_model_beam)
401                         cl_model_beam = Mod_ForName("progs/beam.mdl", true, false, false);
402                 CL_ParseBeam (cl_model_beam);
403                 break;
404 // PGM 01/21/97
405
406 // LordHavoc: for compatibility with the Nehahra movie...
407         case TE_LIGHTNING4NEH:
408                 CL_ParseBeam (Mod_ForName(MSG_ReadString(), true, false, false));
409                 break;
410
411         case TE_LAVASPLASH:
412                 pos[0] = MSG_ReadCoord ();
413                 pos[1] = MSG_ReadCoord ();
414                 pos[2] = MSG_ReadCoord ();
415                 CL_LavaSplash (pos);
416                 break;
417
418         case TE_TELEPORT:
419                 pos[0] = MSG_ReadCoord ();
420                 pos[1] = MSG_ReadCoord ();
421                 pos[2] = MSG_ReadCoord ();
422                 CL_TeleportSplash (pos);
423                 break;
424
425         case TE_EXPLOSION2:                             // color mapped explosion
426                 MSG_ReadVector(pos);
427                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
428                 colorStart = MSG_ReadByte ();
429                 colorLength = MSG_ReadByte ();
430                 CL_ParticleExplosion2 (pos, colorStart, colorLength);
431 //              CL_BlastParticles (pos, 80, 80);
432                 tempcolor = (byte *)&d_8to24table[(rand()%colorLength) + colorStart];
433                 CL_AllocDlight (NULL, pos, 350, tempcolor[0] * (1.0f / 255.0f), tempcolor[1] * (1.0f / 255.0f), tempcolor[2] * (1.0f / 255.0f), 700, 0.5);
434                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
435                 break;
436
437         default:
438                 Host_Error ("CL_ParseTEnt: bad type %d", type);
439         }
440 }
441
442
443 /*
444 =================
445 CL_NewTempEntity
446 =================
447 */
448 entity_t *CL_NewTempEntity (void)
449 {
450         entity_t        *ent;
451
452         if (r_refdef.numentities >= MAX_VISEDICTS)
453                 return NULL;
454         if (num_temp_entities >= MAX_TEMP_ENTITIES)
455                 return NULL;
456         ent = &cl_temp_entities[num_temp_entities++];
457         memset (ent, 0, sizeof(*ent));
458         r_refdef.entities[r_refdef.numentities++] = &ent->render;
459
460         ent->render.colormap = -1; // no special coloring
461         ent->render.scale = 1;
462         ent->render.alpha = 1;
463         return ent;
464 }
465
466
467 /*
468 =================
469 CL_UpdateTEnts
470 =================
471 */
472 void CL_UpdateTEnts (void)
473 {
474         int                     i;
475         beam_t          *b;
476         vec3_t          dist, org;
477         float           d;
478         entity_t        *ent;
479         float           yaw, pitch;
480         float           forward;
481
482         num_temp_entities = 0;
483
484 // update lightning
485         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
486         {
487                 if (!b->model || b->endtime < cl.time)
488                         continue;
489
490                 // if coming from the player, update the start position
491                 if (b->entity == cl.viewentity)
492                         VectorCopy (cl_entities[cl.viewentity].render.origin, b->start);
493
494                 // calculate pitch and yaw
495                 VectorSubtract (b->end, b->start, dist);
496
497                 if (dist[1] == 0 && dist[0] == 0)
498                 {
499                         yaw = 0;
500                         if (dist[2] > 0)
501                                 pitch = 90;
502                         else
503                                 pitch = 270;
504                 }
505                 else
506                 {
507                         yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
508                         if (yaw < 0)
509                                 yaw += 360;
510         
511                         forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
512                         pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
513                         if (pitch < 0)
514                                 pitch += 360;
515                 }
516
517                 // add new entities for the lightning
518                 VectorCopy (b->start, org);
519                 d = VectorNormalizeLength(dist);
520                 while (d > 0)
521                 {
522                         ent = CL_NewTempEntity ();
523                         if (!ent)
524                                 return;
525                         VectorCopy (org, ent->render.origin);
526                         ent->render.model = b->model;
527                         ent->render.effects = EF_FULLBRIGHT;
528                         ent->render.angles[0] = pitch;
529                         ent->render.angles[1] = yaw;
530                         ent->render.angles[2] = rand()%360;
531                         VectorMA(org, 30, dist, org);
532                         d -= 30;
533                 }
534         }
535
536 }
537
538