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