smoke and blood changes
[xonotic/darkplaces.git] / r_part.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 #define MAX_PARTICLES                   4096    // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum {
28         pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash
29 } ptype_t;
30
31 typedef struct particle_s
32 {
33         vec3_t          org;
34         float           color;
35         vec3_t          vel;
36         float           die;
37         ptype_t         type;
38         float           scale;
39         short           texnum;
40         float           alpha; // 0-255
41         float           time2; // used for various things (snow fluttering, for example)
42         vec3_t          oldorg;
43         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
44 } particle_t;
45
46 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
47 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
48 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
49
50 int             particletexture;
51 int             smokeparticletexture[8];
52 int             rainparticletexture;
53 int             bubbleparticletexture;
54
55 particle_t      *particles;
56 int                     r_numparticles;
57
58 vec3_t                  r_pright, r_pup, r_ppn;
59
60 int                     numparticles;
61 particle_t      **freeparticles; // list used only in compacting particles array
62
63 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
64 #define ALLOCPARTICLE \
65         if (numparticles >= r_numparticles)\
66                 return;\
67         p = &particles[numparticles++];
68
69 cvar_t r_particles = {"r_particles", "1"};
70 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
71
72 byte shadebubble(float dx, float dy, vec3_t light)
73 {
74         float   dz, f, dot;
75         vec3_t  normal;
76         if ((dx*dx+dy*dy) < 1) // it does hit the sphere
77         {
78                 dz = 1 - (dx*dx+dy*dy);
79                 f = 0;
80                 // back side
81                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
82                 VectorNormalize(normal);
83                 dot = DotProduct(normal, light);
84                 if (dot > 0.5) // interior reflection
85                         f += ((dot *  2) - 1);
86                 else if (dot < -0.5) // exterior reflection
87                         f += ((dot * -2) - 1);
88                 // front side
89                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
90                 VectorNormalize(normal);
91                 dot = DotProduct(normal, light);
92                 if (dot > 0.5) // interior reflection
93                         f += ((dot *  2) - 1);
94                 else if (dot < -0.5) // exterior reflection
95                         f += ((dot * -2) - 1);
96                 f *= 128;
97                 f += 16; // just to give it a haze so you can see the outline
98                 f = bound(0, f, 255);
99                 return (byte) f;
100         }
101         else
102                 return 0;
103 }
104
105 void R_InitParticleTexture (void)
106 {
107         int             x,y,d,i,m;
108         float   dx, dy;
109         byte    data[32][32][4], noise1[32][32], noise2[32][32];
110         vec3_t  light;
111
112         for (y = 0;y < 32;y++)
113         {
114                 dy = y - 16;
115                 for (x = 0;x < 32;x++)
116                 {
117                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
118                         dx = x - 16;
119                         d = (255 - (dx*dx+dy*dy));
120                         if (d < 0) d = 0;
121                         data[y][x][3] = (byte) d;
122                 }
123         }
124         particletexture = GL_LoadTexture ("particletexture", 32, 32, &data[0][0][0], true, true, 4);
125
126         for (i = 0;i < 8;i++)
127         {
128                 do
129                 {
130                         fractalnoise(&noise1[0][0], 32, 1);
131                         fractalnoise(&noise2[0][0], 32, 8);
132                         m = 0;
133                         for (y = 0;y < 32;y++)
134                         {
135                                 dy = y - 16;
136                                 for (x = 0;x < 32;x++)
137                                 {
138                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
139                                         dx = x - 16;
140                                         d = ((noise2[y][x] * 384) >> 8) - 128;
141                                         if (d > 0)
142                                         {
143                                                 if (d > 255)
144                                                         d = 255;
145                                                 d = (d * (255 - (int) (dx*dx+dy*dy))) >> 7;
146                                                 if (d < 0) d = 0;
147                                                 if (d > 255) d = 255;
148                                                 data[y][x][3] = (byte) d;
149                                                 if (m < d)
150                                                         m = d;
151                                         }
152                                         else
153                                                 data[y][x][3] = 0;
154                                 }
155                         }
156                 }
157                 while (m < 192);
158
159                 smokeparticletexture[i] = GL_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], true, true, 4);
160         }
161
162         light[0] = 1;light[1] = 1;light[2] = 1;
163         VectorNormalize(light);
164         for (x=0 ; x<32 ; x++)
165         {
166                 for (y=0 ; y<32 ; y++)
167                 {
168                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
169                         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);
170                 }
171         }
172         rainparticletexture = GL_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], true, true, 4);
173
174         light[0] = 1;light[1] = 1;light[2] = 1;
175         VectorNormalize(light);
176         for (x=0 ; x<32 ; x++)
177         {
178                 for (y=0 ; y<32 ; y++)
179                 {
180                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
181                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
182                 }
183         }
184         bubbleparticletexture = GL_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], true, true, 4);
185 }
186
187 void r_part_start()
188 {
189         particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t));
190         freeparticles = (void *) malloc (r_numparticles * sizeof(particle_t *));
191         R_InitParticleTexture ();
192 }
193
194 void r_part_shutdown()
195 {
196         free(particles);
197         free(freeparticles);
198 }
199
200 /*
201 ===============
202 R_InitParticles
203 ===============
204 */
205 void R_Particles_Init (void)
206 {
207         int             i;
208
209         i = COM_CheckParm ("-particles");
210
211         if (i)
212         {
213                 r_numparticles = (int)(atoi(com_argv[i+1]));
214                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
215                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
216         }
217         else
218         {
219                 r_numparticles = MAX_PARTICLES;
220         }
221
222         Cvar_RegisterVariable (&r_particles);
223         Cvar_RegisterVariable (&r_dynamicparticles);
224
225         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
226 }
227
228 #define particle(ptype, pcolor, ptex, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
229 {\
230         particle_t      *p;\
231         ALLOCPARTICLE\
232         p->type = (ptype);\
233         p->color = (pcolor);\
234         p->texnum = (ptex);\
235         p->scale = (pscale);\
236         p->alpha = (palpha);\
237         p->die = cl.time + (ptime);\
238         p->org[0] = (px);\
239         p->org[1] = (py);\
240         p->org[2] = (pz);\
241         p->vel[0] = (pvx);\
242         p->vel[1] = (pvy);\
243         p->vel[2] = (pvz);\
244 }
245 #define particle2(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, poscale, pvscale)\
246 {\
247         particle_t      *p;\
248         ALLOCPARTICLE\
249         p->type = (ptype);\
250         p->color = (pcolor);\
251         p->texnum = (ptex);\
252         p->scale = (pscale);\
253         p->alpha = (palpha);\
254         p->die = cl.time + (ptime);\
255         p->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
256         p->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
257         p->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
258         p->vel[0] = lhrandom(-(pvscale), (pvscale));\
259         p->vel[1] = lhrandom(-(pvscale), (pvscale));\
260         p->vel[2] = lhrandom(-(pvscale), (pvscale));\
261 }
262 #define particle3(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
263 {\
264         particle_t      *p;\
265         ALLOCPARTICLE\
266         p->type = (ptype);\
267         p->color = (pcolor);\
268         p->texnum = (ptex);\
269         p->scale = (pscale);\
270         p->alpha = (palpha);\
271         p->die = cl.time + (ptime);\
272         p->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
273         p->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
274         p->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
275         p->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
276         p->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
277         p->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
278 }
279 /*
280 void particle(int type, int color, int tex, float scale, int alpha, float time, float x, float y, float z, float vx, float vy, float vz)
281 {
282         particle_t      *p;
283         ALLOCPARTICLE
284
285         p->type = type;
286         p->color = color;
287         p->texnum = tex;
288         p->scale = scale;
289         p->alpha = alpha;
290         p->die = cl.time + time;
291         p->org[0] = x;
292         p->org[1] = y;
293         p->org[2] = z;
294         p->vel[0] = vx;
295         p->vel[1] = vy;
296         p->vel[2] = vz;
297 }
298 void particle2(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float oscale, float vscale)
299 {
300         particle_t      *p;
301         ALLOCPARTICLE
302
303         p->type = type;
304         p->color = color;
305         p->texnum = tex;
306         p->scale = scale;
307         p->alpha = alpha;
308         p->die = cl.time + time;
309         p->org[0] = lhrandom(-oscale, oscale) + base[0];
310         p->org[1] = lhrandom(-oscale, oscale) + base[1];
311         p->org[2] = lhrandom(-oscale, oscale) + base[2];
312         p->vel[0] = lhrandom(-vscale, vscale);
313         p->vel[1] = lhrandom(-vscale, vscale);
314         p->vel[2] = lhrandom(-vscale, vscale);
315 }
316 void particle3(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float scalex, float scaley, float scalez, float vscalex, float vscaley, float vscalez)
317 {
318         particle_t      *p;
319         ALLOCPARTICLE
320
321         p->type = type;
322         p->color = color;
323         p->texnum = tex;
324         p->scale = scale;
325         p->alpha = alpha;
326         p->die = cl.time + time;
327         p->org[0] = lhrandom(-scalex, scalex) + base[0];
328         p->org[1] = lhrandom(-scaley, scaley) + base[1];
329         p->org[2] = lhrandom(-scalez, scalez) + base[2];
330         p->vel[0] = lhrandom(-vscalex, vscalex);
331         p->vel[1] = lhrandom(-vscaley, vscaley);
332         p->vel[2] = lhrandom(-vscalez, vscalez);
333 }
334 */
335
336 /*
337 ===============
338 R_EntityParticles
339 ===============
340 */
341
342 #define NUMVERTEXNORMALS        162
343 extern  float   r_avertexnormals[NUMVERTEXNORMALS][3];
344 vec3_t  avelocities[NUMVERTEXNORMALS];
345 float   beamlength = 16;
346 vec3_t  avelocity = {23, 7, 3};
347 float   partstep = 0.01;
348 float   timescale = 0.01;
349
350 void R_EntityParticles (entity_t *ent)
351 {
352         int                     count;
353         int                     i;
354         float           angle;
355         float           sp, sy, cp, cy;
356         vec3_t          forward;
357         float           dist;
358         if (!r_particles.value) return; // LordHavoc: particles are optional
359         
360         dist = 64;
361         count = 50;
362
363         if (!avelocities[0][0])
364                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
365                         avelocities[0][i] = (rand()&255) * 0.01;
366
367         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
368         {
369                 angle = cl.time * avelocities[i][0];
370                 sy = sin(angle);
371                 cy = cos(angle);
372                 angle = cl.time * avelocities[i][1];
373                 sp = sin(angle);
374                 cp = cos(angle);
375         
376                 forward[0] = cp*cy;
377                 forward[1] = cp*sy;
378                 forward[2] = -sp;
379
380                 particle(pt_static, 0x6f, particletexture, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
381         }
382 }
383
384
385 /*
386 ===============
387 R_ClearParticles
388 ===============
389 */
390 void R_ClearParticles (void)
391 {
392 //      int             i;
393 //      free_particles = &particles[0];
394 //      active_particles = NULL;
395
396 //      for (i=0 ;i<r_numparticles ; i++)
397 //              particles[i].next = &particles[i+1];
398 //      particles[r_numparticles-1].next = NULL;
399
400         numparticles = 0;
401 }
402
403
404 void R_ReadPointFile_f (void)
405 {
406         FILE    *f;
407         vec3_t  org;
408         int             r;
409         int             c;
410         char    name[MAX_OSPATH];
411         
412         sprintf (name,"maps/%s.pts", sv.name);
413
414         COM_FOpenFile (name, &f, false);
415         if (!f)
416         {
417                 Con_Printf ("couldn't open %s\n", name);
418                 return;
419         }
420         
421         Con_Printf ("Reading %s...\n", name);
422         c = 0;
423         for (;;)
424         {
425                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
426                 if (r != 3)
427                         break;
428                 c++;
429                 
430                 if (numparticles >= r_numparticles)
431                 {
432                         Con_Printf ("Not enough free particles\n");
433                         break;
434                 }
435                 particle(pt_static, (-c)&15, particletexture, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
436         }
437
438         fclose (f);
439         Con_Printf ("%i points read\n", c);
440 }
441
442 /*
443 ===============
444 R_ParseParticleEffect
445
446 Parse an effect out of the server message
447 ===============
448 */
449 void R_ParseParticleEffect (void)
450 {
451         vec3_t          org, dir;
452         int                     i, count, msgcount, color;
453         
454         for (i=0 ; i<3 ; i++)
455                 org[i] = MSG_ReadCoord ();
456         for (i=0 ; i<3 ; i++)
457                 dir[i] = MSG_ReadChar () * (1.0/16);
458         msgcount = MSG_ReadByte ();
459         color = MSG_ReadByte ();
460
461 if (msgcount == 255)
462         count = 1024;
463 else
464         count = msgcount;
465         
466         R_RunParticleEffect (org, dir, color, count);
467 }
468         
469 /*
470 ===============
471 R_ParticleExplosion
472
473 ===============
474 */
475 void R_ParticleExplosion (vec3_t org, int smoke)
476 {
477         int                     i;
478         if (!r_particles.value) return; // LordHavoc: particles are optional
479
480         particle(pt_smokecloud, (rand()&7) + 8, smokeparticletexture[rand()&7], 30, 160, 2, org[0], org[1], org[2], 0, 0, 0);
481
482         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
483         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
484         {
485                 for (i=0 ; i<128 ; i++)
486                         particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, lhrandom(1, 2), 255, 2, org, 16, 96);
487         }
488         else
489         {
490                 for (i = 0;i < 256;i++)
491                         particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1.5, lhrandom(0, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
492         }
493
494 }
495
496 /*
497 ===============
498 R_ParticleExplosion2
499
500 ===============
501 */
502 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
503 {
504         int                     i;
505         if (!r_particles.value) return; // LordHavoc: particles are optional
506
507         for (i = 0;i < 512;i++)
508                 particle2(pt_fade, colorStart + (i % colorLength), particletexture, 1.5, 255, 0.3, org, 8, 192);
509 }
510
511 /*
512 ===============
513 R_BlobExplosion
514
515 ===============
516 */
517 void R_BlobExplosion (vec3_t org)
518 {
519         int                     i;
520         if (!r_particles.value) return; // LordHavoc: particles are optional
521         
522         for (i=0 ; i<512 ; i++)
523                 particle3(pt_blob, 66+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
524         for (i=0 ; i<512 ; i++)
525                 particle3(pt_blob2, 150+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
526 }
527
528 /*
529 ===============
530 R_RunParticleEffect
531
532 ===============
533 */
534 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
535 {
536         if (!r_particles.value) return; // LordHavoc: particles are optional
537         
538         if (count == 1024)
539         {
540                 R_ParticleExplosion(org, false);
541                 return;
542         }
543         color &= ~7;
544         if (count & 7)
545         {
546                 particle2(pt_fade, color + (rand()&7), particletexture, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
547                 count &= ~7;
548         }
549         count >>= 3;
550         while (count--)
551                 particle2(pt_fade, color + (rand()&7), particletexture, 6, 128, 1, org, 8, 15);
552 }
553
554 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
555 /*
556 ===============
557 R_SparkShower
558 ===============
559 */
560 void R_SparkShower (vec3_t org, vec3_t dir, int count)
561 {
562         if (!r_particles.value) return; // LordHavoc: particles are optional
563
564         // smoke puff
565         particle(pt_smokecloud, 12+(rand()&3), smokeparticletexture[rand()&7], 8, 64, 99, org[0], org[1], org[2], 0, 0, 0);
566         // sparks
567         while(count--)
568 //              particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, org, 4, 96);
569                 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
570 }
571
572 void R_BloodPuff (vec3_t org)
573 {
574         if (!r_particles.value) return; // LordHavoc: particles are optional
575
576         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
577         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
578         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
579 }
580
581 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
582 {
583         int                     j;
584         particle_t      *p;
585         vec3_t          diff;
586         vec3_t          center;
587         vec3_t          velscale;
588         if (!r_particles.value) return; // LordHavoc: particles are optional
589
590         VectorSubtract(maxs, mins, diff);
591         center[0] = (mins[0] + maxs[0]) * 0.5;
592         center[1] = (mins[1] + maxs[1]) * 0.5;
593         center[2] = (mins[2] + maxs[2]) * 0.5;
594         // FIXME: change velspeed back to 2.0x after fixing mod
595         velscale[0] = velspeed * 0.5 / diff[0];
596         velscale[1] = velspeed * 0.5 / diff[1];
597         velscale[2] = velspeed * 0.5 / diff[2];
598         
599         while (count--)
600         {
601                 ALLOCPARTICLE
602
603                 p->texnum = smokeparticletexture[rand()&7];
604                 p->scale = lhrandom(4, 6);
605                 p->alpha = 96 + (rand()&63);
606                 p->die = cl.time + 2;
607                 p->type = pt_bloodcloud;
608                 p->color = (rand()&3)+68;
609                 for (j=0 ; j<3 ; j++)
610                 {
611                         p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
612                         p->vel[j] = (p->org[j] - center[j]) * velscale[j];
613                 }
614         }
615 }
616
617 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
618 {
619         int                     j;
620         particle_t      *p;
621         vec3_t          diff;
622         float           t;
623         if (!r_particles.value) return; // LordHavoc: particles are optional
624         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
625         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
626         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
627
628         VectorSubtract(maxs, mins, diff);
629         
630         while (count--)
631         {
632                 ALLOCPARTICLE
633
634                 p->texnum = particletexture;
635                 p->scale = 6;
636                 p->alpha = 255;
637                 p->die = cl.time + 1 + (rand()&15)*0.0625;
638                 if (gravity)
639                         p->type = pt_grav;
640                 else
641                         p->type = pt_static;
642                 p->color = colorbase + (rand()&3);
643                 for (j=0 ; j<3 ; j++)
644                 {
645                         p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
646                         if (randomvel)
647                                 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
648                         else
649                                 p->vel[j] = 0;
650                 }
651         }
652 }
653
654 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
655 {
656         int                     i;
657         particle_t      *p;
658         vec3_t          diff;
659         vec3_t          org;
660         vec3_t          vel;
661         float           t, z;
662         if (!r_particles.value) return; // LordHavoc: particles are optional
663         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
664         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
665         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
666         if (dir[2] < 0) // falling
667         {
668                 t = (maxs[2] - mins[2]) / -dir[2];
669                 z = maxs[2];
670         }
671         else // rising??
672         {
673                 t = (maxs[2] - mins[2]) / dir[2];
674                 z = mins[2];
675         }
676         if (t < 0 || t > 2) // sanity check
677                 t = 2;
678         t += cl.time;
679
680         VectorSubtract(maxs, mins, diff);
681         
682         for (i=0 ; i<count ; i++)
683         {
684                 ALLOCPARTICLE
685
686                 vel[0] = dir[0] + (rand()&31) - 16;
687                 vel[1] = dir[1] + (rand()&31) - 16;
688                 vel[2] = dir[2] + (rand()&63) - 32;
689                 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
690                 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
691                 org[2] = z;
692
693                 p->alpha = 255;
694                 p->die = t;
695                 if (type == 1)
696                 {
697                         p->scale = 2;
698                         p->texnum = particletexture;
699                         p->type = pt_snow;
700                 }
701                 else // 0
702                 {
703                         p->scale = 3;
704                         p->texnum = rainparticletexture;
705                         p->type = pt_rain;
706                 }
707                 p->color = colorbase + (rand()&3);
708                 VectorCopy(org, p->org);
709                 VectorCopy(vel, p->vel);
710                 VectorCopy(vel, p->vel2);
711         }
712 }
713
714
715 /*
716 ===============
717 R_LavaSplash
718
719 ===============
720 */
721 void R_LavaSplash (vec3_t org)
722 {
723         int                     i, j;
724         particle_t      *p;
725         float           vel;
726         vec3_t          dir;
727         if (!r_particles.value) return; // LordHavoc: particles are optional
728
729         for (i=-128 ; i<128 ; i+=16)
730                 for (j=-128 ; j<128 ; j+=16)
731                 {
732                         ALLOCPARTICLE
733                 
734                         p->texnum = particletexture;
735                         p->scale = 10;
736                         p->alpha = 128;
737                         p->die = cl.time + 2 + (rand()&31) * 0.02;
738                         p->color = 224 + (rand()&7);
739                         p->type = pt_grav;
740                         
741                         dir[0] = j + (rand()&7);
742                         dir[1] = i + (rand()&7);
743                         dir[2] = 256;
744
745                         p->org[0] = org[0] + dir[0];
746                         p->org[1] = org[1] + dir[1];
747                         p->org[2] = org[2] + (rand()&63);
748
749                         VectorNormalize (dir);                                          
750                         vel = 50 + (rand()&63);
751                         VectorScale (dir, vel, p->vel);
752                 }
753 }
754
755 /*
756 ===============
757 R_TeleportSplash
758
759 ===============
760 */
761 void R_TeleportSplash (vec3_t org)
762 {
763         int                     i, j, k;
764         particle_t      *p;
765         if (!r_particles.value) return; // LordHavoc: particles are optional
766
767         for (i=-16 ; i<16 ; i+=8)
768                 for (j=-16 ; j<16 ; j+=8)
769                         for (k=-24 ; k<32 ; k+=8)
770                         {
771                                 ALLOCPARTICLE
772                 
773                                 p->texnum = particletexture;
774                                 p->scale = 1;
775                                 p->alpha = lhrandom(32,128);
776                                 p->die = cl.time + 5;
777                                 p->color = 254;
778                                 p->type = pt_fade;
779                                 
780                                 p->org[0] = org[0] + i + (rand()&7);
781                                 p->org[1] = org[1] + j + (rand()&7);
782                                 p->org[2] = org[2] + k + (rand()&7);
783         
784                                 p->vel[0] = i*2 + (rand()%25) - 12;
785                                 p->vel[1] = j*2 + (rand()%25) - 12;
786                                 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
787                         }
788 }
789
790 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
791 {
792         vec3_t          vec;
793         float           len, dec = 0, t, nt, speed;
794         int                     j, contents, bubbles;
795         particle_t      *p;
796         if (!r_particles.value) return; // LordHavoc: particles are optional
797
798         t = cl.oldtime;
799         nt = cl.time;
800         if (ent->trail_leftover < 0)
801                 ent->trail_leftover = 0;
802         t += ent->trail_leftover;
803         ent->trail_leftover -= (cl.time - cl.oldtime);
804         if (t >= cl.time)
805                 return;
806
807         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
808         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
809                 return;
810
811         VectorSubtract (end, start, vec);
812         len = VectorNormalizeLength (vec);
813         if (len <= 0.01f)
814                 return;
815         speed = len / (nt - t);
816
817         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
818
819         while (t < nt)
820         {
821                 ALLOCPARTICLE
822                 
823                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
824                 p->die = cl.time + 2;
825
826                 switch (type)
827                 {
828                         case 0: // rocket trail
829                         case 1: // grenade trail
830                                 if (bubbles)
831                                 {
832                                         dec = 0.005f;
833                                         p->texnum = bubbleparticletexture;
834                                         p->scale = lhrandom(1,2);
835                                         p->alpha = 255;
836                                         p->color = (rand()&3)+12;
837                                         p->type = pt_bubble;
838                                         p->die = cl.time + 2;
839                                         for (j=0 ; j<3 ; j++)
840                                         {
841                                                 p->vel[j] = (rand()&31)-16;
842                                                 p->org[j] = start[j] + ((rand()&3)-2);
843                                         }
844                                 }
845                                 else
846                                 {
847                                         dec = 0.02f;
848                                         p->texnum = smokeparticletexture[rand()&7];
849                                         p->scale = lhrandom(8, 12);
850                                         p->alpha = 64 + (rand()&31);
851                                         p->color = (rand()&3)+12;
852                                         p->type = pt_smoke;
853                                         p->die = cl.time + 10000;
854                                         VectorCopy(start, p->org);
855                                 }
856                                 break;
857
858                                 /*
859                         case 1: // smoke smoke
860                                 dec = 0.016f;
861                                 p->texnum = smokeparticletexture;
862                                 p->scale = lhrandom(6,9);
863                                 p->alpha = 64;
864                                 if (r_smokecolor.value)
865                                         p->color = r_smokecolor.value;
866                                 else
867                                         p->color = (rand()&3)+12;
868                                 p->type = pt_smoke;
869                                 p->die = cl.time + 1;
870                                 VectorCopy(start, p->org);
871                                 break;
872                                 */
873
874                         case 2: // blood
875                         case 4: // slight blood
876                                 dec = 0.025f;
877                                 p->texnum = smokeparticletexture[rand()&7];
878                                 p->scale = lhrandom(4, 6);
879                                 p->alpha = type == 4 ? 192 : 255;
880                                 p->color = (rand()&3)+68;
881                                 p->type = pt_bloodcloud;
882                                 p->die = cl.time + 9999;
883                                 for (j=0 ; j<3 ; j++)
884                                 {
885                                         p->vel[j] = (rand()&15)-8;
886                                         p->org[j] = start[j] + ((rand()&3)-2);
887                                 }
888                                 break;
889
890                         case 3:
891                         case 5: // tracer
892                                 dec = 0.02f;
893                                 p->texnum = smokeparticletexture[rand()&7];
894                                 p->scale = 4;
895                                 p->alpha = 64 + (rand()&31);
896                                 p->color = type == 3 ? 56 : 234;
897                                 p->type = pt_fade;
898                                 p->die = cl.time + 10000;
899                                 VectorCopy(start, p->org);
900                                 break;
901
902                         case 6: // voor trail
903                                 dec = 0.05f; // sparse trail
904                                 p->texnum = smokeparticletexture[rand()&7];
905                                 p->scale = lhrandom(3, 5);
906                                 p->alpha = 255;
907                                 p->color = 9*16 + 8 + (rand()&3);
908                                 p->type = pt_fade;
909                                 p->die = cl.time + 2;
910                                 for (j=0 ; j<3 ; j++)
911                                 {
912                                         p->vel[j] = (rand()&15)-8;
913                                         p->org[j] = start[j] + ((rand()&3)-2);
914                                 }
915                                 break;
916
917                         case 7: // Nehahra smoke tracer
918                                 dec = 0.14f;
919                                 p->texnum = smokeparticletexture[rand()&7];
920                                 p->scale = lhrandom(8, 12);
921                                 p->alpha = 64;
922                                 p->color = (rand()&3)+12;
923                                 p->type = pt_smoke;
924                                 p->die = cl.time + 10000;
925                                 for (j=0 ; j<3 ; j++)
926                                         p->org[j] = start[j] + ((rand()&3)-2);
927                                 break;
928                 }
929                 
930                 t += dec;
931                 dec *= speed;
932                 VectorMA (start, dec, vec, start);
933         }
934         ent->trail_leftover = t - cl.time;
935 }
936
937 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
938 {
939         vec3_t          vec;
940         int                     len;
941         if (!r_particles.value) return; // LordHavoc: particles are optional
942
943         VectorSubtract (end, start, vec);
944         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
945         VectorScale(vec, 3, vec);
946         while (len--)
947         {
948                 particle(pt_smoke, color, particletexture, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
949                 VectorAdd (start, vec, start);
950         }
951 }
952
953
954 /*
955 ===============
956 R_DrawParticles
957 ===============
958 */
959 extern  cvar_t  sv_gravity;
960 void R_CompleteLightPoint (vec3_t color, vec3_t p);
961
962 void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
963
964 void R_DrawParticles (void)
965 {
966         particle_t              *p;
967         int                             i, r,g,b,a;
968         float                   gravity, dvel, frametime, scale, scale2, minparticledist;
969         byte                    *color24;
970         vec3_t                  up, right, uprightangles, forward2, up2, right2, tempcolor, v;
971         int                             activeparticles, maxparticle, j, k;
972
973         // LordHavoc: early out condition
974         if (!numparticles)
975                 return;
976
977         VectorScale (vup, 1.5, up);
978         VectorScale (vright, 1.5, right);
979
980         uprightangles[0] = 0;
981         uprightangles[1] = r_refdef.viewangles[1];
982         uprightangles[2] = 0;
983         AngleVectors (uprightangles, forward2, right2, up2);
984
985         frametime = cl.time - cl.oldtime;
986         gravity = frametime * sv_gravity.value;
987         dvel = 1+4*frametime;
988
989         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
990
991         activeparticles = 0;
992         maxparticle = -1;
993         j = 0;
994         for (k = 0, p = particles;k < numparticles;k++, p++)
995         {
996                 if (p->die < cl.time)
997                 {
998                         freeparticles[j++] = p;
999                         continue;
1000                 }
1001                 maxparticle = k;
1002                 activeparticles++;
1003
1004                 // LordHavoc: only render if not too close
1005                 if (DotProduct(p->org, vpn) >= minparticledist)
1006                 {
1007                         color24 = (byte *) &d_8to24table[(int)p->color];
1008                         r = color24[0];
1009                         g = color24[1];
1010                         b = color24[2];
1011                         a = p->alpha;
1012                         if (r_dynamicparticles.value)
1013                         {
1014                                 R_CompleteLightPoint(tempcolor, p->org);
1015                                 r = (r * (int) tempcolor[0]) >> 7;
1016                                 g = (g * (int) tempcolor[1]) >> 7;
1017                                 b = (b * (int) tempcolor[2]) >> 7;
1018                         }
1019                         transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1020                         scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1021                         if (p->texnum == rainparticletexture) // rain streak
1022                         {
1023                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale , p->org[1] + up2[1]*scale  + right2[1]*scale , p->org[2] + up2[2]*scale  + right2[2]*scale , 0,1,r,g,b,a);
1024                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale , p->org[1] + up2[1]*scale2 + right2[1]*scale , p->org[2] + up2[2]*scale2 + right2[2]*scale , 0,0,r,g,b,a);
1025                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale2, p->org[1] + up2[1]*scale2 + right2[1]*scale2, p->org[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1026                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale2, p->org[1] + up2[1]*scale  + right2[1]*scale2, p->org[2] + up2[2]*scale  + right2[2]*scale2, 1,1,r,g,b,a);
1027                         }
1028                         else
1029                         {
1030                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale , 0,1,r,g,b,a);
1031                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 0,0,r,g,b,a);
1032                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale2, p->org[1] + up[1]*scale2 + right[1]*scale2, p->org[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1033                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2, 1,1,r,g,b,a);
1034                         }
1035                         transpolyend();
1036                 }
1037
1038                 VectorCopy(p->org, p->oldorg);
1039                 p->org[0] += p->vel[0]*frametime;
1040                 p->org[1] += p->vel[1]*frametime;
1041                 p->org[2] += p->vel[2]*frametime;
1042                 
1043                 switch (p->type)
1044                 {
1045                 case pt_static:
1046                         break;
1047
1048                 case pt_blob:
1049                         for (i=0 ; i<3 ; i++)
1050                                 p->vel[i] *= dvel;
1051                         break;
1052
1053                 case pt_blob2:
1054                         for (i=0 ; i<2 ; i++)
1055                                 p->vel[i] *= dvel;
1056                         break;
1057
1058                 case pt_grav:
1059                         p->vel[2] -= gravity;
1060                         break;
1061 // LordHavoc: for smoke trails
1062                 case pt_smoke:
1063                         p->scale += frametime * 6;
1064                         p->alpha -= frametime * 128;
1065                         if (p->alpha < 1)
1066                                 p->die = -1;
1067                         break;
1068                 case pt_snow:
1069                         if (cl.time > p->time2)
1070                         {
1071                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1072                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1073                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1074                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1075                         }
1076                         break;
1077                 case pt_bloodcloud:
1078 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1079 //                      {
1080 //                              p->die = -1;
1081 //                              break;
1082 //                      }
1083                         p->scale += frametime * 16;
1084                         p->alpha -= frametime * 512;
1085                         if (p->alpha < 1 || p->scale < 1)
1086                                 p->die = -1;
1087                         break;
1088                 case pt_fallfadespark:
1089                         p->alpha -= frametime * 256;
1090                         p->vel[2] -= gravity;
1091                         if (p->alpha < 1)
1092                                 p->die = -1;
1093                         break;
1094                 case pt_fade:
1095                         p->alpha -= frametime * 512;
1096                         if (p->alpha < 1)
1097                                 p->die = -1;
1098                         break;
1099                 case pt_bubble:
1100                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1101                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1102                         {
1103                                 p->texnum = smokeparticletexture[rand()&7];
1104                                 p->type = pt_splash;
1105                                 p->alpha = 96;
1106                                 p->scale = 5;
1107                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1108                                 p->die = cl.time + 1000;
1109 //                              p->die = -1;
1110                         }
1111                         p->vel[2] += gravity * 0.25;
1112                         p->vel[0] *= (1 - (frametime * 0.0625));
1113                         p->vel[1] *= (1 - (frametime * 0.0625));
1114                         p->vel[2] *= (1 - (frametime * 0.0625));
1115                         if (cl.time > p->time2)
1116                         {
1117                                 p->time2 = cl.time + lhrandom(0, 0.5);
1118                                 p->vel[0] += lhrandom(-32,32);
1119                                 p->vel[1] += lhrandom(-32,32);
1120                                 p->vel[2] += lhrandom(-32,32);
1121                         }
1122                         p->alpha -= frametime * 64;
1123                         if (p->alpha < 1)
1124                                 p->die = -1;
1125                         break;
1126                 case pt_smokecloud:
1127                         p->scale += frametime * 60;
1128                         p->alpha -= frametime * 96;
1129                         if (p->alpha < 1)
1130                                 p->die = -1;
1131                         break;
1132                 case pt_splash:
1133                         p->scale += frametime * 24;
1134                         p->alpha -= frametime * 256;
1135                         if (p->alpha < 1)
1136                                 p->die = -1;
1137                         break;
1138                 case pt_rain:
1139                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1140                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1141                         {
1142                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1143                                         break; // still in solid
1144                                 p->die = cl.time + 1000;
1145                                 switch (a)
1146                                 {
1147                                 case CONTENTS_LAVA:
1148                                 case CONTENTS_SLIME:
1149                                         p->texnum = smokeparticletexture[rand()&7];
1150                                         p->type = pt_smokecloud;
1151                                         p->alpha = 64;
1152                                         p->vel[2] = 96;
1153                                         break;
1154                                 case CONTENTS_WATER:
1155                                         p->texnum = smokeparticletexture[rand()&7];
1156                                         p->type = pt_splash;
1157                                         p->alpha = 96;
1158                                         p->scale = 5;
1159                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1160 //                                      p->texnum = bubbleparticletexture;
1161 //                                      p->type = pt_bubble;
1162 //                                      p->vel[2] *= 0.1;
1163                                         break;
1164                                 default: // CONTENTS_SOLID and any others
1165                                         TraceLine(p->oldorg, p->org, v);
1166                                         VectorCopy(v, p->org);
1167                                         p->texnum = smokeparticletexture[rand()&7];
1168                                         p->type = pt_splash;
1169                                         p->alpha = 96;
1170                                         p->scale = 5;
1171                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1172                                         p->die = cl.time + 1000;
1173                                         break;
1174                                 }
1175                         }
1176                         break;
1177                 }
1178         }
1179         // fill in gaps to compact the array
1180         i = 0;
1181         while (maxparticle >= activeparticles)
1182         {
1183                 *freeparticles[i++] = particles[maxparticle--];
1184                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1185                         maxparticle--;
1186         }
1187         numparticles = activeparticles;
1188 }
1189