]> icculus.org git repositories - divverent/darkplaces.git/blob - r_part.c
fix for blob particle crash
[divverent/darkplaces.git] / r_part.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
21 #include "quakedef.h"
22
23 #define MAX_PARTICLES                   4096    // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum {
28         pt_static, pt_grav, pt_slowgrav, pt_fire, /*pt_explode, pt_explode2, */pt_blob, pt_blob2,
29         pt_dust, pt_smoke, pt_snow, pt_bulletpuff, pt_bloodcloud, pt_fadespark, pt_fadespark2,
30         pt_fallfadespark, pt_fallfadespark2, pt_bubble, pt_fade, pt_smokecloud
31 } ptype_t;
32
33 typedef struct particle_s
34 {
35         vec3_t          org;
36         float           color;
37 //      struct particle_s       *next;
38         vec3_t          vel;
39         float           ramp;
40         float           die;
41         ptype_t         type;
42         // LordHavoc: added for improved particle effects
43         float           scale;
44         short           texnum;
45         float           alpha; // 0-255
46         float           time2; // used for various things (snow fluttering, for example)
47         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
48 } particle_t;
49
50 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
51 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
52 int             ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
53
54 int             particletexture;
55 int             smokeparticletexture[8];
56 int             flareparticletexture;
57 int             rainparticletexture;
58 int             bloodcloudparticletexture;
59 int             bubbleparticletexture;
60
61 particle_t      *particles;
62 int                     r_numparticles;
63
64 vec3_t                  r_pright, r_pup, r_ppn;
65
66 int                     numparticles;
67 particle_t      **freeparticles; // list used only in compacting particles array
68
69 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
70 #define ALLOCPARTICLE \
71         if (numparticles >= r_numparticles)\
72                 return;\
73         p = &particles[numparticles++];
74
75 cvar_t r_particles = {"r_particles", "1"};
76 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
77
78 void fractalnoise(char *noise, int size);
79 void fractalnoise_zeroedge(char *noise, int size);
80
81 void R_InitParticleTexture (void)
82 {
83         int             x,y,d,i;
84         float   dx, dy, dz, f, dot;
85         byte    data[32][32][4], noise1[32][32], noise2[32][32];
86         vec3_t  normal, light;
87
88         particletexture = texture_extension_number++;
89         glBindTexture(GL_TEXTURE_2D, particletexture);
90
91         for (x=0 ; x<32 ; x++)
92         {
93                 for (y=0 ; y<32 ; y++)
94                 {
95                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
96                         dx = x - 16;
97                         dy = y - 16;
98                         d = (255 - (dx*dx+dy*dy));
99                         if (d < 0) d = 0;
100                         data[y][x][3] = (byte) d;
101                 }
102         }
103         glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
104
105         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
106
107         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
108         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
109
110
111         for (i = 0;i < 8;i++)
112         {
113                 fractalnoise(&noise1[0][0], 32);
114                 fractalnoise(&noise2[0][0], 32);
115                 for (y = 0;y < 32;y++)
116                         for (x = 0;x < 32;x++)
117                         {
118                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
119                                 dx = x - 16;
120                                 dy = y - 16;
121                                 d = noise2[y][x] * 4 - 512;
122                                 if (d > 0)
123                                 {
124                                         if (d > 255)
125                                                 d = 255;
126                                         d = (d * (255 - (int) (dx*dx+dy*dy))) >> 8;
127                                         if (d < 0) d = 0;
128                                         if (d > 255) d = 255;
129                                         data[y][x][3] = (byte) d;
130                                 }
131                                 else
132                                         data[y][x][3] = 0;
133                         }
134
135                 /*
136                 for (x=0 ; x<34 ; x+=2)
137                         for (y=0 ; y<34 ; y+=2)
138                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = (rand()%32)+192;
139                 for (x=0 ; x<32 ; x+=2)
140                         for (y=0 ; y<32 ; y+=2)
141                         {
142                                 data[y  ][x+1][0] = data[y  ][x+1][1] = data[y  ][x+1][2] = (int) (data[y  ][x  ][0] + data[y  ][x+2][0]) >> 1;
143                                 data[y+1][x  ][0] = data[y+1][x  ][1] = data[y+1][x  ][2] = (int) (data[y  ][x  ][0] + data[y+2][x  ][0]) >> 1;
144                                 data[y+1][x+1][0] = data[y+1][x+1][1] = data[y+1][x+1][2] = (int) (data[y  ][x  ][0] + data[y  ][x+2][0] + data[y+2][x  ][0] + data[y+2][x+2][0]) >> 2;
145                         }
146                 for (x=0 ; x<32 ; x++)
147                 {
148                         for (y=0 ; y<32 ; y++)
149                         {
150                                 //data[y][x][0] = data[y][x][1] = data[y][x][2] = (rand()%192)+32;
151                                 dx = x - 16;
152                                 dy = y - 16;
153                                 d = (255 - (dx*dx+dy*dy));
154                                 if (d < 0) d = 0;
155                                 data[y][x][3] = (byte) d;
156                         }
157                 }
158                 */
159                 smokeparticletexture[i] = texture_extension_number++;
160                 glBindTexture(GL_TEXTURE_2D, smokeparticletexture[i]);
161                 glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
162                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
163                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
164                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165         }
166
167         fractalnoise(&noise1[0][0], 32);
168         fractalnoise(&noise2[0][0], 32);
169         for (y = 0;y < 32;y++)
170                 for (x = 0;x < 32;x++)
171                 {
172                         data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
173                         dx = x - 16;
174                         dy = y - 16;
175                         d = (noise2[y][x] * (255 - (dx*dx+dy*dy))) * (1.0f / 255.0f);
176                         if (d < 0) d = 0;
177                         if (d > 255) d = 255;
178                         data[y][x][3] = (byte) d;
179                 }
180
181         bloodcloudparticletexture = texture_extension_number++;
182         glBindTexture(GL_TEXTURE_2D, bloodcloudparticletexture);
183         glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
184         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
185         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
186         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
187
188         flareparticletexture = texture_extension_number++;
189         glBindTexture(GL_TEXTURE_2D, flareparticletexture);
190
191         for (x=0 ; x<32 ; x++)
192         {
193                 for (y=0 ; y<32 ; y++)
194                 {
195                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
196                         dx = x - 16;
197                         dy = y - 16;
198                         d = 2048 / (dx*dx+dy*dy+1) - 32;
199                         d = bound(0, d, 255);
200                         data[y][x][3] = (byte) d;
201                 }
202         }
203         glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
204
205         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
206
207         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
208         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
209
210         rainparticletexture = texture_extension_number++;
211         glBindTexture(GL_TEXTURE_2D, rainparticletexture);
212
213         for (x=0 ; x<32 ; x++)
214         {
215                 for (y=0 ; y<32 ; y++)
216                 {
217                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
218                         if (y < 24) // stretch the upper half to make a raindrop
219                         {
220                                 dx = (x - 16)*2;
221                                 dy = (y - 24)*2/3;
222                                 d = (255 - (dx*dx+dy*dy))/2;
223                         }
224                         else
225                         {
226                                 dx = (x - 16)*2;
227                                 dy = (y - 24)*2;
228                                 d = (255 - (dx*dx+dy*dy))/2;
229                         }
230                         if (d < 0) d = 0;
231                         data[y][x][3] = (byte) d;
232                 }
233         }
234         glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
235
236         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
237
238         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
239         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
240
241         bubbleparticletexture = texture_extension_number++;
242         glBindTexture(GL_TEXTURE_2D, bubbleparticletexture);
243
244         light[0] = 1;light[1] = 1;light[2] = 1;
245         VectorNormalize(light);
246         for (x=0 ; x<32 ; x++)
247         {
248                 for (y=0 ; y<32 ; y++)
249                 {
250                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
251                         dx = x * (1.0 / 16.0) - 1.0;
252                         dy = y * (1.0 / 16.0) - 1.0;
253                         if (dx*dx+dy*dy < 1) // it does hit the sphere
254                         {
255                                 dz = 1 - (dx*dx+dy*dy);
256                                 f = 0;
257                                 // back side
258                                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
259                                 VectorNormalize(normal);
260                                 dot = DotProduct(normal, light);
261                                 if (dot > 0.5) // interior reflection
262                                         f += ((dot *  2) - 1);
263                                 else if (dot < -0.5) // exterior reflection
264                                         f += ((dot * -2) - 1);
265                                 // front side
266                                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
267                                 VectorNormalize(normal);
268                                 dot = DotProduct(normal, light);
269                                 if (dot > 0.5) // interior reflection
270                                         f += ((dot *  2) - 1);
271                                 else if (dot < -0.5) // exterior reflection
272                                         f += ((dot * -2) - 1);
273                                 f *= 64;
274                                 f = bound(0, f, 255);
275                                 data[y][x][3] = (byte) f;
276                         }
277                         else
278                                 data[y][x][3] = 0;
279                 }
280         }
281         glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
282
283         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
284
285         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
286         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
287 }
288
289 /*
290 ===============
291 R_InitParticles
292 ===============
293 */
294 void R_InitParticles (void)
295 {
296         int             i;
297
298         i = COM_CheckParm ("-particles");
299
300         if (i)
301         {
302                 r_numparticles = (int)(atoi(com_argv[i+1]));
303                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
304                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
305         }
306         else
307         {
308                 r_numparticles = MAX_PARTICLES;
309         }
310
311         particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles");
312         freeparticles = (void *) Hunk_AllocName (r_numparticles * sizeof(particle_t *), "particles");
313
314         Cvar_RegisterVariable (&r_particles);
315         Cvar_RegisterVariable (&r_dynamicparticles);
316         R_InitParticleTexture ();
317 }
318
319 /*
320 ===============
321 R_EntityParticles
322 ===============
323 */
324
325 #define NUMVERTEXNORMALS        162
326 extern  float   r_avertexnormals[NUMVERTEXNORMALS][3];
327 vec3_t  avelocities[NUMVERTEXNORMALS];
328 float   beamlength = 16;
329 vec3_t  avelocity = {23, 7, 3};
330 float   partstep = 0.01;
331 float   timescale = 0.01;
332
333 void R_EntityParticles (entity_t *ent)
334 {
335         int                     count;
336         int                     i;
337         particle_t      *p;
338         float           angle;
339         float           sp, sy, cp, cy;
340         vec3_t          forward;
341         float           dist;
342         if (!r_particles.value) return; // LordHavoc: particles are optional
343         
344         dist = 64;
345         count = 50;
346
347 if (!avelocities[0][0])
348 {
349 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
350 avelocities[0][i] = (rand()&255) * 0.01;
351 }
352
353
354         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
355         {
356                 angle = cl.time * avelocities[i][0];
357                 sy = sin(angle);
358                 cy = cos(angle);
359                 angle = cl.time * avelocities[i][1];
360                 sp = sin(angle);
361                 cp = cos(angle);
362         
363                 forward[0] = cp*cy;
364                 forward[1] = cp*sy;
365                 forward[2] = -sp;
366
367                 ALLOCPARTICLE
368
369                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
370                 p->texnum = flareparticletexture;
371                 p->scale = 2;
372                 p->alpha = 255;
373                 p->die = cl.time;
374                 p->color = 0x6f;
375                 p->type = pt_static; //explode;
376                 
377                 p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength;                       
378                 p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength;                       
379                 p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength;                       
380         }
381 }
382
383
384 /*
385 ===============
386 R_ClearParticles
387 ===============
388 */
389 void R_ClearParticles (void)
390 {
391 //      int             i;
392 //      free_particles = &particles[0];
393 //      active_particles = NULL;
394
395 //      for (i=0 ;i<r_numparticles ; i++)
396 //              particles[i].next = &particles[i+1];
397 //      particles[r_numparticles-1].next = NULL;
398
399         numparticles = 0;
400 }
401
402
403 void R_ReadPointFile_f (void)
404 {
405         FILE    *f;
406         vec3_t  org;
407         int             r;
408         int             c;
409         particle_t      *p;
410         char    name[MAX_OSPATH];
411         
412         sprintf (name,"maps/%s.pts", sv.name);
413
414         COM_FOpenFile (name, &f, false);
415         if (!f)
416         {
417                 Con_Printf ("couldn't open %s\n", name);
418                 return;
419         }
420         
421         Con_Printf ("Reading %s...\n", name);
422         c = 0;
423         for ( ;; )
424         {
425                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
426                 if (r != 3)
427                         break;
428                 c++;
429                 
430 //              if (!free_particles)
431                 if (numparticles >= r_numparticles)
432                 {
433                         Con_Printf ("Not enough free particles\n");
434                         break;
435                 }
436                 ALLOCPARTICLE
437                 
438                 p->texnum = particletexture;
439                 p->scale = 2;
440                 p->alpha = 255;
441                 p->die = 99999;
442                 p->color = (-c)&15;
443                 p->type = pt_static;
444                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
445                 VectorCopy (org, p->org);
446         }
447
448         fclose (f);
449         Con_Printf ("%i points read\n", c);
450 }
451
452 /*
453 ===============
454 R_ParseParticleEffect
455
456 Parse an effect out of the server message
457 ===============
458 */
459 void R_ParseParticleEffect (void)
460 {
461         vec3_t          org, dir;
462         int                     i, count, msgcount, color;
463         
464         for (i=0 ; i<3 ; i++)
465                 org[i] = MSG_ReadCoord ();
466         for (i=0 ; i<3 ; i++)
467                 dir[i] = MSG_ReadChar () * (1.0/16);
468         msgcount = MSG_ReadByte ();
469         color = MSG_ReadByte ();
470
471 if (msgcount == 255)
472         count = 1024;
473 else
474         count = msgcount;
475         
476         R_RunParticleEffect (org, dir, color, count);
477 }
478         
479 /*
480 ===============
481 R_ParticleExplosion
482
483 ===============
484 */
485 void R_ParticleExplosion (vec3_t org, int smoke)
486 {
487         int                     i, j;
488         particle_t      *p;
489         if (!r_particles.value) return; // LordHavoc: particles are optional
490
491         /*
492         for (i=0 ; i<1024 ; i++)
493         {
494                 ALLOCPARTICLE
495
496                 p->texnum = particletexture;
497                 p->scale = lhrandom(1,3);
498                 p->alpha = rand()&255;
499                 p->die = cl.time + 5;
500                 p->color = ramp1[0];
501                 p->ramp = lhrandom(0, 4);
502 //              if (i & 1)
503 //                      p->type = pt_explode;
504 //              else
505 //                      p->type = pt_explode2;
506                 p->color = ramp1[rand()&7];
507                 p->type = pt_fallfadespark;
508                 for (j=0 ; j<3 ; j++)
509                 {
510                         p->org[j] = org[j] + lhrandom(-8,8);
511                         p->vel[j] = lhrandom(-192, 192);
512                 }
513                 p->vel[2] += 160;
514         }
515         */
516
517         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
518         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
519         {
520                 for (i=0 ; i<32 ; i++)
521                 {
522                         ALLOCPARTICLE
523
524                         p->texnum = bubbleparticletexture;
525                         p->scale = lhrandom(1,2);
526                         p->alpha = 255;
527                         p->color = (rand()&3)+12;
528                         p->type = pt_bubble;
529                         p->die = cl.time + 2;
530                         for (j=0 ; j<3 ; j++)
531                         {
532                                 p->org[j] = org[j] + lhrandom(-16,16);
533                                 p->vel[j] = lhrandom(-16,16);
534                         }
535                 }
536         }
537         else
538         {
539                 ALLOCPARTICLE
540
541                 p->texnum = smokeparticletexture[rand()&7];
542                 p->scale = 30;
543                 p->alpha = 96;
544                 p->die = cl.time + 2;
545                 p->type = pt_smokecloud;
546                 p->color = (rand()&7) + 8;
547                 for (j=0 ; j<3 ; j++)
548                 {
549                         p->org[j] = org[j];
550                         p->vel[j] = 0;
551                 }
552         }
553 }
554
555 /*
556 ===============
557 R_ParticleExplosion2
558
559 ===============
560 */
561 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
562 {
563         int                     i, j;
564         particle_t      *p;
565         int                     colorMod = 0;
566         if (!r_particles.value) return; // LordHavoc: particles are optional
567
568         for (i=0; i<512; i++)
569         {
570                 ALLOCPARTICLE
571
572                 p->texnum = particletexture;
573                 p->scale = 1.5;
574                 p->alpha = 255;
575                 p->die = cl.time + 0.3;
576                 p->color = colorStart + (colorMod % colorLength);
577                 colorMod++;
578
579                 p->type = pt_blob;
580                 for (j=0 ; j<3 ; j++)
581                 {
582                         p->org[j] = org[j] + ((rand()&15)-8);
583                         p->vel[j] = lhrandom(-192, 192);
584                 }
585         }
586 }
587
588 /*
589 ===============
590 R_BlobExplosion
591
592 ===============
593 */
594 void R_BlobExplosion (vec3_t org)
595 {
596         int                     i, j;
597         particle_t      *p;
598         if (!r_particles.value) return; // LordHavoc: particles are optional
599         
600         for (i=0 ; i<1024 ; i++)
601         {
602                 ALLOCPARTICLE
603
604                 p->texnum = particletexture;
605                 p->scale = 2;
606                 p->alpha = 255;
607                 p->die = cl.time + 1 + (rand()&8)*0.05;
608
609                 if (i & 1)
610                 {
611                         p->type = pt_blob;
612                         p->color = 66 + rand()%6;
613                         for (j=0 ; j<3 ; j++)
614                         {
615                                 p->org[j] = org[j] + ((rand()%32)-16);
616                                 p->vel[j] = lhrandom(-128, 128);
617                         }
618                 }
619                 else
620                 {
621                         p->type = pt_blob2;
622                         p->color = 150 + rand()%6;
623                         for (j=0 ; j<3 ; j++)
624                         {
625                                 p->org[j] = org[j] + ((rand()%32)-16);
626                                 p->vel[j] = lhrandom(-128, 128);
627                         }
628                 }
629                 p->vel[0] *= 0.25;
630                 p->vel[1] *= 0.25;
631         }
632 }
633
634 /*
635 ===============
636 R_RunParticleEffect
637
638 ===============
639 */
640 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
641 {
642         int                     j;
643         particle_t      *p;
644         if (!r_particles.value) return; // LordHavoc: particles are optional
645         
646         if (count == 1024)
647         {
648                 R_ParticleExplosion(org, false);
649                 return;
650         }
651         while (count)
652         {
653                 ALLOCPARTICLE
654                 if (count & 7)
655                 {
656                         p->alpha = (count & 7) * 16 + (rand()&15);
657                         count &= ~7;
658                 }
659                 else
660                 {
661                         p->alpha = 128;
662                         count -= 8;
663                 }
664
665                 p->texnum = particletexture;
666                 p->scale = 6;
667                 p->die = cl.time + 1; //lhrandom(0.1, 0.5);
668                 p->color = (color&~7) + (rand()&7);
669                 p->type = pt_fade; //static; //slowgrav;
670                 for (j=0 ; j<3 ; j++)
671                 {
672                         p->org[j] = org[j] + ((rand()&15)-8);
673                         p->vel[j] = dir[j]*15;// + (rand()%300)-150;
674                 }
675         }
676 }
677
678 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
679 /*
680 ===============
681 R_SparkShower
682
683 ===============
684 */
685 void R_SparkShower (vec3_t org, vec3_t dir, int count, int type)
686 {
687         int                     i, j;
688         particle_t      *p;
689         if (!r_particles.value) return; // LordHavoc: particles are optional
690
691         ALLOCPARTICLE
692         if (type == 0) // sparks
693         {
694                 p->texnum = smokeparticletexture[rand()&7];
695                 p->scale = 10;
696                 p->alpha = 48;
697                 p->color = (rand()&3)+12;
698                 p->type = pt_bulletpuff;
699                 p->die = cl.time + 1;
700                 VectorCopy(org, p->org);
701                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
702         }
703         else // blood
704         {
705                 p->texnum = smokeparticletexture[rand()&7];
706                 p->scale = 12;
707                 p->alpha = 128;
708                 p->color = (rand()&3)+68;
709                 p->type = pt_bloodcloud;
710                 p->die = cl.time + 0.5;
711                 VectorCopy(org, p->org);
712                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
713                 return;
714         }
715         for (i=0 ; i<count ; i++)
716         {
717                 ALLOCPARTICLE
718
719                 p->texnum = flareparticletexture;
720                 p->scale = 2;
721                 p->alpha = 192;
722                 p->die = cl.time + 0.0625 * (rand()&15);
723                 /*
724                 if (type == 0) // sparks
725                 {
726                 */
727                         p->type = pt_dust;
728                         p->ramp = (rand()&3);
729                         p->color = ramp1[(int)p->ramp];
730                         for (j=0 ; j<3 ; j++)
731                         {
732                                 p->org[j] = org[j] + ((rand()&7)-4);
733                                 p->vel[j] = dir[j] + (rand()%192)-96;
734                         }
735                 /*
736                 }
737                 else // blood
738                 {
739                         p->type = pt_fadespark2;
740                         p->color = 67 + (rand()&3);
741                         for (j=0 ; j<3 ; j++)
742                         {
743                                 p->org[j] = org[j] + (rand()&7)-4;
744                                 p->vel[j] = dir[j] + (rand()&63)-32;
745                         }
746                 }
747                 */
748         }
749 }
750
751 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
752 {
753         int                     i, j;
754         particle_t      *p;
755         vec3_t          diff;
756         vec3_t          center;
757         vec3_t          velscale;
758         if (!r_particles.value) return; // LordHavoc: particles are optional
759
760         VectorSubtract(maxs, mins, diff);
761         center[0] = (mins[0] + maxs[0]) * 0.5;
762         center[1] = (mins[1] + maxs[1]) * 0.5;
763         center[2] = (mins[2] + maxs[2]) * 0.5;
764         velscale[0] = velspeed * 2.0 / diff[0];
765         velscale[1] = velspeed * 2.0 / diff[1];
766         velscale[2] = velspeed * 2.0 / diff[2];
767         
768         for (i=0 ; i<count ; i++)
769         {
770                 ALLOCPARTICLE
771
772                 p->texnum = bloodcloudparticletexture;
773                 p->scale = 12;
774                 p->alpha = 96 + (rand()&63);
775                 p->die = cl.time + 2; //0.015625 * (rand()%128);
776                 p->type = pt_fadespark;
777                 p->color = (rand()&3)+68;
778 //              p->color = 67 + (rand()&3);
779                 for (j=0 ; j<3 ; j++)
780                 {
781                         p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
782                         p->vel[j] = (p->org[j] - center[j]) * velscale[j];
783                 }
784         }
785 }
786
787 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
788 {
789         int                     i, j;
790         particle_t      *p;
791         vec3_t          diff;
792         float           t;
793         if (!r_particles.value) return; // LordHavoc: particles are optional
794         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
795         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
796         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
797
798         VectorSubtract(maxs, mins, diff);
799         
800         for (i=0 ; i<count ; i++)
801         {
802                 ALLOCPARTICLE
803
804                 p->texnum = flareparticletexture;
805                 p->scale = 6;
806                 p->alpha = 255;
807                 p->die = cl.time + 1 + (rand()&15)*0.0625;
808                 if (gravity)
809                         p->type = pt_grav;
810                 else
811                         p->type = pt_static;
812                 p->color = colorbase + (rand()&3);
813                 for (j=0 ; j<3 ; j++)
814                 {
815                         p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
816                         if (randomvel)
817                                 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
818                         else
819                                 p->vel[j] = 0;
820                 }
821         }
822 }
823
824 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
825 {
826         int                     i;
827         particle_t      *p;
828         vec3_t          diff;
829         vec3_t          org;
830         vec3_t          vel;
831         float           t, z;
832         if (!r_particles.value) return; // LordHavoc: particles are optional
833         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
834         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
835         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
836         if (dir[2] < 0) // falling
837         {
838                 t = (maxs[2] - mins[2]) / -dir[2];
839                 z = maxs[2];
840         }
841         else // rising??
842         {
843                 t = (maxs[2] - mins[2]) / dir[2];
844                 z = mins[2];
845         }
846         if (t < 0 || t > 2) // sanity check
847                 t = 2;
848         t += cl.time;
849
850         VectorSubtract(maxs, mins, diff);
851         
852         for (i=0 ; i<count ; i++)
853         {
854                 ALLOCPARTICLE
855
856                 vel[0] = dir[0] + (rand()&31) - 16;
857                 vel[1] = dir[1] + (rand()&31) - 16;
858                 vel[2] = dir[2] + (rand()&63) - 32;
859                 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
860                 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
861                 org[2] = z;
862
863                 p->scale = 1.5;
864                 p->alpha = 255;
865                 p->die = t;
866                 if (type == 1)
867                 {
868                         p->texnum = particletexture;
869                         p->type = pt_snow;
870                 }
871                 else // 0
872                 {
873                         p->texnum = rainparticletexture;
874                         p->type = pt_static;
875                 }
876                 p->color = colorbase + (rand()&3);
877                 VectorCopy(org, p->org);
878                 VectorCopy(vel, p->vel);
879                 VectorCopy(vel, p->vel2);
880         }
881 }
882
883
884 /*
885 ===============
886 R_LavaSplash
887
888 ===============
889 */
890 void R_LavaSplash (vec3_t org)
891 {
892         int                     i, j, k;
893         particle_t      *p;
894         float           vel;
895         vec3_t          dir;
896         if (!r_particles.value) return; // LordHavoc: particles are optional
897
898         for (i=-16 ; i<16 ; i+=2)
899                 for (j=-16 ; j<16 ; j+=2)
900                         for (k=0 ; k<1 ; k++)
901                         {
902                                 ALLOCPARTICLE
903                 
904                                 p->texnum = flareparticletexture;
905                                 p->scale = 10;
906                                 p->alpha = 128;
907                                 p->die = cl.time + 2 + (rand()&31) * 0.02;
908                                 p->color = 224 + (rand()&7);
909                                 p->type = pt_slowgrav;
910                                 
911                                 dir[0] = j*8 + (rand()&7);
912                                 dir[1] = i*8 + (rand()&7);
913                                 dir[2] = 256;
914         
915                                 p->org[0] = org[0] + dir[0];
916                                 p->org[1] = org[1] + dir[1];
917                                 p->org[2] = org[2] + (rand()&63);
918         
919                                 VectorNormalize (dir);                                          
920                                 vel = 50 + (rand()&63);
921                                 VectorScale (dir, vel, p->vel);
922                         }
923 }
924
925 /*
926 ===============
927 R_TeleportSplash
928
929 ===============
930 */
931 void R_TeleportSplash (vec3_t org)
932 {
933         int                     i, j, k;
934         particle_t      *p;
935 //      vec3_t          dir;
936         if (!r_particles.value) return; // LordHavoc: particles are optional
937
938         /*
939         for (i=-16 ; i<16 ; i+=4)
940                 for (j=-16 ; j<16 ; j+=4)
941                         for (k=-24 ; k<32 ; k+=4)
942                         {
943                                 ALLOCPARTICLE
944                 
945                                 p->contents = 0;
946                                 p->texnum = particletexture;
947                                 p->scale = 2;
948                                 p->alpha = 255;
949                                 p->die = cl.time + 0.2 + (rand()&7) * 0.02;
950                                 p->color = 7 + (rand()&7);
951                                 p->type = pt_slowgrav;
952                                 
953                                 dir[0] = j*8;
954                                 dir[1] = i*8;
955                                 dir[2] = k*8;
956         
957                                 p->org[0] = org[0] + i + (rand()&3);
958                                 p->org[1] = org[1] + j + (rand()&3);
959                                 p->org[2] = org[2] + k + (rand()&3);
960         
961                                 VectorNormalize (dir);                                          
962                                 vel = 50 + (rand()&63);
963                                 VectorScale (dir, vel, p->vel);
964                         }
965         */
966
967         for (i=-24 ; i<24 ; i+=8)
968                 for (j=-24 ; j<24 ; j+=8)
969                         for (k=-24 ; k<32 ; k+=8)
970                         {
971                                 ALLOCPARTICLE
972                 
973                                 p->texnum = flareparticletexture;
974                                 p->scale = 4;
975                                 p->alpha = lhrandom(32,256);
976                                 p->die = cl.time + 5;
977                                 p->color = 254; //8 + (rand()&7);
978                                 p->type = pt_fadespark;
979                                 
980                                 p->org[0] = org[0] + i + (rand()&7);
981                                 p->org[1] = org[1] + j + (rand()&7);
982                                 p->org[2] = org[2] + k + (rand()&7);
983         
984                                 p->vel[0] = i*2 + (rand()%25) - 12;
985                                 p->vel[1] = j*2 + (rand()%25) - 12;
986                                 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
987                         }
988 }
989
990 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
991 {
992         vec3_t          vec;
993         float           len, dec = 0, t, nt, speed;
994         int                     j, contents, bubbles;
995         particle_t      *p;
996         static int      tracercount;
997         if (!r_particles.value) return; // LordHavoc: particles are optional
998
999         t = cl.oldtime;
1000         nt = cl.time;
1001         if (ent->trail_leftover < 0)
1002                 ent->trail_leftover = 0;
1003         t += ent->trail_leftover;
1004         ent->trail_leftover -= (cl.time - cl.oldtime);
1005         if (t >= cl.time)
1006                 return;
1007
1008         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
1009         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1010                 return;
1011
1012         VectorSubtract (end, start, vec);
1013         len = VectorNormalizeLength (vec);
1014         if (len <= 0.01f)
1015                 return;
1016         speed = len / (nt - t);
1017
1018         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1019
1020         while (t < nt)
1021         {
1022                 ALLOCPARTICLE
1023                 
1024                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1025                 p->die = cl.time + 2;
1026
1027                 switch (type)
1028                 {
1029                         case 0: // rocket trail
1030                         case 1: // grenade trail
1031                                 if (bubbles)
1032                                 {
1033                                         dec = 0.005f;
1034                                         p->texnum = bubbleparticletexture;
1035                                         p->scale = lhrandom(1,2);
1036                                         p->alpha = 255;
1037                                         p->color = (rand()&3)+12;
1038                                         p->type = pt_bubble;
1039                                         p->die = cl.time + 2;
1040                                         for (j=0 ; j<3 ; j++)
1041                                         {
1042                                                 p->vel[j] = (rand()&31)-16;
1043                                                 p->org[j] = start[j] + ((rand()&3)-2);
1044                                         }
1045                                 }
1046                                 else
1047                                 {
1048                                         dec = 0.02f;
1049                                         p->texnum = smokeparticletexture[rand()&7];
1050                                         p->scale = lhrandom(8, 12);
1051                                         p->alpha = 64 + (rand()&31);
1052                                         p->color = (rand()&3)+12;
1053                                         p->type = pt_smoke;
1054                                         p->die = cl.time + 10000;
1055                                         VectorCopy(start, p->org);
1056                                 }
1057                                 break;
1058
1059                                 /*
1060                         case 1: // smoke smoke
1061                                 dec = 0.016f;
1062                                 p->texnum = smokeparticletexture;
1063                                 p->scale = lhrandom(6,9);
1064                                 p->alpha = 64;
1065                                 if (r_smokecolor.value)
1066                                         p->color = r_smokecolor.value;
1067                                 else
1068                                         p->color = (rand()&3)+12;
1069                                 p->type = pt_smoke;
1070                                 p->die = cl.time + 1;
1071                                 VectorCopy(start, p->org);
1072                                 break;
1073                                 */
1074
1075                         case 2: // blood
1076                                 dec = 0.025f;
1077                                 p->texnum = smokeparticletexture[rand()&7];
1078                                 p->scale = lhrandom(6, 8);
1079                                 p->alpha = 255;
1080                                 p->color = (rand()&3)+68;
1081                                 p->type = pt_bloodcloud;
1082                                 p->die = cl.time + 9999;
1083                                 for (j=0 ; j<3 ; j++)
1084                                 {
1085                                         p->vel[j] = (rand()&15)-8;
1086                                         p->org[j] = start[j] + ((rand()&3)-2);
1087                                 }
1088                                 break;
1089
1090                         case 3:
1091                         case 5: // tracer
1092                                 dec = 0.01f;
1093                                 p->texnum = flareparticletexture;
1094                                 p->scale = 2;
1095                                 p->alpha = 255;
1096                                 p->die = cl.time + 0.2; //5;
1097                                 p->type = pt_static;
1098                                 if (type == 3)
1099                                         p->color = 52 + ((tracercount&4)<<1);
1100                                 else
1101                                         p->color = 230 + ((tracercount&4)<<1);
1102
1103                                 tracercount++;
1104
1105                                 VectorCopy (start, p->org);
1106                                 if (tracercount & 1)
1107                                 {
1108                                         p->vel[0] = 30*vec[1];
1109                                         p->vel[1] = 30*-vec[0];
1110                                 }
1111                                 else
1112                                 {
1113                                         p->vel[0] = 30*-vec[1];
1114                                         p->vel[1] = 30*vec[0];
1115                                 }
1116                                 break;
1117
1118                         case 4: // slight blood
1119                                 dec = 0.025f; // sparse trail
1120                                 p->texnum = smokeparticletexture[rand()&7];
1121                                 p->scale = lhrandom(6, 8);
1122                                 p->alpha = 192;
1123                                 p->color = (rand()&3)+68;
1124                                 p->type = pt_fadespark2;
1125                                 p->die = cl.time + 9999;
1126                                 for (j=0 ; j<3 ; j++)
1127                                 {
1128                                         p->vel[j] = (rand()&15)-8;
1129                                         p->org[j] = start[j] + ((rand()&3)-2);
1130                                 }
1131                                 break;
1132
1133                         case 6: // voor trail
1134                                 dec = 0.05f; // sparse trail
1135                                 p->texnum = smokeparticletexture[rand()&7];
1136                                 p->scale = lhrandom(3, 5);
1137                                 p->alpha = 255;
1138                                 p->color = 9*16 + 8 + (rand()&3);
1139                                 p->type = pt_fadespark2;
1140                                 p->die = cl.time + 2;
1141                                 for (j=0 ; j<3 ; j++)
1142                                 {
1143                                         p->vel[j] = (rand()&15)-8;
1144                                         p->org[j] = start[j] + ((rand()&3)-2);
1145                                 }
1146                                 break;
1147
1148                         case 7: // Nehahra smoke tracer
1149                                 dec = 0.14f;
1150                                 p->texnum = smokeparticletexture[rand()&7];
1151                                 p->scale = lhrandom(8, 12);
1152                                 p->alpha = 64;
1153                                 p->color = (rand()&3)+12;
1154                                 p->type = pt_smoke;
1155                                 p->die = cl.time + 10000;
1156                                 for (j=0 ; j<3 ; j++)
1157                                         p->org[j] = start[j] + ((rand()&3)-2);
1158                                 break;
1159                 }
1160                 
1161                 t += dec;
1162                 dec *= speed;
1163                 VectorMA (start, dec, vec, start);
1164         }
1165         ent->trail_leftover = t - cl.time;
1166 }
1167
1168 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1169 {
1170         vec3_t          vec;
1171         float           len;
1172         particle_t      *p;
1173         if (!r_particles.value) return; // LordHavoc: particles are optional
1174
1175         VectorSubtract (end, start, vec);
1176         len = VectorNormalizeLength (vec);
1177         while (len > 0)
1178         {
1179                 len -= 3;
1180
1181                 ALLOCPARTICLE
1182                 
1183                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1184
1185                 p->texnum = flareparticletexture;
1186                 p->scale = 8;
1187                 p->alpha = 192;
1188                 p->color = color;
1189                 p->type = pt_smoke;
1190                 p->die = cl.time + 1;
1191                 VectorCopy(start, p->org);
1192 //              for (j=0 ; j<3 ; j++)
1193 //                      p->org[j] = start[j] + ((rand()&15)-8);
1194
1195                 VectorAdd (start, vec, start);
1196         }
1197 }
1198
1199
1200 extern qboolean lighthalf;
1201
1202 /*
1203 ===============
1204 R_DrawParticles
1205 ===============
1206 */
1207 extern  cvar_t  sv_gravity;
1208 void R_CompleteLightPoint (vec3_t color, vec3_t p);
1209
1210 void R_DrawParticles (void)
1211 {
1212         particle_t              *p;
1213         int                             i, r,g,b,a;
1214         float                   grav, grav1, time1, time2, time3, dvel, frametime, scale, scale2, minparticledist;
1215         byte                    *color24;
1216         vec3_t                  up, right, uprightangles, forward2, up2, right2, tempcolor;
1217         int                             activeparticles, maxparticle, j, k;
1218
1219         // LordHavoc: early out condition
1220         if (!numparticles)
1221                 return;
1222
1223         VectorScale (vup, 1.5, up);
1224         VectorScale (vright, 1.5, right);
1225
1226         uprightangles[0] = 0;
1227         uprightangles[1] = r_refdef.viewangles[1];
1228         uprightangles[2] = 0;
1229         AngleVectors (uprightangles, forward2, right2, up2);
1230
1231         frametime = cl.time - cl.oldtime;
1232         time3 = frametime * 15;
1233         time2 = frametime * 10; // 15;
1234         time1 = frametime * 5;
1235         grav = (grav1 = frametime * sv_gravity.value) * 0.05;
1236         dvel = 1+4*frametime;
1237
1238         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1239
1240         activeparticles = 0;
1241         maxparticle = -1;
1242         j = 0;
1243         for (k = 0, p = particles;k < numparticles;k++, p++)
1244         {
1245                 if (p->die < cl.time)
1246                 {
1247                         freeparticles[j++] = p;
1248                         continue;
1249                 }
1250                 maxparticle = k;
1251                 activeparticles++;
1252
1253                 // LordHavoc: only render if not too close
1254                 if (DotProduct(p->org, vpn) >= minparticledist)
1255                 {
1256                         color24 = (byte *) &d_8to24table[(int)p->color];
1257                         r = color24[0];
1258                         g = color24[1];
1259                         b = color24[2];
1260                         a = p->alpha;
1261                         if (lighthalf)
1262                         {
1263                                 r >>= 1;
1264                                 g >>= 1;
1265                                 b >>= 1;
1266                         }
1267                         if (r_dynamicparticles.value)
1268                         {
1269                                 R_CompleteLightPoint(tempcolor, p->org);
1270                                 r = (r * (int) tempcolor[0]) >> 7;
1271                                 g = (g * (int) tempcolor[1]) >> 7;
1272                                 b = (b * (int) tempcolor[2]) >> 7;
1273                         }
1274                         transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1275                         scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1276                         if (p->texnum == rainparticletexture) // rain streak
1277                         {
1278                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale , p->org[1] + up2[1]*scale  + right2[1]*scale , p->org[2] + up2[2]*scale  + right2[2]*scale , 0,1,r,g,b,a);
1279                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale , p->org[1] + up2[1]*scale2 + right2[1]*scale , p->org[2] + up2[2]*scale2 + right2[2]*scale , 0,0,r,g,b,a);
1280                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale2, p->org[1] + up2[1]*scale2 + right2[1]*scale2, p->org[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1281                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale2, p->org[1] + up2[1]*scale  + right2[1]*scale2, p->org[2] + up2[2]*scale  + right2[2]*scale2, 1,1,r,g,b,a);
1282                         }
1283                         else
1284                         {
1285                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale , 0,1,r,g,b,a);
1286                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 0,0,r,g,b,a);
1287                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale2, p->org[1] + up[1]*scale2 + right[1]*scale2, p->org[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1288                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2, 1,1,r,g,b,a);
1289                         }
1290                         transpolyend();
1291                 }
1292
1293                 p->org[0] += p->vel[0]*frametime;
1294                 p->org[1] += p->vel[1]*frametime;
1295                 p->org[2] += p->vel[2]*frametime;
1296                 
1297                 switch (p->type)
1298                 {
1299                 case pt_static:
1300                         break;
1301                 case pt_fire:
1302                         p->ramp += time1;
1303                         if (p->ramp >= 6)
1304                                 p->die = -1;
1305                         else
1306                                 p->color = ramp3[(int)p->ramp];
1307                         p->vel[2] += grav;
1308                         break;
1309
1310                         /*
1311                 case pt_explode:
1312                         p->ramp += time2;
1313                         if (p->ramp >=8)
1314                                 p->die = -1;
1315                         else
1316                                 p->color = ramp1[(int)p->ramp];
1317 //                      p->vel[2] -= grav1; // LordHavoc: apply full gravity to explosion sparks
1318                         for (i=0 ; i<3 ; i++)
1319                                 p->vel[i] *= dvel;
1320 //                      p->vel[2] -= grav;
1321                         break;
1322
1323                 case pt_explode2:
1324                         p->ramp += time3;
1325                         if (p->ramp >= 8)
1326                                 p->die = -1;
1327                         else
1328                                 p->color = ramp2[(int)p->ramp];
1329 //                      p->vel[2] -= grav1; // LordHavoc: apply full gravity to explosion sparks
1330                         for (i=0 ; i<3 ; i++)
1331 //                              p->vel[i] -= p->vel[i]*frametime;
1332                                 p->vel[i] *= dvel;
1333 ////                    p->vel[2] -= grav;
1334                         break;
1335                         */
1336
1337                 case pt_blob:
1338                         for (i=0 ; i<3 ; i++)
1339                                 p->vel[i] += p->vel[i]*dvel;
1340                         p->vel[2] -= grav;
1341                         break;
1342
1343                 case pt_blob2:
1344                         for (i=0 ; i<2 ; i++)
1345                                 p->vel[i] -= p->vel[i]*dvel;
1346                         p->vel[2] -= grav;
1347                         break;
1348
1349                 case pt_grav:
1350                         p->vel[2] -= grav1;
1351                         break;
1352                 case pt_slowgrav:
1353                         p->vel[2] -= grav;
1354                         break;
1355 // LordHavoc: gunshot spark showers
1356                 case pt_dust:
1357                         p->ramp += time1;
1358                         p->scale -= frametime * 4;
1359                         p->alpha -= frametime * 48;
1360                         if (p->ramp >= 8 || p->scale < 1 || p->alpha < 1)
1361                                 p->die = -1;
1362                         else
1363                                 p->color = ramp3[(int)p->ramp];
1364                         p->vel[2] -= grav1;
1365                         break;
1366 // LordHavoc: for smoke trails
1367                 case pt_smoke:
1368                         p->scale += frametime * 6;
1369                         p->alpha -= frametime * 128;
1370 //                      p->vel[2] += grav;
1371                         if (p->alpha < 1)
1372                                 p->die = -1;
1373                         break;
1374                 case pt_snow:
1375                         if (cl.time > p->time2)
1376                         {
1377                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1378                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1379                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1380                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1381                         }
1382                         break;
1383                 case pt_bulletpuff:
1384                         p->scale -= frametime * 64;
1385                         p->alpha -= frametime * 1024;
1386                         p->vel[2] -= grav;
1387                         if (p->alpha < 1 || p->scale < 1)
1388                                 p->die = -1;
1389                         break;
1390                 case pt_bloodcloud:
1391                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1392                         {
1393                                 p->die = -1;
1394                                 break;
1395                         }
1396                         p->scale += frametime * 4;
1397                         p->alpha -= frametime * 64;
1398                         p->vel[2] -= grav;
1399                         if (p->alpha < 1 || p->scale < 1)
1400                                 p->die = -1;
1401                         break;
1402                 case pt_fadespark:
1403                         p->alpha -= frametime * 256;
1404                         p->vel[2] -= grav;
1405                         if (p->alpha < 1)
1406                                 p->die = -1;
1407                         break;
1408                 case pt_fadespark2:
1409                         p->alpha -= frametime * 512;
1410                         p->vel[2] -= grav;
1411                         if (p->alpha < 1)
1412                                 p->die = -1;
1413                         break;
1414                 case pt_fallfadespark:
1415                         p->alpha -= frametime * 256;
1416                         p->vel[2] -= grav1;
1417                         if (p->alpha < 1)
1418                                 p->die = -1;
1419                         break;
1420                 case pt_fallfadespark2:
1421                         p->alpha -= frametime * 512;
1422                         p->vel[2] -= grav1;
1423                         if (p->alpha < 1)
1424                                 p->die = -1;
1425                         break;
1426                 case pt_fade:
1427                         p->alpha -= frametime * 512;
1428                         if (p->alpha < 1)
1429                                 p->die = -1;
1430                         break;
1431                 case pt_bubble:
1432                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1433                                 p->die = -1;
1434                         p->vel[2] += grav1 * 2;
1435                         if (p->vel[2] >= 200)
1436                                 p->vel[2] = lhrandom(130, 200);
1437                         if (cl.time > p->time2)
1438                         {
1439                                 p->time2 = cl.time + lhrandom(0, 0.5);
1440                                 p->vel[0] = lhrandom(-32,32);
1441                                 p->vel[1] = lhrandom(-32,32);
1442                         }
1443                         p->alpha -= frametime * 64;
1444                         if (p->alpha < 1)
1445                                 p->die = -1;
1446                         break;
1447                 case pt_smokecloud:
1448                         p->scale += frametime * 60;
1449                         p->alpha -= frametime * 96;
1450                         if (p->alpha < 1)
1451                                 p->die = -1;
1452                         break;
1453                 }
1454         }
1455         // fill in gaps to compact the array
1456         i = 0;
1457         while (maxparticle >= activeparticles)
1458         {
1459                 *freeparticles[i++] = particles[maxparticle--];
1460                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1461                         maxparticle--;
1462         }
1463         numparticles = activeparticles;
1464 }
1465