]> icculus.org git repositories - divverent/darkplaces.git/blob - r_part.c
6abff27cd49ee2954c935faecbab0e204f7df7e8
[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                   16384   // 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 {
29         pt_static, pt_grav, pt_slowgrav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash, pt_flame, pt_glow, pt_decal, pt_blood, pt_bloodsplatter
30 }
31 ptype_t;
32
33 typedef struct particle_s
34 {
35         vec3_t          org;
36         float           color;
37         vec3_t          vel;
38         float           die;
39         ptype_t         type;
40         float           scale;
41         rtexture_t      *tex;
42         byte            dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
43         byte            rendermode; // a TPOLYTYPE_ value
44         byte            pad1;
45         byte            pad2;
46         float           alpha; // 0-255
47         float           time2; // used for various things (snow fluttering, for example)
48         float           bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical of bouncing particles)
49         vec3_t          oldorg;
50         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
51         vec3_t          direction; // used by decals
52 }
53 particle_t;
54
55 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
56
57 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
58 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
59 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
60
61 rtexture_t *particletexture;
62 rtexture_t *smokeparticletexture[8];
63 rtexture_t *rainparticletexture;
64 rtexture_t *bubbleparticletexture;
65
66 particle_t      *particles;
67 int                     r_numparticles;
68
69 vec3_t                  r_pright, r_pup, r_ppn;
70
71 int                     numparticles;
72 particle_t      **freeparticles; // list used only in compacting particles array
73
74 cvar_t r_particles = {"r_particles", "1"};
75 cvar_t r_drawparticles = {"r_drawparticles", "1"};
76 cvar_t r_dynamicparticles = {"r_dynamicparticles", "1", true};
77
78 byte shadebubble(float dx, float dy, vec3_t light)
79 {
80         float   dz, f, dot;
81         vec3_t  normal;
82         if ((dx*dx+dy*dy) < 1) // it does hit the sphere
83         {
84                 dz = 1 - (dx*dx+dy*dy);
85                 f = 0;
86                 // back side
87                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
88                 VectorNormalize(normal);
89                 dot = DotProduct(normal, light);
90                 if (dot > 0.5) // interior reflection
91                         f += ((dot *  2) - 1);
92                 else if (dot < -0.5) // exterior reflection
93                         f += ((dot * -2) - 1);
94                 // front side
95                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
96                 VectorNormalize(normal);
97                 dot = DotProduct(normal, light);
98                 if (dot > 0.5) // interior reflection
99                         f += ((dot *  2) - 1);
100                 else if (dot < -0.5) // exterior reflection
101                         f += ((dot * -2) - 1);
102                 f *= 128;
103                 f += 16; // just to give it a haze so you can see the outline
104                 f = bound(0, f, 255);
105                 return (byte) f;
106         }
107         else
108                 return 0;
109 }
110
111 void R_InitParticleTexture (void)
112 {
113         int             x,y,d,i,m;
114         float   dx, dy;
115         byte    data[32][32][4], noise1[64][64], noise2[64][64];
116         vec3_t  light;
117
118         for (y = 0;y < 32;y++)
119         {
120                 dy = y - 16;
121                 for (x = 0;x < 32;x++)
122                 {
123                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
124                         dx = x - 16;
125                         d = (255 - (dx*dx+dy*dy));
126                         if (d < 0) d = 0;
127                         data[y][x][3] = (byte) d;
128                 }
129         }
130         particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
131
132         for (i = 0;i < 8;i++)
133         {
134                 do
135                 {
136                         fractalnoise(&noise1[0][0], 64, 4);
137                         fractalnoise(&noise2[0][0], 64, 8);
138                         m = 0;
139                         for (y = 0;y < 32;y++)
140                         {
141                                 dy = y - 16;
142                                 for (x = 0;x < 32;x++)
143                                 {
144                                         int j;
145                                         j = (noise1[y][x] - 128) * 2 + 128;
146                                         if (j < 0) j = 0;
147                                         if (j > 255) j = 255;
148                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = j;
149                                         dx = x - 16;
150                                         d = (noise2[y][x] - 128) * 4 + 128;
151                                         if (d > 0)
152                                         {
153                                                 d = (d * (255 - (int) (dx*dx+dy*dy))) >> 8;
154                                                 //j = (sqrt(dx*dx+dy*dy) * 2.0f - 16.0f);
155                                                 //if (j > 0)
156                                                 //      d = (d * (255 - j*j)) >> 8;
157                                                 if (d < 0) d = 0;
158                                                 if (d > 255) d = 255;
159                                                 data[y][x][3] = (byte) d;
160                                                 if (m < d)
161                                                         m = d;
162                                         }
163                                 }
164                         }
165                 }
166                 while (m < 192);
167
168                 smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
169         }
170
171         light[0] = 1;light[1] = 1;light[2] = 1;
172         VectorNormalize(light);
173         for (x=0 ; x<32 ; x++)
174         {
175                 for (y=0 ; y<32 ; y++)
176                 {
177                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
178                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
179                 }
180         }
181         rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
182
183         light[0] = 1;light[1] = 1;light[2] = 1;
184         VectorNormalize(light);
185         for (x=0 ; x<32 ; x++)
186         {
187                 for (y=0 ; y<32 ; y++)
188                 {
189                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
190                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
191                 }
192         }
193         bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
194 }
195
196 void r_part_start()
197 {
198         particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
199         freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
200         numparticles = 0;
201         R_InitParticleTexture ();
202 }
203
204 void r_part_shutdown()
205 {
206         numparticles = 0;
207         qfree(particles);
208         qfree(freeparticles);
209 }
210
211 /*
212 ===============
213 R_InitParticles
214 ===============
215 */
216 void R_ReadPointFile_f (void);
217 void R_Particles_Init (void)
218 {
219         int             i;
220
221         i = COM_CheckParm ("-particles");
222
223         if (i)
224         {
225                 r_numparticles = (int)(atoi(com_argv[i+1]));
226                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
227                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
228         }
229         else
230         {
231                 r_numparticles = MAX_PARTICLES;
232         }
233
234         Cmd_AddCommand ("pointfile", R_ReadPointFile_f);        
235
236         Cvar_RegisterVariable (&r_particles);
237         Cvar_RegisterVariable (&r_drawparticles);
238         Cvar_RegisterVariable (&r_dynamicparticles);
239
240         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
241 }
242
243 //void particle(int ptype, int pcolor, int ptex, int prendermode, int plight, float pscale, float palpha, float ptime, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz)
244 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
245 {\
246         particle_t      *part;\
247         if (numparticles >= r_numparticles)\
248                 return;\
249         part = &particles[numparticles++];\
250         part->type = (ptype);\
251         part->color = (pcolor);\
252         part->tex = (ptex);\
253         part->dynlight = (plight);\
254         part->rendermode = (prendermode);\
255         part->scale = (pscale);\
256         part->alpha = (palpha);\
257         part->die = cl.time + (ptime);\
258         part->bounce = (pbounce);\
259         part->org[0] = (px);\
260         part->org[1] = (py);\
261         part->org[2] = (pz);\
262         part->vel[0] = (pvx);\
263         part->vel[1] = (pvy);\
264         part->vel[2] = (pvz);\
265         part->time2 = 0;\
266         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
267 }
268 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
269 {\
270         particle_t      *part;\
271         if (numparticles >= r_numparticles)\
272                 return;\
273         part = &particles[numparticles++];\
274         part->type = (ptype);\
275         part->color = (pcolor);\
276         part->tex = (ptex);\
277         part->dynlight = (plight);\
278         part->rendermode = (prendermode);\
279         part->scale = (pscale);\
280         part->alpha = (palpha);\
281         part->die = cl.time + (ptime);\
282         part->bounce = (pbounce);\
283         part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
284         part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
285         part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
286         part->vel[0] = lhrandom(-(pvscale), (pvscale));\
287         part->vel[1] = lhrandom(-(pvscale), (pvscale));\
288         part->vel[2] = lhrandom(-(pvscale), (pvscale));\
289         part->time2 = 0;\
290         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
291 }
292 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
293 {\
294         particle_t      *part;\
295         if (numparticles >= r_numparticles)\
296                 return;\
297         part = &particles[numparticles++];\
298         part->type = (ptype);\
299         part->color = (pcolor);\
300         part->tex = (ptex);\
301         part->dynlight = (plight);\
302         part->rendermode = (prendermode);\
303         part->scale = (pscale);\
304         part->alpha = (palpha);\
305         part->die = cl.time + (ptime);\
306         part->bounce = (pbounce);\
307         part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
308         part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
309         part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
310         part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
311         part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
312         part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
313         part->time2 = 0;\
314         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
315 }
316 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
317 {\
318         particle_t      *part;\
319         if (numparticles >= r_numparticles)\
320                 return;\
321         part = &particles[numparticles++];\
322         part->type = (ptype);\
323         part->color = (pcolor);\
324         part->tex = (ptex);\
325         part->dynlight = (plight);\
326         part->rendermode = (prendermode);\
327         part->scale = (pscale);\
328         part->alpha = (palpha);\
329         part->die = cl.time + (ptime);\
330         part->bounce = (pbounce);\
331         part->org[0] = (px);\
332         part->org[1] = (py);\
333         part->org[2] = (pz);\
334         part->vel[0] = (pvx);\
335         part->vel[1] = (pvy);\
336         part->vel[2] = (pvz);\
337         part->time2 = (ptime2);\
338         part->vel2[0] = (pvx2);\
339         part->vel2[1] = (pvy2);\
340         part->vel2[2] = (pvz2);\
341 }
342
343 /*
344 ===============
345 R_EntityParticles
346 ===============
347 */
348 void R_EntityParticles (entity_t *ent)
349 {
350         int                     i;
351         float           angle;
352         float           sp, sy, cp, cy;
353         vec3_t          forward;
354         float           dist;
355         float           beamlength;
356         static vec3_t avelocities[NUMVERTEXNORMALS];
357         if (!r_particles.value) return; // LordHavoc: particles are optional
358         
359         dist = 64;
360         beamlength = 16;
361
362         if (!avelocities[0][0])
363                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
364                         avelocities[0][i] = (rand()&255) * 0.01;
365
366         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
367         {
368                 angle = cl.time * avelocities[i][0];
369                 sy = sin(angle);
370                 cy = cos(angle);
371                 angle = cl.time * avelocities[i][1];
372                 sp = sin(angle);
373                 cp = cos(angle);
374         
375                 forward[0] = cp*cy;
376                 forward[1] = cp*sy;
377                 forward[2] = -sp;
378
379                 particle(pt_static, 0x6f, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
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         char    name[MAX_OSPATH];
410         
411         sprintf (name,"maps/%s.pts", sv.name);
412
413         COM_FOpenFile (name, &f, false);
414         if (!f)
415         {
416                 Con_Printf ("couldn't open %s\n", name);
417                 return;
418         }
419         
420         Con_Printf ("Reading %s...\n", name);
421         c = 0;
422         for (;;)
423         {
424                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
425                 if (r != 3)
426                         break;
427                 c++;
428                 
429                 if (numparticles >= r_numparticles)
430                 {
431                         Con_Printf ("Not enough free particles\n");
432                         break;
433                 }
434                 particle(pt_static, (-c)&15, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0);
435         }
436
437         fclose (f);
438         Con_Printf ("%i points read\n", c);
439 }
440
441 /*
442 ===============
443 R_ParseParticleEffect
444
445 Parse an effect out of the server message
446 ===============
447 */
448 void R_ParseParticleEffect (void)
449 {
450         vec3_t          org, dir;
451         int                     i, count, msgcount, color;
452         
453         for (i=0 ; i<3 ; i++)
454                 org[i] = MSG_ReadCoord ();
455         for (i=0 ; i<3 ; i++)
456                 dir[i] = MSG_ReadChar () * (1.0/16);
457         msgcount = MSG_ReadByte ();
458         color = MSG_ReadByte ();
459
460 if (msgcount == 255)
461         count = 1024;
462 else
463         count = msgcount;
464         
465         R_RunParticleEffect (org, dir, color, count);
466 }
467         
468 /*
469 ===============
470 R_ParticleExplosion
471
472 ===============
473 */
474 void R_ParticleExplosion (vec3_t org, int smoke)
475 {
476         int i;
477         if (!r_particles.value) return; // LordHavoc: particles are optional
478
479 //      particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
480
481         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
482         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
483         {
484                 for (i=0 ; i<128 ; i++)
485                         particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, TPOLYTYPE_ADD, false, lhrandom(1, 2), 255, 2, 1.5, org, 16, 96);
486         }
487         else
488                 R_NewExplosion(org);
489         /*
490         else
491         {
492                 int j;
493 //              int color;
494                 float f, forg[3], fvel[3], fvel2[3];
495 //              for (i = 0;i < 256;i++)
496 //                      particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
497 //              for (i = 0;i < 256;i++)
498 //                      particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, org, 15, 150);
499                 for (i = 0;i < 32;i++)
500                 {
501                         fvel[0] = lhrandom(-150, 150);
502                         fvel[1] = lhrandom(-150, 150);
503                         fvel[2] = lhrandom(-150, 150) + 80;
504 //                      particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 3, 255, 5, forg[0] + lhrandom(-5, 5), forg[1] + lhrandom(-5, 5), forg[2] + lhrandom(-5, 5), fvel2[0], fvel2[1], fvel2[2]);
505                         for (j = 0;j < 64;j++)
506                         {
507                                 forg[0] = lhrandom(-20, 20) + org[0];
508                                 forg[1] = lhrandom(-20, 20) + org[1];
509                                 forg[2] = lhrandom(-20, 20) + org[2];
510                                 fvel2[0] = fvel[0] + lhrandom(-30, 30);
511                                 fvel2[1] = fvel[1] + lhrandom(-30, 30);
512                                 fvel2[2] = fvel[2] + lhrandom(-30, 30);
513                                 f = lhrandom(0.2, 1);
514                                 fvel2[0] *= f;
515                                 fvel2[1] *= f;
516                                 fvel2[2] *= f;
517                                 particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
518                         }
519                 }
520 //              for (i = 0;i < 16;i++)
521 //                      particle2(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 20, 192, 99, org, 20, 0);
522 //              for (i = 0;i < 50;i++)
523 //                      particle2(pt_flamingdebris, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 3, 255, 99, org, 10, 200);
524 //              for (i = 0;i < 30;i++)
525 //                      particle2(pt_smokingdebris, 10 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99, org, 10, 100);
526         }
527         */
528 }
529
530 /*
531 ===============
532 R_ParticleExplosion2
533
534 ===============
535 */
536 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
537 {
538         int                     i;
539         if (!r_particles.value) return; // LordHavoc: particles are optional
540
541         for (i = 0;i < 512;i++)
542                 particle2(pt_fade, colorStart + (i % colorLength), particletexture, TPOLYTYPE_ALPHA, false, 1.5, 255, 0.3, 0, org, 8, 192);
543 }
544
545 /*
546 ===============
547 R_BlobExplosion
548
549 ===============
550 */
551 void R_BlobExplosion (vec3_t org)
552 {
553         int                     i;
554         if (!r_particles.value) return; // LordHavoc: particles are optional
555         
556         for (i=0 ; i<512 ; i++)
557                 particle3(pt_blob, 66+(rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 1.4), 0, org, 16, 16, 16, 4, 4, 128);
558         for (i=0 ; i<512 ; i++)
559                 particle3(pt_blob2, 150+(rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 1.4), 0, org, 16, 16, 16, 4, 4, 128);
560 }
561
562 /*
563 ===============
564 R_RunParticleEffect
565
566 ===============
567 */
568 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
569 {
570         if (!r_particles.value) return; // LordHavoc: particles are optional
571         
572         if (count == 1024)
573         {
574                 R_ParticleExplosion(org, false);
575                 return;
576         }
577         while (count--)
578                 particle2(pt_fade, color + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 1, 128, 1, 0, org, 8, 15);
579 }
580
581 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
582 /*
583 ===============
584 R_SparkShower
585 ===============
586 */
587 void R_SparkShower (vec3_t org, vec3_t dir, int count)
588 {
589         if (!r_particles.value) return; // LordHavoc: particles are optional
590
591         // smoke puff
592         particle(pt_bulletsmoke, 12+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 1, 160, 99, 0, org[0], org[1], org[2], lhrandom(-4, 4), lhrandom(-4, 4), 16);
593         // sparks
594         while(count--)
595                 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(0, 255), 1.5, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 128);
596 }
597
598 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
599 {
600         if (!r_particles.value) return; // LordHavoc: particles are optional
601
602         if (count > 100)
603                 count = 100;
604         while(count > 0)
605         {
606                 particle(pt_bloodsplatter, 68+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, lhrandom(10, 20), min(count, 10) * 25 + 5, 99, -1, org[0], org[1], org[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
607                 count -= 10;
608         }
609 }
610
611 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
612 {
613         vec3_t          diff;
614         vec3_t          center;
615         vec3_t          velscale;
616         if (!r_particles.value) return; // LordHavoc: particles are optional
617
618         VectorSubtract(maxs, mins, diff);
619         center[0] = (mins[0] + maxs[0]) * 0.5;
620         center[1] = (mins[1] + maxs[1]) * 0.5;
621         center[2] = (mins[2] + maxs[2]) * 0.5;
622         // FIXME: change velspeed back to 2.0x after fixing mod
623         velscale[0] = velspeed * 2.0 / diff[0];
624         velscale[1] = velspeed * 2.0 / diff[1];
625         velscale[2] = velspeed * 2.0 / diff[2];
626         
627         while (count--)
628         {
629                 vec3_t org, vel;
630                 org[0] = lhrandom(mins[0], maxs[0]);
631                 org[1] = lhrandom(mins[1], maxs[1]);
632                 org[2] = lhrandom(mins[2], maxs[2]);
633                 vel[0] = (org[0] - center[0]) * velscale[0];
634                 vel[1] = (org[1] - center[1]) * velscale[1];
635                 vel[2] = (org[2] - center[2]) * velscale[2];
636                 particle(pt_bloodsplatter, 68+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, lhrandom(10, 25), 255, 99, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2]);
637         }
638 }
639
640 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
641 {
642         float           t;
643         if (!r_particles.value) return; // LordHavoc: particles are optional
644         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
645         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
646         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
647
648         while (count--)
649                 particle(gravity ? pt_grav : pt_static, colorbase + (rand()&3), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 2), 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel));
650 }
651
652 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
653 {
654         vec3_t          vel;
655         float           t, z;
656         if (!r_particles.value) return; // LordHavoc: particles are optional
657         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
658         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
659         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
660         if (dir[2] < 0) // falling
661         {
662                 t = (maxs[2] - mins[2]) / -dir[2];
663                 z = maxs[2];
664         }
665         else // rising??
666         {
667                 t = (maxs[2] - mins[2]) / dir[2];
668                 z = mins[2];
669         }
670         if (t < 0 || t > 2) // sanity check
671                 t = 2;
672
673         switch(type)
674         {
675         case 0:
676                 while(count--)
677                 {
678                         vel[0] = dir[0] + lhrandom(-16, 16);
679                         vel[1] = dir[1] + lhrandom(-16, 16);
680                         vel[2] = dir[2] + lhrandom(-32, 32);
681                         particle4(pt_rain, colorbase + (rand()&3), rainparticletexture, TPOLYTYPE_ALPHA, true, 3, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2]);
682                 }
683                 break;
684         case 1:
685                 while(count--)
686                 {
687                         vel[0] = dir[0] + lhrandom(-16, 16);
688                         vel[1] = dir[1] + lhrandom(-16, 16);
689                         vel[2] = dir[2] + lhrandom(-32, 32);
690                         particle4(pt_snow, colorbase + (rand()&3), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2]);
691                 }
692                 break;
693         default:
694                 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
695         }
696 }
697
698 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
699 {
700         float           t;
701         if (!r_particles.value) return; // LordHavoc: particles are optional
702         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
703         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
704         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
705
706         while (count--)
707                 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, false, 1, lhrandom(64, 255), 5, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 48));
708 }
709
710 void R_Flames (vec3_t org, vec3_t vel, int count)
711 {
712         if (!r_particles.value) return; // LordHavoc: particles are optional
713
714         while (count--)
715                 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, false, 1, lhrandom(64, 255), 5, 1.5, org[0], org[1], org[2], vel[0] + lhrandom(-16, 16), vel[1] + lhrandom(-16, 16), vel[2] + lhrandom(-16, 16));
716 }
717
718
719
720 /*
721 ===============
722 R_LavaSplash
723
724 ===============
725 */
726 void R_LavaSplash (vec3_t origin)
727 {
728         int                     i, j;
729         float           vel;
730         vec3_t          dir, org;
731         if (!r_particles.value) return; // LordHavoc: particles are optional
732
733         for (i=-128 ; i<128 ; i+=8)
734         {
735                 for (j=-128 ; j<128 ; j+=8)
736                 {
737                         dir[0] = j + lhrandom(0, 8);
738                         dir[1] = i + lhrandom(0, 8);
739                         dir[2] = 256;
740                         org[0] = origin[0] + dir[0];
741                         org[1] = origin[1] + dir[1];
742                         org[2] = origin[2] + lhrandom(0, 64);
743                         vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
744                         particle(pt_slowgrav, 224 + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 3, 128, lhrandom(2, 2.5), 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel);
745 //                      particle(pt_slowgrav, 224 + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 3, 128, lhrandom(2, 2.5), 0, origin[0] + i, origin[1] + j, origin[2] + lhrandom(0, 63), i * lhrandom(0.125, 0.25), j * lhrandom(0.125, 0.25), lhrandom(64, 128));
746                 }
747         }
748 }
749
750 /*
751 ===============
752 R_TeleportSplash
753
754 ===============
755 */
756 void R_TeleportSplash (vec3_t org)
757 {
758         int                     i, j, k;
759         if (!r_particles.value) return; // LordHavoc: particles are optional
760
761         for (i=-16 ; i<16 ; i+=8)
762                 for (j=-16 ; j<16 ; j+=8)
763                         for (k=-24 ; k<32 ; k+=8)
764                                 particle(pt_fade, 254, particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(64, 128), 5, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), i*2 + lhrandom(-12.5, 12.5), j*2 + lhrandom(-12.5, 12.5), k*2 + lhrandom(27.5, 52.5));
765 }
766
767 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
768 {
769         vec3_t          vec, dir, vel;
770         float           len, dec = 0, speed;
771         int                     contents, bubbles, polytype;
772         double          t;
773         if (!r_particles.value) return; // LordHavoc: particles are optional
774
775         VectorSubtract(end, start, dir);
776         VectorNormalize(dir);
777
778         /*
779         if (type == 0) // rocket glow
780                 particle(pt_glow, 254, particletexture, TPOLYTYPE_ADD, false, 10, 160, 999, 0, start[0] - 12 * dir[0], start[1] - 12 * dir[1], start[2] - 12 * dir[2], 0, 0, 0);
781         */
782
783         t = ent->trail_time;
784         if (t >= cl.time)
785                 return; // no particles to spawn this frame (sparse trail)
786
787         if (t < cl.oldtime)
788                 t = cl.oldtime;
789
790         VectorSubtract (end, start, vec);
791         len = VectorNormalizeLength (vec);
792         if (len <= 0.01f)
793         {
794                 // advance the trail time
795                 ent->trail_time = cl.time;
796                 return;
797         }
798         speed = len / (cl.time - cl.oldtime);
799         VectorScale(vec, speed, vel);
800
801         // advance into this frame to reach the first puff location
802         dec = t - cl.oldtime;
803         dec *= speed;
804         VectorMA(start, dec, vec, start);
805
806         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
807         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
808         {
809                 // advance the trail time
810                 ent->trail_time = cl.time;
811                 return;
812         }
813
814         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
815
816         polytype = TPOLYTYPE_ALPHA;
817         if (ent->effects & EF_ADDITIVE)
818                 polytype = TPOLYTYPE_ADD;
819
820         while (t < cl.time)
821         {
822                 switch (type)
823                 {
824                         case 0: // rocket trail
825                                 if (bubbles)
826                                 {
827                                         dec = 0.01f;
828                                         particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 2, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
829                                 }
830                                 else
831                                 {
832                                         dec = 0.01f;
833                                         particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 16);
834                                         if (type == 0)
835                                         {
836                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 5, 0, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
837                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 5, 0, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
838 //                                              particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 5, 0, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
839 //                                              particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 5, 0, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
840                                         }
841                                 }
842                                 break;
843
844                         case 1: // grenade trail
845                                 // FIXME: make it gradually stop smoking
846                                 if (bubbles)
847                                 {
848                                         dec = 0.02f;
849                                         particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 2, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
850                                 }
851                                 else
852                                 {
853                                         dec = 0.02f;
854                                         particle(pt_smoke, 6, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 16);
855                                 }
856                                 break;
857
858
859                         case 2: // blood
860                                 dec = 0.025f;
861                                 particle(pt_bloodsplatter, 67+(rand()&3), smokeparticletexture[rand()&7], polytype, true, lhrandom(5, 20), 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
862                                 break;
863
864                         case 4: // slight blood
865                                 dec = 0.025f;
866                                 particle(pt_bloodsplatter, 67+(rand()&3), smokeparticletexture[rand()&7], polytype, true, lhrandom(5, 20), 192, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
867                                 break;
868
869                         case 3: // green tracer
870                                 dec = 0.02f;
871                                 particle(pt_fade,  56, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
872                                 break;
873
874                         case 5: // flame tracer
875                                 dec = 0.02f;
876                                 particle(pt_fade, 234, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
877                                 break;
878
879                         case 6: // voor trail
880                                 dec = 0.05f; // sparse trail
881                                 particle(pt_fade, 152 + (rand()&3), smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
882                                 break;
883
884                         case 7: // Nehahra smoke tracer
885                                 dec = 0.14f;
886                                 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
887                                 break;
888                 }
889                 
890                 // advance to next time and position
891                 t += dec;
892                 dec *= speed;
893                 VectorMA (start, dec, vec, start);
894         }
895         ent->trail_time = t;
896 }
897
898 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
899 {
900         vec3_t          vec;
901         int                     len;
902         if (!r_particles.value) return; // LordHavoc: particles are optional
903
904         VectorSubtract (end, start, vec);
905         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
906         VectorScale(vec, 3, vec);
907         while (len--)
908         {
909                 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 99, 0, start[0], start[1], start[2], 0, 0, 0);
910                 VectorAdd (start, vec, start);
911         }
912 }
913
914
915 /*
916 ===============
917 R_DrawParticles
918 ===============
919 */
920 extern  cvar_t  sv_gravity;
921
922 void R_MoveParticles (void)
923 {
924         particle_t              *p;
925         int                             i, activeparticles, maxparticle, j, a;
926         vec3_t                  v;
927         float                   gravity, dvel, frametime;
928
929         // LordHavoc: early out condition
930         if (!numparticles)
931                 return;
932
933         frametime = cl.time - cl.oldtime;
934         gravity = frametime * sv_gravity.value;
935         dvel = 1+4*frametime;
936
937         activeparticles = 0;
938         maxparticle = -1;
939         j = 0;
940         for (i = 0, p = particles;i < numparticles;i++, p++)
941         {
942                 if (p->die < cl.time)
943                 {
944                         freeparticles[j++] = p;
945                         continue;
946                 }
947
948                 VectorCopy(p->org, p->oldorg);
949                 p->org[0] += p->vel[0]*frametime;
950                 p->org[1] += p->vel[1]*frametime;
951                 p->org[2] += p->vel[2]*frametime;
952                 if (p->bounce)
953                 {
954                         vec3_t normal;
955                         float dist;
956                         if (TraceLine(p->oldorg, p->org, v, normal) < 1)
957                         {
958                                 VectorCopy(v, p->org);
959                                 if (p->bounce < 0)
960                                 {
961                                         VectorClear(p->vel);
962                                         p->type = pt_decal;
963                                         // have to negate the direction (why?)
964                                         VectorNegate(normal, p->direction);
965                                         p->bounce = 0;
966                                         p->time2 = cl.time + 30;
967                                 }
968                                 else
969                                 {
970                                         dist = DotProduct(p->vel, normal) * -p->bounce;
971                                         VectorMAQuick(p->vel, dist, normal, p->vel);
972                                         if (DotProduct(p->vel, p->vel) < 0.03)
973                                         {
974                                                 VectorClear(p->vel);
975                                                 // hack - world is static, therefore there won't be any moving or disappearing surfaces to worry about
976                                                 //p->bounce = 0;
977                                         }
978                                 }
979                         }
980                 }
981                 
982                 switch (p->type)
983                 {
984                 case pt_static:
985                         break;
986
987                         // LordHavoc: drop-through because of shared code
988                 case pt_blob:
989                         p->vel[2] *= dvel;
990                 case pt_blob2:
991                         p->vel[0] *= dvel;
992                         p->vel[1] *= dvel;
993                         break;
994
995                 case pt_grav:
996                         p->vel[2] -= gravity;
997                         break;
998                 case pt_slowgrav:
999                         p->vel[2] -= gravity * 0.05;
1000                         break;
1001                 case pt_snow:
1002                         if (cl.time > p->time2)
1003                         {
1004                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1005                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1006                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1007                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1008                         }
1009                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1010                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1011                         {
1012                                 vec3_t normal;
1013                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1014                                         break; // still in solid
1015                                 p->die = cl.time + 1000;
1016                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1017                                 switch (a)
1018                                 {
1019                                 case CONTENTS_LAVA:
1020                                 case CONTENTS_SLIME:
1021                                         p->tex = smokeparticletexture[rand()&7];
1022                                         p->type = pt_smokecloud;
1023                                         p->alpha = 96;
1024                                         p->scale = 5;
1025                                         p->vel[2] = 96;
1026                                         break;
1027                                 case CONTENTS_WATER:
1028                                         p->tex = smokeparticletexture[rand()&7];
1029                                         p->type = pt_splash;
1030                                         p->alpha = 96;
1031                                         p->scale = 5;
1032                                         p->vel[2] = 96;
1033                                         break;
1034                                 default: // CONTENTS_SOLID and any others
1035                                         TraceLine(p->oldorg, p->org, v, normal);
1036                                         VectorCopy(v, p->org);
1037                                         p->tex = smokeparticletexture[rand()&7];
1038                                         p->type = pt_fade;
1039                                         VectorClear(p->vel);
1040                                         break;
1041                                 }
1042                         }
1043                         break;
1044                 case pt_bloodcloud:
1045 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1046 //                      {
1047 //                              p->die = -1;
1048 //                              break;
1049 //                      }
1050                         p->scale += frametime * 16;
1051                         p->alpha -= frametime * 256;
1052                         p->vel[2] -= gravity * 0.25;
1053                         if (p->alpha < 1)
1054                                 p->die = -1;
1055                         break;
1056                 case pt_blood:
1057 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1058 //                      {
1059 //                              p->die = -1;
1060 //                              break;
1061 //                      }
1062                         p->scale += frametime * 16;
1063                         p->alpha -= frametime * 512;
1064                         p->vel[2] -= gravity * 0.25;
1065                         if (p->alpha < 1)
1066                                 p->die = -1;
1067                         break;
1068                 case pt_bloodsplatter:
1069 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1070 //                      {
1071 //                              p->die = -1;
1072 //                              break;
1073 //                      }
1074                         p->alpha -= frametime * 128;
1075                         if (p->alpha < 1)
1076                                 p->die = -1;
1077                         p->vel[2] -= gravity * 0.5;
1078                         break;
1079                 case pt_fallfadespark:
1080                         p->alpha -= frametime * 256;
1081                         p->vel[2] -= gravity;
1082                         if (p->alpha < 1)
1083                                 p->die = -1;
1084                         break;
1085                 case pt_fade:
1086                         p->alpha -= frametime * 512;
1087                         if (p->alpha < 1)
1088                                 p->die = -1;
1089                         break;
1090                 case pt_bubble:
1091                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1092                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1093                         {
1094                                 p->tex = smokeparticletexture[rand()&7];
1095                                 p->type = pt_splash;
1096                                 p->alpha = 96;
1097                                 p->scale = 5;
1098                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1099                                 p->die = cl.time + 1000;
1100 //                              p->die = -1;
1101                         }
1102                         p->vel[2] += gravity * 0.25;
1103                         p->vel[0] *= (1 - (frametime * 0.0625));
1104                         p->vel[1] *= (1 - (frametime * 0.0625));
1105                         p->vel[2] *= (1 - (frametime * 0.0625));
1106                         if (cl.time > p->time2)
1107                         {
1108                                 p->time2 = cl.time + lhrandom(0, 0.5);
1109                                 p->vel[0] += lhrandom(-32,32);
1110                                 p->vel[1] += lhrandom(-32,32);
1111                                 p->vel[2] += lhrandom(-32,32);
1112                         }
1113                         p->alpha -= frametime * 64;
1114                         if (p->alpha < 1)
1115                                 p->die = -1;
1116                         if (p->alpha < 1)
1117                                 p->die = -1;
1118                         break;
1119 // LordHavoc: for smoke trails
1120                 case pt_bulletsmoke:
1121                         p->scale += frametime * 60;
1122                         p->alpha -= frametime * 512;
1123                         p->vel[2] += gravity * 0.05;
1124                         if (p->alpha < 1)
1125                                 p->die = -1;
1126                         break;
1127                 case pt_smoke:
1128                         p->scale += frametime * 20;
1129                         p->alpha -= frametime * 256;
1130                         p->vel[2] += gravity * 0.05;
1131                         if (p->alpha < 1)
1132                                 p->die = -1;
1133                         break;
1134                 case pt_smokecloud:
1135                         p->scale += frametime * 64;
1136                         p->alpha -= frametime * 256;
1137                         p->vel[2] += gravity * 0.05;
1138                         if (p->alpha < 1)
1139                                 p->die = -1;
1140                         break;
1141                 case pt_splash:
1142                         p->scale += frametime * 24;
1143                         p->alpha -= frametime * 512;
1144                         if (p->alpha < 1)
1145                                 p->die = -1;
1146                         break;
1147                 case pt_rain:
1148                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1149                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1150                         {
1151                                 vec3_t normal;
1152                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1153                                         break; // still in solid
1154                                 p->die = cl.time + 1000;
1155                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1156                                 switch (a)
1157                                 {
1158                                 case CONTENTS_LAVA:
1159                                 case CONTENTS_SLIME:
1160                                         p->tex = smokeparticletexture[rand()&7];
1161                                         p->type = pt_smokecloud;
1162                                         p->alpha = 96;
1163                                         p->scale = 5;
1164                                         p->vel[2] = 96;
1165                                         break;
1166                                 case CONTENTS_WATER:
1167                                         p->tex = smokeparticletexture[rand()&7];
1168                                         p->type = pt_splash;
1169                                         p->alpha = 96;
1170                                         p->scale = 5;
1171                                         break;
1172                                 default: // CONTENTS_SOLID and any others
1173                                         TraceLine(p->oldorg, p->org, v, normal);
1174                                         VectorCopy(v, p->org);
1175                                         p->tex = smokeparticletexture[rand()&7];
1176                                         p->type = pt_splash;
1177                                         p->alpha = 96;
1178                                         p->scale = 5;
1179                                         particle(pt_fallfadespark, 245, particletexture, TPOLYTYPE_ADD, false, 1,  64, 1, 1.3, p->org[0], p->org[1], p->org[2] + 1, lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 32) + 48);
1180                                         particle(pt_fallfadespark, 245, particletexture, TPOLYTYPE_ADD, false, 1, 128, 1, 1.3, p->org[0], p->org[1], p->org[2] + 1, lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 32) + 48);
1181                                         particle(pt_fallfadespark, 245, particletexture, TPOLYTYPE_ADD, false, 1, 192, 1, 1.3, p->org[0], p->org[1], p->org[2] + 1, lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 32) + 48);
1182                                         particle(pt_fallfadespark, 245, particletexture, TPOLYTYPE_ADD, false, 1, 255, 1, 1.3, p->org[0], p->org[1], p->org[2] + 1, lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 32) + 48);
1183                                         break;
1184                                 }
1185                         }
1186                         break;
1187                 case pt_flame:
1188                         p->alpha -= frametime * 512;
1189                         p->vel[2] += gravity * 0.2;
1190                         if (p->alpha < 1)
1191                                 p->die = -1;
1192                         break;
1193                         /*
1194                 case pt_flamingdebris:
1195                         if (cl.time >= p->time2)
1196                         {
1197                                 p->time2 = cl.time + 0.01;
1198                                 particle2(pt_flame, p->color, particletexture, TPOLYTYPE_ADD, false, 4, p->alpha, 999, 0, p->org, 0, 50);
1199                         }
1200                         p->alpha -= frametime * 512;
1201                         p->vel[2] -= gravity * 0.5f;
1202                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1203                                 p->die = -1;
1204                         if (p->alpha < 1)
1205                                 p->die = -1;
1206                         break;
1207                 case pt_smokingdebris:
1208                         if (cl.time >= p->time2)
1209                         {
1210                                 p->time2 = cl.time + 0.01;
1211                                 particle2(pt_flame, 15, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 4, p->alpha, 999, 0, p->org, 0, 50);
1212                         }
1213                         p->alpha -= frametime * 512;
1214                         p->vel[2] -= gravity * 0.5f;
1215                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1216                                 p->die = -1;
1217                         if (p->alpha < 1)
1218                                 p->die = -1;
1219                         break;
1220                 case pt_flamefall:
1221                         p->alpha -= frametime * 512;
1222                         p->vel[2] -= gravity * 0.5f;
1223                         if (p->alpha < 1)
1224                                 p->die = -1;
1225                         break;
1226                         */
1227                 case pt_glow:
1228                         if (p->time2)
1229                                 p->die = -1;
1230                         p->time2 = 1;
1231                         break;
1232                 case pt_decal:
1233                         if (cl.time > p->time2)
1234                         {
1235                                 p->alpha -= frametime * 256;
1236                                 if (p->alpha < 1)
1237                                         p->die = -1;
1238                         }
1239                         if (p->alpha < 64)
1240                                 p->die = -1;
1241                         break;
1242                 default:
1243                         printf("unknown particle type %i\n", p->type);
1244                         p->die = -1;
1245                         break;
1246                 }
1247
1248                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1249                 if (p->die < cl.time)
1250                         freeparticles[j++] = p;
1251                 else
1252                 {
1253                         maxparticle = i;
1254                         activeparticles++;
1255                 }
1256         }
1257         // fill in gaps to compact the array
1258         i = 0;
1259         while (maxparticle >= activeparticles)
1260         {
1261                 *freeparticles[i++] = particles[maxparticle--];
1262                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1263                         maxparticle--;
1264         }
1265         numparticles = activeparticles;
1266 }
1267
1268 void R_DrawParticles (void)
1269 {
1270         particle_t              *p;
1271         int                             i, r,g,b,a, dynlight;
1272         float                   scale, scale2, minparticledist;
1273         byte                    *color24;
1274         vec3_t                  up, right, uprightangles, up2, right2, tempcolor, corner, decalright, decalup, v;
1275
1276         // LordHavoc: early out condition
1277         if ((!numparticles) || (!r_drawparticles.value))
1278                 return;
1279
1280         dynlight = r_dynamicparticles.value;
1281         if (!r_dynamic.value)
1282                 dynlight = 0;
1283         c_particles += numparticles;
1284
1285         VectorScale (vup, 1.5, up);
1286         VectorScale (vright, 1.5, right);
1287
1288         uprightangles[0] = 0;
1289         uprightangles[1] = r_refdef.viewangles[1];
1290         uprightangles[2] = 0;
1291         AngleVectors (uprightangles, NULL, right2, up2);
1292
1293         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1294
1295         for (i = 0, p = particles;i < numparticles;i++, p++)
1296         {
1297                 // LordHavoc: unnecessary (array was already compacted)
1298 //              if (p->die < cl.time)
1299 //                      continue;
1300
1301                 if (p->type == pt_decal)
1302                 {
1303                         VectorSubtract(p->org, r_refdef.vieworg, v);
1304                         if (DotProduct(p->direction, v) < 0)
1305                                 continue;
1306                 }
1307
1308                 // LordHavoc: only render if not too close
1309                 if (DotProduct(p->org, vpn) < minparticledist)
1310                         continue;
1311
1312                 color24 = (byte *) &d_8to24table[(int)p->color];
1313                 r = color24[0];
1314                 g = color24[1];
1315                 b = color24[2];
1316                 a = p->alpha;
1317                 if (dynlight && (p->dynlight || dynlight >= 2)) // LordHavoc: only light blood and smoke
1318                 {
1319                         R_CompleteLightPoint(tempcolor, p->org);
1320                         r = (r * (int) tempcolor[0]) >> 7;
1321                         g = (g * (int) tempcolor[1]) >> 7;
1322                         b = (b * (int) tempcolor[2]) >> 7;
1323                 }
1324                 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1325                 scale = p->scale * -0.5;scale2 = p->scale;
1326                 if (p->type == pt_decal)
1327                 {
1328                         VectorVectors(p->direction, decalright, decalup);
1329                         corner[0] = p->org[0] + decalup[0]*scale + decalright[0]*scale;
1330                         corner[1] = p->org[1] + decalup[1]*scale + decalright[1]*scale;
1331                         corner[2] = p->org[2] + decalup[2]*scale + decalright[2]*scale;
1332                         transpolyvert(corner[0]                                           , corner[1]                                           , corner[2]                                           , 0,1,r,g,b,a);
1333                         transpolyvert(corner[0] + decalup[0]*scale2                       , corner[1] + decalup[1]*scale2                       , corner[2] + decalup[2]*scale2                       , 0,0,r,g,b,a);
1334                         transpolyvert(corner[0] + decalup[0]*scale2 + decalright[0]*scale2, corner[1] + decalup[1]*scale2 + decalright[1]*scale2, corner[2] + decalup[2]*scale2 + decalright[2]*scale2, 1,0,r,g,b,a);
1335                         transpolyvert(corner[0]                     + decalright[0]*scale2, corner[1]                     + decalright[1]*scale2, corner[2]                     + decalright[2]*scale2, 1,1,r,g,b,a);
1336                 }
1337                 else if (p->tex == rainparticletexture) // rain streak
1338                 {
1339                         corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1340                         corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1341                         corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1342                         transpolyvert(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,r,g,b,a);
1343                         transpolyvert(corner[0] + up2[0]*scale2                   , corner[1] + up2[1]*scale2                   , corner[2] + up2[2]*scale2                   , 0,0,r,g,b,a);
1344                         transpolyvert(corner[0] + up2[0]*scale2 + right2[0]*scale2, corner[1] + up2[1]*scale2 + right2[1]*scale2, corner[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1345                         transpolyvert(corner[0]                 + right2[0]*scale2, corner[1]                 + right2[1]*scale2, corner[2]                 + right2[2]*scale2, 1,1,r,g,b,a);
1346                 }
1347                 else
1348                 {
1349                         corner[0] = p->org[0] + up[0]*scale + right[0]*scale;
1350                         corner[1] = p->org[1] + up[1]*scale + right[1]*scale;
1351                         corner[2] = p->org[2] + up[2]*scale + right[2]*scale;
1352                         transpolyvert(corner[0]                                 , corner[1]                                 , corner[2]                                 , 0,1,r,g,b,a);
1353                         transpolyvert(corner[0] + up[0]*scale2                  , corner[1] + up[1]*scale2                  , corner[2] + up[2]*scale2                  , 0,0,r,g,b,a);
1354                         transpolyvert(corner[0] + up[0]*scale2 + right[0]*scale2, corner[1] + up[1]*scale2 + right[1]*scale2, corner[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1355                         transpolyvert(corner[0]                + right[0]*scale2, corner[1]                + right[1]*scale2, corner[2]                + right[2]*scale2, 1,1,r,g,b,a);
1356                 }
1357                 transpolyend();
1358         }
1359 }