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