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