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