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