***map loader generates portals for the map*** (can you tell this is a big deal? :)
[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                   16384   // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum
28 {
29         pt_static, pt_grav, pt_slowgrav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_spark, pt_bubble, pt_fade, pt_steam, pt_splash, pt_splashpuff, pt_flame/*, pt_decal*/, pt_blood, pt_oneframe, pt_lavasplash
30 }
31 ptype_t;
32
33 typedef struct particle_s
34 {
35         vec3_t          org;
36         float           color;
37         vec3_t          vel;
38         float           die;
39         ptype_t         type;
40         float           scale;
41         rtexture_t      *tex;
42         byte            dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
43         byte            rendermode; // a TPOLYTYPE_ value
44         byte            pad1;
45         byte            pad2;
46         float           alpha; // 0-255
47         float           time2; // used for various things (snow fluttering, for example)
48         float           bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical of bouncing particles)
49         vec3_t          oldorg;
50         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
51 //      vec3_t          direction; // used by decals
52 //      vec3_t          decalright; // used by decals
53 //      vec3_t          decalup; // used by decals
54 }
55 particle_t;
56
57 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
58
59 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
60 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
61 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
62
63 rtexture_t *particletexture;
64 rtexture_t *smokeparticletexture[8];
65 rtexture_t *rainparticletexture;
66 rtexture_t *bubbleparticletexture;
67 rtexture_t *bulletholetexture[8];
68 rtexture_t *rocketglowparticletexture;
69
70 particle_t      *particles;
71 int                     r_numparticles;
72
73 vec3_t                  r_pright, r_pup, r_ppn;
74
75 int                     numparticles;
76 particle_t      **freeparticles; // list used only in compacting particles array
77
78 cvar_t r_particles = {"r_particles", "1", true};
79 cvar_t r_drawparticles = {"r_drawparticles", "1"};
80 cvar_t r_particles_lighting = {"r_particles_lighting", "1", true};
81 cvar_t r_particles_bloodshowers = {"r_particles_bloodshowers", "1", true};
82 cvar_t r_particles_blood = {"r_particles_blood", "1", true};
83 cvar_t r_particles_smoke = {"r_particles_smoke", "1", true};
84 cvar_t r_particles_sparks = {"r_particles_sparks", "1", true};
85 cvar_t r_particles_bubbles = {"r_particles_bubbles", "1", true};
86
87 byte shadebubble(float dx, float dy, vec3_t light)
88 {
89         float   dz, f, dot;
90         vec3_t  normal;
91         dz = 1 - (dx*dx+dy*dy);
92         if (dz > 0) // it does hit the sphere
93         {
94                 f = 0;
95                 // back side
96                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
97                 VectorNormalize(normal);
98                 dot = DotProduct(normal, light);
99                 if (dot > 0.5) // interior reflection
100                         f += ((dot *  2) - 1);
101                 else if (dot < -0.5) // exterior reflection
102                         f += ((dot * -2) - 1);
103                 // front side
104                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
105                 VectorNormalize(normal);
106                 dot = DotProduct(normal, light);
107                 if (dot > 0.5) // interior reflection
108                         f += ((dot *  2) - 1);
109                 else if (dot < -0.5) // exterior reflection
110                         f += ((dot * -2) - 1);
111                 f *= 128;
112                 f += 16; // just to give it a haze so you can see the outline
113                 f = bound(0, f, 255);
114                 return (byte) f;
115         }
116         else
117                 return 0;
118 }
119
120 void R_InitParticleTexture (void)
121 {
122         int             x,y,d,i,m;
123         float   dx, dy;
124         byte    data[32][32][4], noise1[64][64], noise2[64][64];
125         vec3_t  light;
126
127         for (y = 0;y < 32;y++)
128         {
129                 dy = y - 16;
130                 for (x = 0;x < 32;x++)
131                 {
132                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
133                         dx = x - 16;
134                         d = (256 - (dx*dx+dy*dy));
135                         d = bound(0, d, 255);
136                         data[y][x][3] = (byte) d;
137                 }
138         }
139         particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
140
141         for (i = 0;i < 8;i++)
142         {
143                 do
144                 {
145                         fractalnoise(&noise1[0][0], 64, 4);
146                         fractalnoise(&noise2[0][0], 64, 8);
147                         m = 0;
148                         for (y = 0;y < 32;y++)
149                         {
150                                 dy = y - 16;
151                                 for (x = 0;x < 32;x++)
152                                 {
153                                         d = (noise1[y][x] - 128) * 2 + 128;
154                                         d = bound(0, d, 255);
155                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
156                                         dx = x - 16;
157                                         d = (noise2[y][x] - 128) * 4 + 128;
158                                         if (d > 0)
159                                                 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
160                                         d = bound(0, d, 255);
161                                         data[y][x][3] = (byte) d;
162                                         if (m < d)
163                                                 m = d;
164                                 }
165                         }
166                 }
167                 while (m < 224);
168
169                 smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
170         }
171
172         light[0] = 1;light[1] = 1;light[2] = 1;
173         VectorNormalize(light);
174         for (y = 0;y < 32;y++)
175         {
176                 for (x = 0;x < 32;x++)
177                 {
178                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
179                         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);
180                 }
181         }
182         rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
183
184         light[0] = 1;light[1] = 1;light[2] = 1;
185         VectorNormalize(light);
186         for (y = 0;y < 32;y++)
187         {
188                 for (x = 0;x < 32;x++)
189                 {
190                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
191                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
192                 }
193         }
194         bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
195
196         for (i = 0;i < 8;i++)
197         {
198                 float p[32][32];
199                 fractalnoise(&noise1[0][0], 64, 8);
200                 for (y = 0;y < 32;y++)
201                         for (x = 0;x < 32;x++)
202                                 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
203                 for (m = 0;m < 32;m++)
204                 {
205                         int j;
206                         float fx, fy, f;
207                         fx = lhrandom(14, 18);
208                         fy = lhrandom(14, 18);
209                         do
210                         {
211                                 dx = lhrandom(-1, 1);
212                                 dy = lhrandom(-1, 1);
213                                 f = (dx * dx + dy * dy);
214                         }
215                         while(f < 0.125f || f > 1.0f);
216                         f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
217                         dx *= 1.0f / 32.0f;
218                         dy *= 1.0f / 32.0f;
219                         for (j = 0;f > 0 && j < (32 * 14);j++)
220                         {
221                                 y = fy;
222                                 x = fx;
223                                 fx += dx;
224                                 fy += dy;
225                                 p[y - 1][x - 1] += f * 0.125f;
226                                 p[y - 1][x    ] += f * 0.25f;
227                                 p[y - 1][x + 1] += f * 0.125f;
228                                 p[y    ][x - 1] += f * 0.25f;
229                                 p[y    ][x    ] += f;
230                                 p[y    ][x + 1] += f * 0.25f;
231                                 p[y + 1][x - 1] += f * 0.125f;
232                                 p[y + 1][x    ] += f * 0.25f;
233                                 p[y + 1][x + 1] += f * 0.125f;
234 //                              f -= (0.5f / (32 * 16));
235                         }
236                 }
237                 for (y = 0;y < 32;y++)
238                 {
239                         for (x = 0;x < 32;x++)
240                         {
241                                 m = p[y][x];
242                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
243                                 data[y][x][3] = (byte) bound(0, m, 255);
244                         }
245                 }
246
247                 bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
248         }
249
250         for (y = 0;y < 32;y++)
251         {
252                 dy = y - 16;
253                 for (x = 0;x < 32;x++)
254                 {
255                         dx = x - 16;
256                         d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
257                         data[y][x][0] = bound(0, d * 1.0f, 255);
258                         data[y][x][1] = bound(0, d * 0.8f, 255);
259                         data[y][x][2] = bound(0, d * 0.5f, 255);
260                         data[y][x][3] = bound(0, d * 1.0f, 255);
261                 }
262         }
263         rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
264 }
265
266 void r_part_start()
267 {
268         particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
269         freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
270         numparticles = 0;
271         R_InitParticleTexture ();
272 }
273
274 void r_part_shutdown()
275 {
276         numparticles = 0;
277         qfree(particles);
278         qfree(freeparticles);
279 }
280
281 void r_part_newmap()
282 {
283         numparticles = 0;
284 }
285
286 /*
287 ===============
288 R_InitParticles
289 ===============
290 */
291 void R_ReadPointFile_f (void);
292 void R_Particles_Init (void)
293 {
294         int             i;
295
296         i = COM_CheckParm ("-particles");
297
298         if (i)
299         {
300                 r_numparticles = (int)(atoi(com_argv[i+1]));
301                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
302                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
303         }
304         else
305         {
306                 r_numparticles = MAX_PARTICLES;
307         }
308
309         Cmd_AddCommand ("pointfile", R_ReadPointFile_f);        
310
311         Cvar_RegisterVariable (&r_particles);
312         Cvar_RegisterVariable (&r_drawparticles);
313         Cvar_RegisterVariable (&r_particles_lighting);
314         Cvar_RegisterVariable (&r_particles_bloodshowers);
315         Cvar_RegisterVariable (&r_particles_blood);
316         Cvar_RegisterVariable (&r_particles_smoke);
317         Cvar_RegisterVariable (&r_particles_sparks);
318         Cvar_RegisterVariable (&r_particles_bubbles);
319
320         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
321 }
322
323 //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)
324 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
325 {\
326         particle_t      *part;\
327         if (numparticles >= r_numparticles)\
328                 return;\
329         part = &particles[numparticles++];\
330         part->type = (ptype);\
331         part->color = (pcolor);\
332         part->tex = (ptex);\
333         part->dynlight = (plight);\
334         part->rendermode = (prendermode);\
335         part->scale = (pscale);\
336         part->alpha = (palpha);\
337         part->die = cl.time + (ptime);\
338         part->bounce = (pbounce);\
339         part->org[0] = (px);\
340         part->org[1] = (py);\
341         part->org[2] = (pz);\
342         part->vel[0] = (pvx);\
343         part->vel[1] = (pvy);\
344         part->vel[2] = (pvz);\
345         part->time2 = 0;\
346         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
347 }
348 /*
349 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
350 {\
351         particle_t      *part;\
352         if (numparticles >= r_numparticles)\
353                 return;\
354         part = &particles[numparticles++];\
355         part->type = (ptype);\
356         part->color = (pcolor);\
357         part->tex = (ptex);\
358         part->dynlight = (plight);\
359         part->rendermode = (prendermode);\
360         part->scale = (pscale);\
361         part->alpha = (palpha);\
362         part->die = cl.time + (ptime);\
363         part->bounce = (pbounce);\
364         part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
365         part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
366         part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
367         part->vel[0] = lhrandom(-(pvscale), (pvscale));\
368         part->vel[1] = lhrandom(-(pvscale), (pvscale));\
369         part->vel[2] = lhrandom(-(pvscale), (pvscale));\
370         part->time2 = 0;\
371         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
372 }
373 */
374 /*
375 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
376 {\
377         particle_t      *part;\
378         if (numparticles >= r_numparticles)\
379                 return;\
380         part = &particles[numparticles++];\
381         part->type = (ptype);\
382         part->color = (pcolor);\
383         part->tex = (ptex);\
384         part->dynlight = (plight);\
385         part->rendermode = (prendermode);\
386         part->scale = (pscale);\
387         part->alpha = (palpha);\
388         part->die = cl.time + (ptime);\
389         part->bounce = (pbounce);\
390         part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
391         part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
392         part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
393         part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
394         part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
395         part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
396         part->time2 = 0;\
397         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
398 }
399 */
400 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
401 {\
402         particle_t      *part;\
403         if (numparticles >= r_numparticles)\
404                 return;\
405         part = &particles[numparticles++];\
406         part->type = (ptype);\
407         part->color = (pcolor);\
408         part->tex = (ptex);\
409         part->dynlight = (plight);\
410         part->rendermode = (prendermode);\
411         part->scale = (pscale);\
412         part->alpha = (palpha);\
413         part->die = cl.time + (ptime);\
414         part->bounce = (pbounce);\
415         part->org[0] = (px);\
416         part->org[1] = (py);\
417         part->org[2] = (pz);\
418         part->vel[0] = (pvx);\
419         part->vel[1] = (pvy);\
420         part->vel[2] = (pvz);\
421         part->time2 = (ptime2);\
422         part->vel2[0] = (pvx2);\
423         part->vel2[1] = (pvy2);\
424         part->vel2[2] = (pvz2);\
425 }
426
427 /*
428 ===============
429 R_EntityParticles
430 ===============
431 */
432 void R_EntityParticles (entity_t *ent)
433 {
434         int                     i;
435         float           angle;
436         float           sp, sy, cp, cy;
437         vec3_t          forward;
438         float           dist;
439         float           beamlength;
440         static vec3_t avelocities[NUMVERTEXNORMALS];
441         if (!r_particles.value) return; // LordHavoc: particles are optional
442         
443         dist = 64;
444         beamlength = 16;
445
446         if (!avelocities[0][0])
447                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
448                         avelocities[0][i] = (rand()&255) * 0.01;
449
450         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
451         {
452                 angle = cl.time * avelocities[i][0];
453                 sy = sin(angle);
454                 cy = cos(angle);
455                 angle = cl.time * avelocities[i][1];
456                 sp = sin(angle);
457                 cp = cos(angle);
458         
459                 forward[0] = cp*cy;
460                 forward[1] = cp*sy;
461                 forward[2] = -sp;
462
463                 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);
464         }
465 }
466
467
468 void R_ReadPointFile_f (void)
469 {
470         FILE    *f;
471         vec3_t  org;
472         int             r;
473         int             c;
474         char    name[MAX_OSPATH];
475         
476         sprintf (name,"maps/%s.pts", sv.name);
477
478         COM_FOpenFile (name, &f, false);
479         if (!f)
480         {
481                 Con_Printf ("couldn't open %s\n", name);
482                 return;
483         }
484         
485         Con_Printf ("Reading %s...\n", name);
486         c = 0;
487         for (;;)
488         {
489                 r = fscanf (f,"%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         fclose (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) // 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 extern  cvar_t  sv_gravity;
1018
1019 void R_MoveParticles (void)
1020 {
1021         particle_t              *p;
1022         int                             i, activeparticles, maxparticle, j, a;
1023         vec3_t                  v;
1024         float                   gravity, dvel, frametime;
1025
1026         // LordHavoc: early out condition
1027         if (!numparticles)
1028                 return;
1029
1030         frametime = cl.time - cl.oldtime;
1031         if (!frametime)
1032                 return; // if absolutely still, don't update particles
1033         gravity = frametime * sv_gravity.value;
1034         dvel = 1+4*frametime;
1035
1036         activeparticles = 0;
1037         maxparticle = -1;
1038         j = 0;
1039         for (i = 0, p = particles;i < numparticles;i++, p++)
1040         {
1041                 if (p->die < cl.time)
1042                 {
1043                         freeparticles[j++] = p;
1044                         continue;
1045                 }
1046
1047                 VectorCopy(p->org, p->oldorg);
1048                 p->org[0] += p->vel[0]*frametime;
1049                 p->org[1] += p->vel[1]*frametime;
1050                 p->org[2] += p->vel[2]*frametime;
1051                 if (p->bounce)
1052                 {
1053                         vec3_t normal;
1054                         float dist;
1055                         if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1056                         {
1057                                 VectorCopy(v, p->org);
1058                                 if (p->bounce < 0)
1059                                 {
1060                                         byte *color24 = (byte *) &d_8to24table[(int)p->color];
1061                                         R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1062                                         p->die = -1;
1063                                         freeparticles[j++] = p;
1064                                         continue;
1065                                         /*
1066                                         VectorClear(p->vel);
1067                                         p->type = pt_decal;
1068                                         // have to negate the direction (why?)
1069                                         VectorNegate(normal, p->direction);
1070                                         VectorVectors(p->direction, p->decalright, p->decalup);
1071                                         VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1072                                         p->bounce = 0;
1073                                         p->time2 = cl.time + 30;
1074                                         */
1075                                 }
1076                                 else
1077                                 {
1078                                         dist = DotProduct(p->vel, normal) * -p->bounce;
1079                                         VectorMAQuick(p->vel, dist, normal, p->vel);
1080                                         if (DotProduct(p->vel, p->vel) < 0.03)
1081                                                 VectorClear(p->vel);
1082                                 }
1083                         }
1084                 }
1085                 
1086                 switch (p->type)
1087                 {
1088                 case pt_static:
1089                         break;
1090
1091                         // LordHavoc: drop-through because of shared code
1092                 case pt_blob:
1093                         p->vel[2] *= dvel;
1094                 case pt_blob2:
1095                         p->vel[0] *= dvel;
1096                         p->vel[1] *= dvel;
1097                         p->alpha -= frametime * 256;
1098                         if (p->alpha < 1)
1099                                 p->die = -1;
1100                         break;
1101
1102                 case pt_grav:
1103                         p->vel[2] -= gravity;
1104                         break;
1105                 case pt_slowgrav:
1106                         p->vel[2] -= gravity * 0.05;
1107                         break;
1108                 case pt_lavasplash:
1109                         p->vel[2] -= gravity * 0.05;
1110                         p->alpha -= frametime * 192;
1111                         if (p->alpha < 1)
1112                                 p->die = -1;
1113                         break;
1114                 case pt_snow:
1115                         if (cl.time > p->time2)
1116                         {
1117                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1118                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1119                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1120                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1121                         }
1122                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1123                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1124                         {
1125                                 vec3_t normal;
1126                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1127                                         break; // still in solid
1128                                 p->die = cl.time + 1000;
1129                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1130                                 switch (a)
1131                                 {
1132                                 case CONTENTS_LAVA:
1133                                 case CONTENTS_SLIME:
1134                                         p->tex = smokeparticletexture[rand()&7];
1135                                         p->type = pt_steam;
1136                                         p->alpha = 96;
1137                                         p->scale = 5;
1138                                         p->vel[2] = 96;
1139                                         break;
1140                                 case CONTENTS_WATER:
1141                                         p->tex = smokeparticletexture[rand()&7];
1142                                         p->type = pt_splash;
1143                                         p->alpha = 96;
1144                                         p->scale = 5;
1145                                         p->vel[2] = 96;
1146                                         break;
1147                                 default: // CONTENTS_SOLID and any others
1148                                         TraceLine(p->oldorg, p->org, v, normal);
1149                                         VectorCopy(v, p->org);
1150                                         p->tex = smokeparticletexture[rand()&7];
1151                                         p->type = pt_fade;
1152                                         VectorClear(p->vel);
1153                                         break;
1154                                 }
1155                         }
1156                         break;
1157                 case pt_blood:
1158                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1159                         {
1160                                 p->die = -1;
1161                                 break;
1162                         }
1163                         p->vel[2] -= gravity * 0.5;
1164                         break;
1165                 case pt_spark:
1166                         p->alpha -= frametime * 512;
1167                         p->vel[2] -= gravity;
1168                         if (p->alpha < 1)
1169                                 p->die = -1;
1170                         break;
1171                 case pt_fade:
1172                         p->alpha -= frametime * 512;
1173                         if (p->alpha < 1)
1174                                 p->die = -1;
1175                         break;
1176                 case pt_bubble:
1177                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1178                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1179                         {
1180                                 p->tex = smokeparticletexture[rand()&7];
1181                                 p->type = pt_splashpuff;
1182                                 p->scale = 4;
1183                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1184                                 break;
1185                         }
1186                         p->vel[2] += gravity * 0.25;
1187                         p->vel[0] *= (1 - (frametime * 0.0625));
1188                         p->vel[1] *= (1 - (frametime * 0.0625));
1189                         p->vel[2] *= (1 - (frametime * 0.0625));
1190                         if (cl.time > p->time2)
1191                         {
1192                                 p->time2 = cl.time + lhrandom(0, 0.5);
1193                                 p->vel[0] += lhrandom(-32,32);
1194                                 p->vel[1] += lhrandom(-32,32);
1195                                 p->vel[2] += lhrandom(-32,32);
1196                         }
1197                         p->alpha -= frametime * 256;
1198                         if (p->alpha < 1)
1199                                 p->die = -1;
1200                         break;
1201                 case pt_bulletsmoke:
1202                         p->scale += frametime * 16;
1203                         p->alpha -= frametime * 1024;
1204                         p->vel[2] += gravity * 0.05;
1205                         if (p->alpha < 1)
1206                                 p->die = -1;
1207                         break;
1208                 case pt_smoke:
1209                         p->scale += frametime * 32;
1210                         p->alpha -= frametime * 512;
1211                         p->vel[2] += gravity * 0.05;
1212                         if (p->alpha < 1)
1213                                 p->die = -1;
1214                         break;
1215                 case pt_steam:
1216                         p->scale += frametime * 48;
1217                         p->alpha -= frametime * 512;
1218                         p->vel[2] += gravity * 0.05;
1219                         if (p->alpha < 1)
1220                                 p->die = -1;
1221                         break;
1222                 case pt_splashpuff:
1223 //                      p->scale += frametime * 24;
1224                         p->alpha -= frametime * 1024;
1225                         if (p->alpha < 1)
1226                                 p->die = -1;
1227                         break;
1228                 case pt_rain:
1229                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1230                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1231                         {
1232                                 vec3_t normal;
1233                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1234                                         break; // still in solid
1235                                 p->die = cl.time + 1000;
1236                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1237                                 switch (a)
1238                                 {
1239                                 case CONTENTS_LAVA:
1240                                 case CONTENTS_SLIME:
1241                                         p->tex = smokeparticletexture[rand()&7];
1242                                         p->type = pt_steam;
1243                                         p->scale = 3;
1244                                         p->vel[2] = 96;
1245                                         break;
1246                                 case CONTENTS_WATER:
1247                                         p->tex = smokeparticletexture[rand()&7];
1248                                         p->type = pt_splashpuff;
1249                                         p->scale = 4;
1250                                         break;
1251                                 default: // CONTENTS_SOLID and any others
1252                                         TraceLine(p->oldorg, p->org, v, normal);
1253                                         VectorCopy(v, p->org);
1254                                         p->tex = smokeparticletexture[rand()&7];
1255                                         p->type = pt_splashpuff;
1256                                         p->scale = 4;
1257                                         break;
1258                                 }
1259                         }
1260                         break;
1261                 case pt_flame:
1262                         p->alpha -= frametime * 512;
1263                         p->vel[2] += gravity;
1264 //                      p->scale -= frametime * 16;
1265                         if (p->alpha < 16)
1266                                 p->die = -1;
1267                         break;
1268                         /*
1269                 case pt_flamingdebris:
1270                         if (cl.time >= p->time2)
1271                         {
1272                                 p->time2 = cl.time + 0.01;
1273                                 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));
1274                         }
1275                         p->alpha -= frametime * 512;
1276                         p->vel[2] -= gravity * 0.5f;
1277                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1278                                 p->die = -1;
1279                         if (p->alpha < 1)
1280                                 p->die = -1;
1281                         break;
1282                 case pt_smokingdebris:
1283                         if (cl.time >= p->time2)
1284                         {
1285                                 p->time2 = cl.time + 0.01;
1286                                 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));
1287                         }
1288                         p->alpha -= frametime * 512;
1289                         p->vel[2] -= gravity * 0.5f;
1290                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1291                                 p->die = -1;
1292                         if (p->alpha < 1)
1293                                 p->die = -1;
1294                         break;
1295                 case pt_flamefall:
1296                         p->alpha -= frametime * 512;
1297                         p->vel[2] -= gravity * 0.5f;
1298                         if (p->alpha < 1)
1299                                 p->die = -1;
1300                         break;
1301                         */
1302                         /*
1303                 case pt_decal:
1304                         if (cl.time > p->time2)
1305                         {
1306                                 p->alpha -= frametime * 256;
1307                                 if (p->alpha < 1)
1308                                         p->die = -1;
1309                         }
1310                         if (p->alpha < 64)
1311                                 p->die = -1;
1312                         break;
1313                         */
1314                 case pt_oneframe:
1315                         if (p->time2)
1316                                 p->die = -1;
1317                         p->time2 = 1;
1318                         break;
1319                 default:
1320                         printf("unknown particle type %i\n", p->type);
1321                         p->die = -1;
1322                         break;
1323                 }
1324
1325                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1326                 if (p->die < cl.time)
1327                         freeparticles[j++] = p;
1328                 else
1329                 {
1330                         maxparticle = i;
1331                         activeparticles++;
1332                 }
1333         }
1334         // fill in gaps to compact the array
1335         i = 0;
1336         while (maxparticle >= activeparticles)
1337         {
1338                 *freeparticles[i++] = particles[maxparticle--];
1339                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1340                         maxparticle--;
1341         }
1342         numparticles = activeparticles;
1343 }
1344
1345 void R_DrawParticles (void)
1346 {
1347         particle_t              *p;
1348         int                             i, dynamiclight, staticlight, r, g, b;
1349         byte                    br, bg, bb, ba;
1350         float                   scale, scale2, minparticledist;
1351         byte                    *color24;
1352         vec3_t                  uprightangles, up2, right2, tempcolor, corner;
1353         mleaf_t                 *leaf;
1354
1355         // LordHavoc: early out condition
1356         if ((!numparticles) || (!r_drawparticles.value))
1357                 return;
1358
1359         staticlight = dynamiclight = r_particles_lighting.value;
1360         if (!r_dynamic.value)
1361                 dynamiclight = 0;
1362         c_particles += numparticles;
1363
1364         uprightangles[0] = 0;
1365         uprightangles[1] = r_refdef.viewangles[1];
1366         uprightangles[2] = 0;
1367         AngleVectors (uprightangles, NULL, right2, up2);
1368
1369         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1370
1371         for (i = 0, p = particles;i < numparticles;i++, p++)
1372         {
1373                 // LordHavoc: unnecessary (array was already compacted)
1374 //              if (p->die < cl.time)
1375 //                      continue;
1376
1377                 // LordHavoc: only render if not too close
1378                 if (DotProduct(p->org, vpn) < minparticledist)
1379                         continue;
1380
1381                 // LordHavoc: check if it's in a visible leaf
1382                 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1383                 if (leaf->visframe != r_framecount)
1384                         continue;
1385
1386                 /*
1387                 if (p->type == pt_decal)
1388                 {
1389                         VectorSubtract(p->org, r_refdef.vieworg, v);
1390                         if (DotProduct(p->direction, v) < 0)
1391                                 continue;
1392                 }
1393                 */
1394
1395                 color24 = (byte *) &d_8to24table[(int)p->color];
1396                 r = color24[0];
1397                 g = color24[1];
1398                 b = color24[2];
1399                 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1400                 {
1401                         R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1402                         r = (r * (int) tempcolor[0]) >> 7;
1403                         g = (g * (int) tempcolor[1]) >> 7;
1404                         b = (b * (int) tempcolor[2]) >> 7;
1405                 }
1406                 br = (byte) min(r, 255);
1407                 bg = (byte) min(g, 255);
1408                 bb = (byte) min(b, 255);
1409                 ba = (byte) p->alpha;
1410                 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1411                 scale = p->scale * -0.5;scale2 = p->scale;
1412                 /*
1413                 if (p->type == pt_decal)
1414                 {
1415                         corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1416                         corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1417                         corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1418                         transpolyvertub(corner[0]                                                 , corner[1]                                                 , corner[2]                                                 , 0,1,br,bg,bb,ba);
1419                         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);
1420                         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);
1421                         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);
1422                 }
1423                 else*/ if (p->tex == rainparticletexture) // rain streak
1424                 {
1425                         corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1426                         corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1427                         corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1428                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1429                         transpolyvertub(corner[0] + up2[0]*scale2                   , corner[1] + up2[1]*scale2                   , corner[2] + up2[2]*scale2                   , 0,0,br,bg,bb,ba);
1430                         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);
1431                         transpolyvertub(corner[0]                 + right2[0]*scale2, corner[1]                 + right2[1]*scale2, corner[2]                 + right2[2]*scale2, 1,1,br,bg,bb,ba);
1432                 }
1433                 else
1434                 {
1435                         corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1436                         corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1437                         corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1438                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1439                         transpolyvertub(corner[0] + vup[0]*scale2                   , corner[1] + vup[1]*scale2                   , corner[2] + vup[2]*scale2                   , 0,0,br,bg,bb,ba);
1440                         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);
1441                         transpolyvertub(corner[0]                 + vright[0]*scale2, corner[1]                 + vright[1]*scale2, corner[2]                 + vright[2]*scale2, 1,1,br,bg,bb,ba);
1442                 }
1443                 transpolyend();
1444         }
1445 }