]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_particles.c
implemented cl_particles_quake cvar to use quake-style particle effects
[divverent/darkplaces.git] / cl_particles.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 #ifdef WORKINGLQUAKE
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS        162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
29 typedef unsigned char unsigned char;
30 #define cl_stainmaps.integer 0
31 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
32 {
33 }
34 #define CL_EntityParticles R_EntityParticles
35 #define CL_ReadPointFile_f R_ReadPointFile_f
36 #define CL_ParseParticleEffect R_ParseParticleEffect
37 #define CL_ParticleExplosion R_ParticleExplosion
38 #define CL_ParticleExplosion2 R_ParticleExplosion2
39 #define CL_TeleportSplash R_TeleportSplash
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
44 {
45         vec3_t right1, right2, diff, normal;
46
47         VectorSubtract (org2, org1, normal);
48         VectorNormalize (normal);
49
50         // calculate 'right' vector for start
51         VectorSubtract (r_vieworigin, org1, diff);
52         VectorNormalize (diff);
53         CrossProduct (normal, diff, right1);
54
55         // calculate 'right' vector for end
56         VectorSubtract (r_vieworigin, org2, diff);
57         VectorNormalize (diff);
58         CrossProduct (normal, diff, right2);
59
60         vert[ 0] = org1[0] + width * right1[0];
61         vert[ 1] = org1[1] + width * right1[1];
62         vert[ 2] = org1[2] + width * right1[2];
63         vert[ 3] = org1[0] - width * right1[0];
64         vert[ 4] = org1[1] - width * right1[1];
65         vert[ 5] = org1[2] - width * right1[2];
66         vert[ 6] = org2[0] - width * right2[0];
67         vert[ 7] = org2[1] - width * right2[1];
68         vert[ 8] = org2[2] - width * right2[2];
69         vert[ 9] = org2[0] + width * right2[0];
70         vert[10] = org2[1] + width * right2[1];
71         vert[11] = org2[2] + width * right2[2];
72 }
73 void fractalnoise(unsigned char *noise, int size, int startgrid)
74 {
75         int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
76         int *noisebuf;
77 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
78
79         for (sizepower = 0;(1 << sizepower) < size;sizepower++);
80         if (size != (1 << sizepower))
81         {
82                 Con_Printf("fractalnoise: size must be power of 2\n");
83                 return;
84         }
85
86         for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
87         if (startgrid != (1 << gridpower))
88         {
89                 Con_Printf("fractalnoise: grid must be power of 2\n");
90                 return;
91         }
92
93         startgrid = bound(0, startgrid, size);
94
95         amplitude = 0xFFFF; // this gets halved before use
96         noisebuf = malloc(size*size*sizeof(int));
97         memset(noisebuf, 0, size*size*sizeof(int));
98
99         for (g2 = startgrid;g2;g2 >>= 1)
100         {
101                 // brownian motion (at every smaller level there is random behavior)
102                 amplitude >>= 1;
103                 for (y = 0;y < size;y += g2)
104                         for (x = 0;x < size;x += g2)
105                                 n(x,y) += (rand()&amplitude);
106
107                 g = g2 >> 1;
108                 if (g)
109                 {
110                         // subdivide, diamond-square algorithm (really this has little to do with squares)
111                         // diamond
112                         for (y = 0;y < size;y += g2)
113                                 for (x = 0;x < size;x += g2)
114                                         n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
115                         // square
116                         for (y = 0;y < size;y += g2)
117                                 for (x = 0;x < size;x += g2)
118                                 {
119                                         n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
120                                         n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
121                                 }
122                 }
123         }
124         // find range of noise values
125         min = max = 0;
126         for (y = 0;y < size;y++)
127                 for (x = 0;x < size;x++)
128                 {
129                         if (n(x,y) < min) min = n(x,y);
130                         if (n(x,y) > max) max = n(x,y);
131                 }
132         max -= min;
133         max++;
134         // normalize noise and copy to output
135         for (y = 0;y < size;y++)
136                 for (x = 0;x < size;x++)
137                         *noise++ = (unsigned char) (((n(x,y) - min) * 256) / max);
138         free(noisebuf);
139 #undef n
140 }
141 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
142 {
143         float d;
144
145         right[0] = forward[2];
146         right[1] = -forward[0];
147         right[2] = forward[1];
148
149         d = DotProduct(forward, right);
150         right[0] -= d * forward[0];
151         right[1] -= d * forward[1];
152         right[2] -= d * forward[2];
153         VectorNormalize(right);
154         CrossProduct(right, forward, up);
155 }
156 #if QW
157 #include "pmove.h"
158 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
159 #endif
160 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
161 {
162 #if QW
163         pmtrace_t trace;
164 #else
165         trace_t trace;
166 #endif
167         memset (&trace, 0, sizeof(trace));
168         trace.fraction = 1;
169         VectorCopy (end, trace.endpos);
170 #if QW
171         PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
172 #else
173         RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
174 #endif
175         return trace;
176 }
177 #else
178 #include "cl_collision.h"
179 #include "image.h"
180 #endif
181
182 #define MAX_PARTICLES                   32768   // default max # of particles at one time
183 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
184
185 typedef enum
186 {
187         PARTICLE_BILLBOARD = 0,
188         PARTICLE_SPARK = 1,
189         PARTICLE_ORIENTED_DOUBLESIDED = 2,
190         PARTICLE_BEAM = 3
191 }
192 porientation_t;
193
194 typedef enum
195 {
196         PBLEND_ALPHA = 0,
197         PBLEND_ADD = 1,
198         PBLEND_MOD = 2
199 }
200 pblend_t;
201
202 typedef struct particletype_s
203 {
204         pblend_t blendmode;
205         porientation_t orientation;
206         qboolean lighting;
207 }
208 particletype_t;
209
210 typedef enum
211 {
212         pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
213 }
214 ptype_t;
215
216 // must match ptype_t values
217 particletype_t particletype[pt_total] =
218 {
219         {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
220         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
221         {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
222         {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
223         {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
224         {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
225         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
226         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
227         {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
228         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
229         {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
230         {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
231 };
232
233 typedef struct particle_s
234 {
235         particletype_t *type;
236         int                     texnum;
237         vec3_t          org;
238         vec3_t          vel; // velocity of particle, or orientation of decal, or end point of beam
239         float           size;
240         float           alpha; // 0-255
241         float           alphafade; // how much alpha reduces per second
242         float           time2; // used for snow fluttering and decal fade
243         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)
244         float           gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
245         float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
246         unsigned char           color[4];
247         unsigned short owner; // decal stuck to this entity
248         model_t         *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
249         vec3_t          relativeorigin; // decal at this location in entity's coordinate space
250         vec3_t          relativedirection; // decal oriented this way relative to entity's coordinate space
251 }
252 particle_t;
253
254 static int particlepalette[256] =
255 {
256         0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
257         0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
258         0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
259         0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
260         0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
261         0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
262         0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
263         0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
264         0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
265         0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
266         0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
267         0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
268         0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
269         0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
270         0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
271         0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
272         0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
273         0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
274         0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
275         0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
276         0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
277         0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
278         0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
279         0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
280         0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
281         0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
282         0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
283         0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
284         0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
285         0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
286         0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
287         0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53  // 248-255
288 };
289
290 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
291 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
292 int             ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
293
294 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
295
296 // texture numbers in particle font
297 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
298 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
299 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
300 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
301 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
302 static const int tex_particle = 63;
303 static const int tex_bubble = 62;
304 static const int tex_raindrop = 61;
305 static const int tex_beam = 60;
306
307 static int                      cl_maxparticles;
308 static int                      cl_numparticles;
309 static int                      cl_freeparticle;
310 static particle_t       *particles;
311
312 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
313 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
314 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
315 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0"};
316 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
317 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
318 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
319 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
320 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
321 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
322 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
323 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
324 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
325 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
326 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
327 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
328 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
329 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
330 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
331 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
332 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
333
334 void CL_Particles_Clear(void)
335 {
336         cl_numparticles = 0;
337         cl_freeparticle = 0;
338         memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
339 }
340
341 /*
342 ===============
343 CL_InitParticles
344 ===============
345 */
346 void CL_ReadPointFile_f (void);
347 void CL_Particles_Init (void)
348 {
349         int             i;
350
351 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
352         i = COM_CheckParm ("-particles");
353
354         if (i && i < com_argc - 1)
355         {
356                 cl_maxparticles = (int)(atoi(com_argv[i+1]));
357                 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
358                         cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
359         }
360         else
361                 cl_maxparticles = MAX_PARTICLES;
362
363         Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
364
365         Cvar_RegisterVariable (&cl_particles);
366         Cvar_RegisterVariable (&cl_particles_quality);
367         Cvar_RegisterVariable (&cl_particles_size);
368         Cvar_RegisterVariable (&cl_particles_quake);
369         Cvar_RegisterVariable (&cl_particles_bloodshowers);
370         Cvar_RegisterVariable (&cl_particles_blood);
371         Cvar_RegisterVariable (&cl_particles_blood_alpha);
372         Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
373         Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
374         Cvar_RegisterVariable (&cl_particles_explosions_smoke);
375         Cvar_RegisterVariable (&cl_particles_explosions_sparks);
376         Cvar_RegisterVariable (&cl_particles_explosions_shell);
377         Cvar_RegisterVariable (&cl_particles_bulletimpacts);
378         Cvar_RegisterVariable (&cl_particles_smoke);
379         Cvar_RegisterVariable (&cl_particles_smoke_alpha);
380         Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
381         Cvar_RegisterVariable (&cl_particles_sparks);
382         Cvar_RegisterVariable (&cl_particles_bubbles);
383         Cvar_RegisterVariable (&cl_decals);
384         Cvar_RegisterVariable (&cl_decals_time);
385         Cvar_RegisterVariable (&cl_decals_fadetime);
386
387 #ifdef WORKINGLQUAKE
388         particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
389 #else
390         particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
391 #endif
392         CL_Particles_Clear();
393 }
394
395 void CL_Particles_Shutdown (void)
396 {
397 #ifdef WORKINGLQUAKE
398         // No clue what to do here...
399 #endif
400 }
401
402 // list of all 26 parameters:
403 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
404 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
405 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
406 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
407 // palpha - opacity of particle as 0-255 (can be more than 255)
408 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
409 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
410 // pgravity - how much effect gravity has on the particle (0-1)
411 // pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
412 // px,py,pz - starting origin of particle
413 // pvx,pvy,pvz - starting velocity of particle
414 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
415 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction, float originjitter, float velocityjitter)
416 {
417         int l1, l2;
418         particle_t *part;
419         vec3_t v;
420         for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
421         if (cl_freeparticle >= cl_maxparticles)
422                 return NULL;
423         part = &particles[cl_freeparticle++];
424         if (cl_numparticles < cl_freeparticle)
425                 cl_numparticles = cl_freeparticle;
426         memset(part, 0, sizeof(*part));
427         part->type = ptype;
428         l2 = (int)lhrandom(0.5, 256.5);
429         l1 = 256 - l2;
430         part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
431         part->color[1] = ((((pcolor1 >>  8) & 0xFF) * l1 + ((pcolor2 >>  8) & 0xFF) * l2) >> 8) & 0xFF;
432         part->color[2] = ((((pcolor1 >>  0) & 0xFF) * l1 + ((pcolor2 >>  0) & 0xFF) * l2) >> 8) & 0xFF;
433         part->color[3] = 0xFF;
434         part->texnum = ptex;
435         part->size = psize;
436         part->alpha = palpha;
437         part->alphafade = palphafade;
438         part->gravity = pgravity;
439         part->bounce = pbounce;
440         VectorRandom(v);
441         part->org[0] = px + originjitter * v[0];
442         part->org[1] = py + originjitter * v[1];
443         part->org[2] = pz + originjitter * v[2];
444         part->vel[0] = pvx + velocityjitter * v[0];
445         part->vel[1] = pvy + velocityjitter * v[1];
446         part->vel[2] = pvz + velocityjitter * v[2];
447         part->time2 = 0;
448         part->friction = pfriction;
449         return part;
450 }
451
452 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
453 {
454         particle_t *p;
455         if (!cl_decals.integer)
456                 return;
457         p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0, 0, 0);
458         if (p)
459         {
460                 p->time2 = cl.time;
461 #ifndef WORKINGLQUAKE
462                 p->owner = hitent;
463                 p->ownermodel = cl_entities[p->owner].render.model;
464                 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
465                 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
466                 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
467 #endif
468         }
469 }
470
471 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
472 {
473         int i;
474         float bestfrac, bestorg[3], bestnormal[3];
475         float org2[3];
476         int besthitent = 0, hitent;
477         trace_t trace;
478         bestfrac = 10;
479         for (i = 0;i < 32;i++)
480         {
481                 VectorRandom(org2);
482                 VectorMA(org, maxdist, org2, org2);
483                 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
484                 if (bestfrac > trace.fraction)
485                 {
486                         bestfrac = trace.fraction;
487                         besthitent = hitent;
488                         VectorCopy(trace.endpos, bestorg);
489                         VectorCopy(trace.plane.normal, bestnormal);
490                 }
491         }
492         if (bestfrac < 1)
493                 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
494 }
495
496 /*
497 ===============
498 CL_EntityParticles
499 ===============
500 */
501 void CL_EntityParticles (entity_t *ent)
502 {
503         int i;
504         float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
505         static vec3_t avelocities[NUMVERTEXNORMALS];
506         if (!cl_particles.integer) return;
507
508 #ifdef WORKINGLQUAKE
509         VectorCopy(ent->origin, org);
510 #else
511         Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
512 #endif
513
514         if (!avelocities[0][0])
515                 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
516                         avelocities[0][i] = lhrandom(0, 2.55);
517
518         for (i = 0;i < NUMVERTEXNORMALS;i++)
519         {
520                 yaw = cl.time * avelocities[i][0];
521                 pitch = cl.time * avelocities[i][1];
522                 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
523                 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
524                 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
525                 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0);
526         }
527 }
528
529
530 void CL_ReadPointFile_f (void)
531 {
532         vec3_t org, leakorg;
533         int r, c, s;
534         char *pointfile = NULL, *pointfilepos, *t, tchar;
535         char name[MAX_OSPATH];
536
537         if (!cl.worldmodel)
538                 return;
539
540         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
541         strlcat (name, ".pts", sizeof (name));
542 #if WORKINGLQUAKE
543         pointfile = COM_LoadTempFile (name);
544 #else
545         pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
546 #endif
547         if (!pointfile)
548         {
549                 Con_Printf("Could not open %s\n", name);
550                 return;
551         }
552
553         Con_Printf("Reading %s...\n", name);
554         VectorClear(leakorg);
555         c = 0;
556         s = 0;
557         pointfilepos = pointfile;
558         while (*pointfilepos)
559         {
560                 while (*pointfilepos == '\n' || *pointfilepos == '\r')
561                         pointfilepos++;
562                 if (!*pointfilepos)
563                         break;
564                 t = pointfilepos;
565                 while (*t && *t != '\n' && *t != '\r')
566                         t++;
567                 tchar = *t;
568                 *t = 0;
569                 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
570                 *t = tchar;
571                 pointfilepos = t;
572                 if (r != 3)
573                         break;
574                 if (c == 0)
575                         VectorCopy(org, leakorg);
576                 c++;
577
578                 if (cl_numparticles < cl_maxparticles - 3)
579                 {
580                         s++;
581                         particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0);
582                 }
583         }
584 #ifndef WORKINGLQUAKE
585         Mem_Free(pointfile);
586 #endif
587         VectorCopy(leakorg, org);
588         Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
589
590         particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0);
591         particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0);
592         particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0);
593 }
594
595 /*
596 ===============
597 CL_ParseParticleEffect
598
599 Parse an effect out of the server message
600 ===============
601 */
602 void CL_ParseParticleEffect (void)
603 {
604         vec3_t org, dir;
605         int i, count, msgcount, color;
606
607         MSG_ReadVector(org, cl.protocol);
608         for (i=0 ; i<3 ; i++)
609                 dir[i] = MSG_ReadChar ();
610         msgcount = MSG_ReadByte ();
611         color = MSG_ReadByte ();
612
613         if (msgcount == 255)
614                 count = 1024;
615         else
616                 count = msgcount;
617
618         if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer)
619         {
620                 if (color == 73)
621                 {
622                         // regular blood
623                         CL_BloodPuff(org, dir, count / 2);
624                         return;
625                 }
626                 if (color == 225)
627                 {
628                         // lightning blood
629                         CL_BloodPuff(org, dir, count / 2);
630                         return;
631                 }
632         }
633         CL_RunParticleEffect (org, dir, color, count);
634 }
635
636 /*
637 ===============
638 CL_ParticleExplosion
639
640 ===============
641 */
642 void CL_ParticleExplosion (vec3_t org)
643 {
644         int i;
645         trace_t trace;
646         //vec3_t v;
647         //vec3_t v2;
648         if (cl_stainmaps.integer)
649                 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
650         CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
651
652         if (cl_particles_quake.integer)
653         {
654                 for (i = 0;i < 1024;i++)
655                 {
656                         int r, color;
657                         r = rand()&3;
658                         if (i & 1)
659                         {
660                                 color = particlepalette[ramp1[r]];
661                                 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, (1.0f / cl_particles_quality.value) * 32 * (8 - r), (1.0f / cl_particles_quality.value) * 318, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
662                         }
663                         else
664                         {
665                                 color = particlepalette[ramp2[r]];
666                                 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, (1.0f / cl_particles_quality.value) * 32 * (8 - r), (1.0f / cl_particles_quality.value) * 478, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 16, 256);
667                         }
668                 }
669         }
670         else
671         {
672                 i = CL_PointSuperContents(org);
673                 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
674                 {
675                         if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
676                                 for (i = 0;i < 128 * cl_particles_quality.value;i++)
677                                         particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, (1.0 / 16.0), 16, 96);
678                 }
679                 else
680                 {
681                         // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
682                         // smoke puff
683                         if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
684                         {
685                                 for (i = 0;i < 32;i++)
686                                 {
687                                         int k;
688                                         vec3_t v, v2;
689 #ifdef WORKINGLQUAKE
690                                         v2[0] = lhrandom(-48, 48);
691                                         v2[1] = lhrandom(-48, 48);
692                                         v2[2] = lhrandom(-48, 48);
693 #else
694                                         for (k = 0;k < 16;k++)
695                                         {
696                                                 v[0] = org[0] + lhrandom(-48, 48);
697                                                 v[1] = org[1] + lhrandom(-48, 48);
698                                                 v[2] = org[2] + lhrandom(-48, 48);
699                                                 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
700                                                 if (trace.fraction >= 0.1)
701                                                         break;
702                                         }
703                                         VectorSubtract(trace.endpos, org, v2);
704 #endif
705                                         VectorScale(v2, 2.0f, v2);
706                                         particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0);
707                                 }
708                         }
709
710                         if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
711                                 for (i = 0;i < 128 * cl_particles_quality.value;i++)
712                                         particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], 0, 0, 80, 0.2, 0, 256);
713                 }
714         }
715
716         if (cl_particles_explosions_shell.integer)
717                 R_NewExplosion(org);
718 }
719
720 /*
721 ===============
722 CL_ParticleExplosion2
723
724 ===============
725 */
726 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
727 {
728         int i, k;
729         if (!cl_particles.integer) return;
730
731         for (i = 0;i < 512 * cl_particles_quality.value;i++)
732         {
733                 k = particlepalette[colorStart + (i % colorLength)];
734                 if (cl_particles_quake.integer)
735                         particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*850, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 8, 256);
736                 else
737                         particle(particletype + pt_static, k, k, tex_particle, lhrandom(0.5, 1.5), (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), 8, 192);
738         }
739 }
740
741 /*
742 ===============
743 CL_BlobExplosion
744
745 ===============
746 */
747 void CL_BlobExplosion (vec3_t org)
748 {
749         int i, k;
750         if (!cl_particles.integer) return;
751
752         if (!cl_particles_quake.integer)
753         {
754                 CL_ParticleExplosion(org);
755                 return;
756         }
757
758         for (i = 0;i < 1024 * cl_particles_quality.value;i++)
759         {
760                 if (i & 1)
761                 {
762                         k = particlepalette[66 + rand()%6];
763                         particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(182, 255), (1.0f/cl_particles_quality.value)*182, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
764                 }
765                 else
766                 {
767                         k = particlepalette[150 + rand()%6];
768                         particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(182, 255), (1.0f/cl_particles_quality.value)*182, 0, 0, org[0], org[1], org[2], 0, 0, lhrandom(-256, 256), 0, 16, 0);
769                 }
770         }
771 }
772
773 /*
774 ===============
775 CL_RunParticleEffect
776
777 ===============
778 */
779 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
780 {
781         int k;
782
783         if (count == 1024)
784         {
785                 CL_ParticleExplosion(org);
786                 return;
787         }
788         if (!cl_particles.integer) return;
789         if (cl_particles_quake.integer)
790         {
791                 count *= cl_particles_quality.value;
792                 while (count--)
793                 {
794                         k = particlepalette[color + (rand()&7)];
795                         particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(51, 255), (1.0f / cl_particles_quality.value) * 512, 0, 0.05, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 0);
796                 }
797         }
798         else
799         {
800                 count *= cl_particles_quality.value;
801                 while (count--)
802                 {
803                         k = particlepalette[color + (rand()&7)];
804                         if (gamemode == GAME_GOODVSBAD2)
805                                 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 8, 10);
806                         else
807                                 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 15);
808                 }
809         }
810 }
811
812 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
813 /*
814 ===============
815 CL_SparkShower
816 ===============
817 */
818 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
819 {
820         int k;
821
822         if (!cl_particles.integer) return;
823
824         if (cl_particles_sparks.integer)
825         {
826                 // sparks
827                 count *= cl_particles_quality.value;
828                 while(count--)
829                 {
830                         k = particlepalette[0x68 + (rand() & 7)];
831                         particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 64);
832                 }
833         }
834 }
835
836 void CL_Smoke (vec3_t org, vec3_t dir, int count)
837 {
838         vec3_t org2;
839         int k;
840         trace_t trace;
841
842         if (!cl_particles.integer) return;
843
844         // smoke puff
845         if (cl_particles_smoke.integer)
846         {
847                 k = count * 0.25 * cl_particles_quality.value;
848                 while(k--)
849                 {
850                         org2[0] = org[0] + 0.125f * lhrandom(-count, count);
851                         org2[1] = org[1] + 0.125f * lhrandom(-count, count);
852                         org2[2] = org[2] + 0.125f * lhrandom(-count, count);
853                         trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
854                         particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], 0, 0, 0, 0, 0, 8);
855                 }
856         }
857 }
858
859 void CL_BulletMark (vec3_t org)
860 {
861         if (cl_stainmaps.integer)
862                 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
863         CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
864 }
865
866 void CL_PlasmaBurn (vec3_t org)
867 {
868         if (cl_stainmaps.integer)
869                 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
870         CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
871 }
872
873 static float bloodcount = 0;
874 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
875 {
876         float s;
877         vec3_t org2;
878         trace_t trace;
879         // bloodcount is used to accumulate counts too small to cause a blood particle
880         if (!cl_particles.integer) return;
881         if (cl_particles_quake.integer)
882         {
883                 CL_RunParticleEffect(org, vel, 73, count * 2);
884                 return;
885         }
886         if (!cl_particles_blood.integer) return;
887
888         s = count + 64.0f;
889         count *= 5.0f;
890         if (count > 1000)
891                 count = 1000;
892         bloodcount += count;
893         while(bloodcount > 0)
894         {
895                 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
896                 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
897                 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
898                 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
899                 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, trace.endpos[0], trace.endpos[1], trace.endpos[2], vel[0], vel[1], vel[2], 1, 0, s);
900                 bloodcount -= 16 / cl_particles_quality.value;
901         }
902 }
903
904 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
905 {
906         vec3_t org, vel, diff, center, velscale;
907         if (!cl_particles.integer) return;
908         if (!cl_particles_bloodshowers.integer) return;
909         if (!cl_particles_blood.integer) return;
910
911         VectorSubtract(maxs, mins, diff);
912         center[0] = (mins[0] + maxs[0]) * 0.5;
913         center[1] = (mins[1] + maxs[1]) * 0.5;
914         center[2] = (mins[2] + maxs[2]) * 0.5;
915         velscale[0] = velspeed * 2.0 / diff[0];
916         velscale[1] = velspeed * 2.0 / diff[1];
917         velscale[2] = velspeed * 2.0 / diff[2];
918
919         bloodcount += count * 5.0f;
920         while (bloodcount > 0)
921         {
922                 org[0] = lhrandom(mins[0], maxs[0]);
923                 org[1] = lhrandom(mins[1], maxs[1]);
924                 org[2] = lhrandom(mins[2], maxs[2]);
925                 vel[0] = (org[0] - center[0]) * velscale[0];
926                 vel[1] = (org[1] - center[1]) * velscale[1];
927                 vel[2] = (org[2] - center[2]) * velscale[2];
928                 bloodcount -= 16 / cl_particles_quality.value;
929                 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1, 0, 0);
930         }
931 }
932
933 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
934 {
935         int k;
936         float t;
937         if (!cl_particles.integer) return;
938         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
939         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
940         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
941
942         count *= cl_particles_quality.value;
943         while (count--)
944         {
945                 k = particlepalette[colorbase + (rand()&3)];
946                 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 2, gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, randomvel);
947         }
948 }
949
950 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
951 {
952         int k;
953         float t, z, minz, maxz;
954         particle_t *p;
955         if (!cl_particles.integer) return;
956         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
957         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
958         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
959         if (dir[2] < 0) // falling
960                 z = maxs[2];
961         else // rising??
962                 z = mins[2];
963
964         minz = z - fabs(dir[2]) * 0.1;
965         maxz = z + fabs(dir[2]) * 0.1;
966         minz = bound(mins[2], minz, maxs[2]);
967         maxz = bound(mins[2], maxz, maxs[2]);
968
969         count *= cl_particles_quality.value;
970
971         switch(type)
972         {
973         case 0:
974                 count *= 4; // ick, this should be in the mod or maps?
975
976                 while(count--)
977                 {
978                         k = particlepalette[colorbase + (rand()&3)];
979                         if (gamemode == GAME_GOODVSBAD2)
980                                 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0);
981                         else
982                                 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0);
983                 }
984                 break;
985         case 1:
986                 while(count--)
987                 {
988                         k = particlepalette[colorbase + (rand()&3)];
989                         if (gamemode == GAME_GOODVSBAD2)
990                                 p = particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0);
991                         else
992                                 p = particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0);
993                         if (p)
994                                 VectorCopy(p->vel, p->relativedirection);
995                 }
996                 break;
997         default:
998                 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
999         }
1000 }
1001
1002 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
1003 {
1004         int k;
1005         float t;
1006         vec3_t o, v, center;
1007         if (!cl_particles.integer) return;
1008
1009         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1010         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1011         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1012
1013         center[0] = (mins[0] + maxs[0]) * 0.5f;
1014         center[1] = (mins[1] + maxs[1]) * 0.5f;
1015         center[2] = (mins[2] + maxs[2]) * 0.5f;
1016
1017         count *= cl_particles_quality.value;
1018         while (count--)
1019         {
1020                 k = particlepalette[224 + (rand()&15)];
1021                 o[0] = lhrandom(mins[0], maxs[0]);
1022                 o[1] = lhrandom(mins[1], maxs[1]);
1023                 o[2] = lhrandom(mins[2], maxs[2]);
1024                 VectorSubtract(o, center, v);
1025                 VectorNormalize(v);
1026                 VectorScale(v, 100, v);
1027                 v[2] += sv_gravity.value * 0.15f;
1028                 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2, 0, 0);
1029         }
1030 }
1031
1032 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1033 {
1034         int k;
1035         float t;
1036         if (!cl_particles.integer) return;
1037         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1038         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1039         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1040
1041         count *= cl_particles_quality.value;
1042         while (count--)
1043         {
1044                 k = particlepalette[224 + (rand()&15)];
1045                 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), 0, 0, 32, 1, 0, 32);
1046                 if (count & 1)
1047                         particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), 0, 0, 24, 0, 0, 8);
1048         }
1049 }
1050
1051 void CL_Flames (vec3_t org, vec3_t vel, int count)
1052 {
1053         int k;
1054         if (!cl_particles.integer) return;
1055
1056         count *= cl_particles_quality.value;
1057         while (count--)
1058         {
1059                 k = particlepalette[224 + (rand()&15)];
1060                 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1, 0, 128);
1061         }
1062 }
1063
1064
1065
1066 /*
1067 ===============
1068 CL_LavaSplash
1069
1070 ===============
1071 */
1072 void CL_LavaSplash (vec3_t origin)
1073 {
1074         float i, j, inc, vel;
1075         int k, l;
1076         vec3_t          dir, org;
1077         if (!cl_particles.integer) return;
1078
1079         if (cl_particles_quake.integer)
1080         {
1081                 inc = 8 / cl_particles_quality.value;
1082                 for (i = -128;i < 128;i += inc)
1083                 {
1084                         for (j = -128;j < 128;j += inc)
1085                         {
1086                                 dir[0] = j + lhrandom(0, inc);
1087                                 dir[1] = i + lhrandom(0, inc);
1088                                 dir[2] = 256;
1089                                 org[0] = origin[0] + dir[0];
1090                                 org[1] = origin[1] + dir[1];
1091                                 org[2] = origin[2] + lhrandom(0, 64);
1092                                 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1093                                 k = l = particlepalette[224 + (rand()&7)];
1094                                 particle(particletype + pt_alphastatic, k, l, tex_particle, 1, inc * lhrandom(24, 32), inc * 12, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1095                         }
1096                 }
1097         }
1098         else
1099         {
1100                 inc = 32 / cl_particles_quality.value;
1101                 for (i = -128;i < 128;i += inc)
1102                 {
1103                         for (j = -128;j < 128;j += inc)
1104                         {
1105                                 dir[0] = j + lhrandom(0, inc);
1106                                 dir[1] = i + lhrandom(0, inc);
1107                                 dir[2] = 256;
1108                                 org[0] = origin[0] + dir[0];
1109                                 org[1] = origin[1] + dir[1];
1110                                 org[2] = origin[2] + lhrandom(0, 64);
1111                                 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1112                                 if (gamemode == GAME_GOODVSBAD2)
1113                                 {
1114                                         k = particlepalette[0 + (rand()&255)];
1115                                         l = particlepalette[0 + (rand()&255)];
1116                                         particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1117                                 }
1118                                 else
1119                                 {
1120                                         k = l = particlepalette[224 + (rand()&7)];
1121                                         particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1122                                 }
1123                         }
1124                 }
1125         }
1126 }
1127
1128 /*
1129 ===============
1130 CL_TeleportSplash
1131
1132 ===============
1133 */
1134 void CL_TeleportSplash (vec3_t org)
1135 {
1136         float i, j, k, inc;
1137         if (!cl_particles.integer) return;
1138
1139         if (cl_particles_quake.integer)
1140         {
1141                 inc = 4 / cl_particles_quality.value;
1142                 for (i = -16;i < 16;i += inc)
1143                 {
1144                         for (j = -16;j < 16;j += inc)
1145                         {
1146                                 for (k = -24;k < 32;k += inc)
1147                                 {
1148                                         vec3_t dir;
1149                                         float vel;
1150                                         VectorSet(dir, i*8, j*8, k*8);
1151                                         VectorNormalize(dir);
1152                                         vel = lhrandom(50, 113);
1153                                         particle(particletype + pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1, inc * lhrandom(37, 63), inc * 187, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1154                                 }
1155                         }
1156                 }
1157         }
1158         else
1159         {
1160                 inc = 8 / cl_particles_quality.value;
1161                 for (i = -16;i < 16;i += inc)
1162                         for (j = -16;j < 16;j += inc)
1163                                 for (k = -24;k < 32;k += inc)
1164                                         particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), 0, 0, lhrandom(-256, 256), 1, 0, 0);
1165         }
1166 }
1167
1168 #ifdef WORKINGLQUAKE
1169 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1170 #else
1171 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1172 #endif
1173 {
1174         vec3_t vec, dir, vel, pos;
1175         float len, dec, speed, qd;
1176         int smoke, blood, bubbles, r;
1177 #ifdef WORKINGLQUAKE
1178         int contents;
1179 #endif
1180
1181         if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1182                 return;
1183
1184         VectorSubtract(end, start, dir);
1185         VectorNormalize(dir);
1186
1187         VectorSubtract (end, start, vec);
1188 #ifdef WORKINGLQUAKE
1189         len = VectorNormalize (vec);
1190         dec = 0;
1191         speed = 1.0f / cl.frametime;
1192         VectorSubtract(end, start, vel);
1193 #else
1194         len = VectorNormalizeLength (vec);
1195         dec = -ent->persistent.trail_time;
1196         ent->persistent.trail_time += len;
1197         if (ent->persistent.trail_time < 0.01f)
1198                 return;
1199
1200         // if we skip out, leave it reset
1201         ent->persistent.trail_time = 0.0f;
1202
1203         speed = ent->state_current.time - ent->state_previous.time;
1204         if (speed)
1205                 speed = 1.0f / speed;
1206         VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1207         color = particlepalette[color];
1208 #endif
1209         VectorScale(vel, speed, vel);
1210
1211         // advance into this frame to reach the first puff location
1212         VectorMA(start, dec, vec, pos);
1213         len -= dec;
1214
1215         smoke = cl_particles.integer && cl_particles_smoke.integer;
1216         blood = cl_particles.integer && cl_particles_blood.integer;
1217 #ifdef WORKINGLQUAKE
1218         contents = CL_PointQ1Contents(pos);
1219         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1220 #else
1221         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1222 #endif
1223         qd = 1.0f / cl_particles_quality.value;
1224
1225         while (len >= 0)
1226         {
1227                 switch (type)
1228                 {
1229                         case 0: // rocket trail
1230                                 if (cl_particles_quake.integer)
1231                                 {
1232                                         dec = qd*3;
1233                                         r = rand()&3;
1234                                         color = particlepalette[ramp3[r]];
1235                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*42*(6-r), qd*306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1236                                 }
1237                                 else
1238                                 {
1239                                         dec = qd*3;
1240                                         if (smoke)
1241                                         {
1242                                                 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1243                                                 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 20);
1244                                         }
1245                                         if (bubbles)
1246                                                 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, (1.0 / 16.0), 0, 16);
1247                                 }
1248                                 break;
1249
1250                         case 1: // grenade trail
1251                                 if (cl_particles_quake.integer)
1252                                 {
1253                                         dec = qd*3;
1254                                         r = 2 + (rand()%5);
1255                                         color = particlepalette[ramp3[r]];
1256                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*42*(6-r), qd*306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1257                                 }
1258                                 else
1259                                 {
1260                                         dec = qd*3;
1261                                         if (smoke)
1262                                                 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1263                                 }
1264                                 break;
1265
1266
1267                         case 2: // blood
1268                         case 4: // slight blood
1269                                 if (cl_particles_quake.integer)
1270                                 {
1271                                         if (type == 2)
1272                                         {
1273                                                 dec = qd*3;
1274                                                 color = particlepalette[67 + (rand()&3)];
1275                                                 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1276                                         }
1277                                         else
1278                                         {
1279                                                 dec = qd*6;
1280                                                 color = particlepalette[67 + (rand()&3)];
1281                                                 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1282                                         }
1283                                 }
1284                                 else
1285                                 {
1286                                         dec = qd*16;
1287                                         if (blood)
1288                                                 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f, vel[1] * 0.5f, vel[2] * 0.5f, 1, 0, 64);
1289                                 }
1290                                 break;
1291
1292                         case 3: // green tracer
1293                                 if (cl_particles_quake.integer)
1294                                 {
1295                                         dec = qd*6;
1296                                         color = particlepalette[52 + (rand()&7)];
1297                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1298                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1299                                 }
1300                                 else
1301                                 {
1302                                         dec = qd*16;
1303                                         if (smoke)
1304                                         {
1305                                                 if (gamemode == GAME_GOODVSBAD2)
1306                                                 {
1307                                                         dec = qd*6;
1308                                                         particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1309                                                 }
1310                                                 else
1311                                                 {
1312                                                         dec = qd*3;
1313                                                         color = particlepalette[20 + (rand()&7)];
1314                                                         particle(particletype + pt_static, color, color, tex_particle, 2, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1315                                                 }
1316                                         }
1317                                 }
1318                                 break;
1319
1320                         case 5: // flame tracer
1321                                 if (cl_particles_quake.integer)
1322                                 {
1323                                         dec = qd*6;
1324                                         color = particlepalette[230 + (rand()&7)];
1325                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1326                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1327                                 }
1328                                 else
1329                                 {
1330                                         dec = qd*3;
1331                                         if (smoke)
1332                                         {
1333                                                 color = particlepalette[226 + (rand()&7)];
1334                                                 particle(particletype + pt_static, color, color, tex_particle, 2, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1335                                         }
1336                                 }
1337                                 break;
1338
1339                         case 6: // voor trail
1340                                 if (cl_particles_quake.integer)
1341                                 {
1342                                         dec = qd*3;
1343                                         color = particlepalette[152 + (rand()&3)];
1344                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*850, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 8, 0);
1345                                 }
1346                                 else
1347                                 {
1348                                         dec = qd*16;
1349                                         if (smoke)
1350                                         {
1351                                                 if (gamemode == GAME_GOODVSBAD2)
1352                                                 {
1353                                                         dec = qd*6;
1354                                                         particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1355                                                 }
1356                                                 else if (gamemode == GAME_PRYDON)
1357                                                 {
1358                                                         dec = qd*6;
1359                                                         particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1360                                                 }
1361                                                 else
1362                                                 {
1363                                                         dec = qd*3;
1364                                                         particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 3, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1365                                                 }
1366                                         }
1367                                 }
1368                                 break;
1369 #ifndef WORKINGLQUAKE
1370                         case 7: // Nehahra smoke tracer
1371                                 dec = qd*7;
1372                                 if (smoke)
1373                                         particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 4);
1374                                 break;
1375                         case 8: // Nexuiz plasma trail
1376                                 dec = qd*4;
1377                                 if (smoke)
1378                                         particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 16);
1379                                 break;
1380                         case 9: // glow trail
1381                                 dec = qd*3;
1382                                 if (smoke)
1383                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1384                                 break;
1385 #endif
1386                         default:
1387                                 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1388                 }
1389
1390                 // advance to next time and position
1391                 len -= dec;
1392                 VectorMA (pos, dec, vec, pos);
1393         }
1394 #ifndef WORKINGLQUAKE
1395         ent->persistent.trail_time = len;
1396 #endif
1397 }
1398
1399 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1400 {
1401         int tempcolor2, cr, cg, cb;
1402         cr = red * 255;
1403         cg = green * 255;
1404         cb = blue * 255;
1405         tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1406         particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0, 0, 0);
1407 }
1408
1409 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1410 {
1411         float f;
1412         if (!cl_particles.integer) return;
1413
1414         // smoke puff
1415         if (cl_particles_smoke.integer)
1416                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1417                         particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count * 0.5f);
1418 }
1419
1420 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1421 {
1422         float f;
1423         if (!cl_particles.integer) return;
1424
1425         if (cl_stainmaps.integer)
1426                 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1427         CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1428
1429         // smoke puff
1430         if (cl_particles_smoke.integer)
1431                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1432                         particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count);
1433
1434         // sparks
1435         if (cl_particles_sparks.integer)
1436                 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1437                         particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, count * 3.0f);
1438 }
1439
1440 /*
1441 ===============
1442 CL_MoveParticles
1443 ===============
1444 */
1445 void CL_MoveParticles (void)
1446 {
1447         particle_t *p;
1448         int i, maxparticle, j, a, content;
1449         float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1450         int hitent;
1451         trace_t trace;
1452
1453         // LordHavoc: early out condition
1454         if (!cl_numparticles)
1455         {
1456                 cl_freeparticle = 0;
1457                 return;
1458         }
1459
1460 #ifdef WORKINGLQUAKE
1461         frametime = cl.frametime;
1462 #else
1463         frametime = cl.time - cl.oldtime;
1464 #endif
1465         gravity = frametime * sv_gravity.value;
1466         dvel = 1+4*frametime;
1467         bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1468
1469         maxparticle = -1;
1470         j = 0;
1471         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1472         {
1473                 if (!p->type)
1474                         continue;
1475                 maxparticle = i;
1476                 content = 0;
1477
1478                 p->alpha -= p->alphafade * frametime;
1479
1480                 if (p->alpha <= 0)
1481                 {
1482                         p->type = NULL;
1483                         continue;
1484                 }
1485
1486                 if (p->type->orientation != PARTICLE_BEAM)
1487                 {
1488                         VectorCopy(p->org, oldorg);
1489                         VectorMA(p->org, frametime, p->vel, p->org);
1490                         VectorCopy(p->org, org);
1491                         if (p->bounce)
1492                         {
1493                                 if (p->type == particletype + pt_rain)
1494                                 {
1495                                         // raindrop - splash on solid/water/slime/lava
1496                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1497                                         if (trace.fraction < 1)
1498                                         {
1499                                                 // convert from a raindrop particle to a rainsplash decal
1500                                                 VectorCopy(trace.endpos, p->org);
1501                                                 VectorCopy(trace.plane.normal, p->vel);
1502                                                 VectorAdd(p->org, p->vel, p->org);
1503                                                 p->type = particletype + pt_raindecal;
1504                                                 p->texnum = tex_rainsplash[0];
1505                                                 p->time2 = cl.time;
1506                                                 p->alphafade = p->alpha / 0.4;
1507                                                 p->bounce = 0;
1508                                                 p->friction = 0;
1509                                                 p->gravity = 0;
1510                                                 p->size = 8.0;
1511                                         }
1512                                 }
1513                                 else if (p->type == particletype + pt_blood)
1514                                 {
1515                                         // blood - splash on solid
1516                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1517                                         if (trace.fraction < 1)
1518                                         {
1519                                                 // convert from a blood particle to a blood decal
1520                                                 VectorCopy(trace.endpos, p->org);
1521                                                 VectorCopy(trace.plane.normal, p->vel);
1522                                                 VectorAdd(p->org, p->vel, p->org);
1523 #ifndef WORKINGLQUAKE
1524                                                 if (cl_stainmaps.integer)
1525                                                         R_Stain(p->org, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1526 #endif
1527                                                 if (!cl_decals.integer)
1528                                                 {
1529                                                         p->type = NULL;
1530                                                         continue;
1531                                                 }
1532
1533                                                 p->type = particletype + pt_decal;
1534                                                 p->texnum = tex_blooddecal[rand()&7];
1535 #ifndef WORKINGLQUAKE
1536                                                 p->owner = hitent;
1537                                                 p->ownermodel = cl_entities[hitent].render.model;
1538                                                 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1539                                                 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1540 #endif
1541                                                 p->time2 = cl.time;
1542                                                 p->alphafade = 0;
1543                                                 p->bounce = 0;
1544                                                 p->friction = 0;
1545                                                 p->gravity = 0;
1546                                                 p->size *= 2.0f;
1547                                         }
1548                                 }
1549                                 else
1550                                 {
1551                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1552                                         if (trace.fraction < 1)
1553                                         {
1554                                                 VectorCopy(trace.endpos, p->org);
1555                                                 if (p->bounce < 0)
1556                                                 {
1557                                                         p->type = NULL;
1558                                                         continue;
1559                                                 }
1560                                                 else
1561                                                 {
1562                                                         dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1563                                                         VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1564                                                         if (DotProduct(p->vel, p->vel) < 0.03)
1565                                                                 VectorClear(p->vel);
1566                                                 }
1567                                         }
1568                                 }
1569                         }
1570                         p->vel[2] -= p->gravity * gravity;
1571
1572                         if (p->friction)
1573                         {
1574                                 f = p->friction * frametime;
1575 #ifdef WORKINGLQUAKE
1576                                 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1577 #else
1578                                 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1579 #endif
1580                                         f *= 4;
1581                                 f = 1.0f - f;
1582                                 VectorScale(p->vel, f, p->vel);
1583                         }
1584                 }
1585
1586                 if (p->type != particletype + pt_static)
1587                 {
1588                         switch (p->type - particletype)
1589                         {
1590                         case pt_entityparticle:
1591                                 // particle that removes itself after one rendered frame
1592                                 if (p->time2)
1593                                         p->type = NULL;
1594                                 else
1595                                         p->time2 = 1;
1596                                 break;
1597                         case pt_blood:
1598 #ifdef WORKINGLQUAKE
1599                                 a = CL_PointQ1Contents(p->org);
1600                                 if (a <= CONTENTS_WATER)
1601 #else
1602                                 a = CL_PointSuperContents(p->org);
1603                                 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1604 #endif
1605                                 {
1606                                         p->size += frametime * 8;
1607                                         //p->alpha -= bloodwaterfade;
1608                                 }
1609                                 else
1610                                         p->vel[2] -= gravity;
1611 #ifdef WORKINGLQUAKE
1612                                 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1613 #else
1614                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1615 #endif
1616                                         p->type = NULL;
1617                                 break;
1618                         case pt_bubble:
1619 #ifdef WORKINGLQUAKE
1620                                 a = CL_PointQ1Contents(p->org);
1621                                 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1622 #else
1623                                 a = CL_PointSuperContents(p->org);
1624                                 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1625 #endif
1626                                 {
1627                                         p->type = NULL;
1628                                         break;
1629                                 }
1630                                 break;
1631                         case pt_rain:
1632 #ifdef WORKINGLQUAKE
1633                                 a = CL_PointQ1Contents(p->org);
1634                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1635 #else
1636                                 a = CL_PointSuperContents(p->org);
1637                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1638 #endif
1639                                         p->type = NULL;
1640                                 break;
1641                         case pt_snow:
1642                                 if (cl.time > p->time2)
1643                                 {
1644                                         // snow flutter
1645                                         p->time2 = cl.time + (rand() & 3) * 0.1;
1646                                         p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1647                                         p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1648                                         //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1649                                 }
1650 #ifdef WORKINGLQUAKE
1651                                 a = CL_PointQ1Contents(p->org);
1652                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1653 #else
1654                                 a = CL_PointSuperContents(p->org);
1655                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1656 #endif
1657                                         p->type = NULL;
1658                                 break;
1659                         case pt_smoke:
1660                                 //p->size += frametime * 15;
1661                                 break;
1662                         case pt_decal:
1663                                 // FIXME: this has fairly wacky handling of alpha
1664                                 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1665 #ifndef WORKINGLQUAKE
1666                                 if (cl_entities[p->owner].render.model == p->ownermodel)
1667                                 {
1668                                         Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1669                                         Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1670                                 }
1671                                 else
1672                                         p->type = NULL;
1673 #endif
1674                                 break;
1675                         case pt_raindecal:
1676                                 a = max(0, (cl.time - p->time2) * 40);
1677                                 if (a < 16)
1678                                         p->texnum = tex_rainsplash[a];
1679                                 else
1680                                         p->type = NULL;
1681                                 break;
1682                         default:
1683                                 break;
1684                         }
1685                 }
1686         }
1687         cl_numparticles = maxparticle + 1;
1688         cl_freeparticle = 0;
1689 }
1690
1691 #define MAX_PARTICLETEXTURES 64
1692 // particletexture_t is a rectangle in the particlefonttexture
1693 typedef struct particletexture_s
1694 {
1695         rtexture_t *texture;
1696         float s1, t1, s2, t2;
1697 }
1698 particletexture_t;
1699
1700 #if WORKINGLQUAKE
1701 static int particlefonttexture;
1702 #else
1703 static rtexturepool_t *particletexturepool;
1704 static rtexture_t *particlefonttexture;
1705 #endif
1706 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1707
1708 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1709
1710 #define PARTICLETEXTURESIZE 64
1711 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1712
1713 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1714 {
1715         float dz, f, dot;
1716         vec3_t normal;
1717         dz = 1 - (dx*dx+dy*dy);
1718         if (dz > 0) // it does hit the sphere
1719         {
1720                 f = 0;
1721                 // back side
1722                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1723                 VectorNormalize(normal);
1724                 dot = DotProduct(normal, light);
1725                 if (dot > 0.5) // interior reflection
1726                         f += ((dot *  2) - 1);
1727                 else if (dot < -0.5) // exterior reflection
1728                         f += ((dot * -2) - 1);
1729                 // front side
1730                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1731                 VectorNormalize(normal);
1732                 dot = DotProduct(normal, light);
1733                 if (dot > 0.5) // interior reflection
1734                         f += ((dot *  2) - 1);
1735                 else if (dot < -0.5) // exterior reflection
1736                         f += ((dot * -2) - 1);
1737                 f *= 128;
1738                 f += 16; // just to give it a haze so you can see the outline
1739                 f = bound(0, f, 255);
1740                 return (unsigned char) f;
1741         }
1742         else
1743                 return 0;
1744 }
1745
1746 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1747 {
1748         int basex, basey, y;
1749         basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1750         basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1751         particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1752         particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1753         particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1754         particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1755         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1756                 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1757 }
1758
1759 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1760 {
1761         int x, y;
1762         float cx, cy, dx, dy, f, iradius;
1763         unsigned char *d;
1764         cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1765         cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1766         iradius = 1.0f / radius;
1767         alpha *= (1.0f / 255.0f);
1768         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1769         {
1770                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1771                 {
1772                         dx = (x - cx);
1773                         dy = (y - cy);
1774                         f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1775                         if (f > 0)
1776                         {
1777                                 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1778                                 d[0] += f * (red   - d[0]);
1779                                 d[1] += f * (green - d[1]);
1780                                 d[2] += f * (blue  - d[2]);
1781                         }
1782                 }
1783         }
1784 }
1785
1786 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1787 {
1788         int i;
1789         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1790         {
1791                 data[0] = bound(minr, data[0], maxr);
1792                 data[1] = bound(ming, data[1], maxg);
1793                 data[2] = bound(minb, data[2], maxb);
1794         }
1795 }
1796
1797 void particletextureinvert(unsigned char *data)
1798 {
1799         int i;
1800         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1801         {
1802                 data[0] = 255 - data[0];
1803                 data[1] = 255 - data[1];
1804                 data[2] = 255 - data[2];
1805         }
1806 }
1807
1808 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1809 static void R_InitBloodTextures (unsigned char *particletexturedata)
1810 {
1811         int i, j, k, m;
1812         unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1813
1814         // blood particles
1815         for (i = 0;i < 8;i++)
1816         {
1817                 memset(&data[0][0][0], 255, sizeof(data));
1818                 for (k = 0;k < 24;k++)
1819                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1820                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1821                 particletextureinvert(&data[0][0][0]);
1822                 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1823         }
1824
1825         // blood decals
1826         for (i = 0;i < 8;i++)
1827         {
1828                 memset(&data[0][0][0], 255, sizeof(data));
1829                 m = 8;
1830                 for (j = 1;j < 10;j++)
1831                         for (k = min(j, m - 1);k < m;k++)
1832                                 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1833                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1834                 particletextureinvert(&data[0][0][0]);
1835                 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1836         }
1837
1838 }
1839
1840 static void R_InitParticleTexture (void)
1841 {
1842         int x, y, d, i, k, m;
1843         float dx, dy, radius, f, f2;
1844         unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1845         vec3_t light;
1846         unsigned char *particletexturedata;
1847
1848         // a note: decals need to modulate (multiply) the background color to
1849         // properly darken it (stain), and they need to be able to alpha fade,
1850         // this is a very difficult challenge because it means fading to white
1851         // (no change to background) rather than black (darkening everything
1852         // behind the whole decal polygon), and to accomplish this the texture is
1853         // inverted (dark red blood on white background becomes brilliant cyan
1854         // and white on black background) so we can alpha fade it to black, then
1855         // we invert it again during the blendfunc to make it work...
1856
1857         particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1858         memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1859
1860         // smoke
1861         for (i = 0;i < 8;i++)
1862         {
1863                 memset(&data[0][0][0], 255, sizeof(data));
1864                 do
1865                 {
1866                         unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1867
1868                         fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1869                         fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1870                         m = 0;
1871                         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1872                         {
1873                                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1874                                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1875                                 {
1876                                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1877                                         d = (noise2[y][x] - 128) * 3 + 192;
1878                                         if (d > 0)
1879                                                 d = d * (1-(dx*dx+dy*dy));
1880                                         d = (d * noise1[y][x]) >> 7;
1881                                         d = bound(0, d, 255);
1882                                         data[y][x][3] = (unsigned char) d;
1883                                         if (m < d)
1884                                                 m = d;
1885                                 }
1886                         }
1887                 }
1888                 while (m < 224);
1889                 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1890         }
1891
1892         // rain splash
1893         for (i = 0;i < 16;i++)
1894         {
1895                 memset(&data[0][0][0], 255, sizeof(data));
1896                 radius = i * 3.0f / 4.0f / 16.0f;
1897                 f2 = 255.0f * ((15.0f - i) / 15.0f);
1898                 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1899                 {
1900                         dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1901                         for (x = 0;x < PARTICLETEXTURESIZE;x++)
1902                         {
1903                                 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1904                                 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1905                                 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1906                         }
1907                 }
1908                 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1909         }
1910
1911         // normal particle
1912         memset(&data[0][0][0], 255, sizeof(data));
1913         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1914         {
1915                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1916                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1917                 {
1918                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1919                         d = 256 * (1 - (dx*dx+dy*dy));
1920                         d = bound(0, d, 255);
1921                         data[y][x][3] = (unsigned char) d;
1922                 }
1923         }
1924         setuptex(tex_particle, &data[0][0][0], particletexturedata);
1925
1926         // rain
1927         memset(&data[0][0][0], 255, sizeof(data));
1928         light[0] = 1;light[1] = 1;light[2] = 1;
1929         VectorNormalize(light);
1930         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1931         {
1932                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1933                 // stretch upper half of bubble by +50% and shrink lower half by -50%
1934                 // (this gives an elongated teardrop shape)
1935                 if (dy > 0.5f)
1936                         dy = (dy - 0.5f) * 2.0f;
1937                 else
1938                         dy = (dy - 0.5f) / 1.5f;
1939                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1940                 {
1941                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1942                         // shrink bubble width to half
1943                         dx *= 2.0f;
1944                         data[y][x][3] = shadebubble(dx, dy, light);
1945                 }
1946         }
1947         setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1948
1949         // bubble
1950         memset(&data[0][0][0], 255, sizeof(data));
1951         light[0] = 1;light[1] = 1;light[2] = 1;
1952         VectorNormalize(light);
1953         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1954         {
1955                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1956                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1957                 {
1958                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1959                         data[y][x][3] = shadebubble(dx, dy, light);
1960                 }
1961         }
1962         setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1963
1964         // Blood particles and blood decals
1965         R_InitBloodTextures (particletexturedata);
1966
1967         // bullet decals
1968         for (i = 0;i < 8;i++)
1969         {
1970                 memset(&data[0][0][0], 255, sizeof(data));
1971                 for (k = 0;k < 12;k++)
1972                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1973                 for (k = 0;k < 3;k++)
1974                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1975                 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1976                 particletextureinvert(&data[0][0][0]);
1977                 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1978         }
1979
1980 #if WORKINGLQUAKE
1981         glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1982         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1983         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1984 #else
1985
1986 #if 0
1987         Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1988 #endif
1989
1990         particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1991         if (!particlefonttexture)
1992                 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1993         for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1994                 particletexture[i].texture = particlefonttexture;
1995
1996         // nexbeam
1997         fractalnoise(&noise3[0][0], 64, 4);
1998         m = 0;
1999         for (y = 0;y < 64;y++)
2000         {
2001                 dy = (y - 0.5f*64) / (64*0.5f-1);
2002                 for (x = 0;x < 16;x++)
2003                 {
2004                         dx = (x - 0.5f*16) / (16*0.5f-2);
2005                         d = (1 - sqrt(fabs(dx))) * noise3[y][x];
2006                         data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2007                         data2[y][x][3] = 255;
2008                 }
2009         }
2010
2011 #if 0
2012         Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2013 #endif
2014
2015         particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
2016         if (!particletexture[tex_beam].texture)
2017                 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2018         particletexture[tex_beam].s1 = 0;
2019         particletexture[tex_beam].t1 = 0;
2020         particletexture[tex_beam].s2 = 1;
2021         particletexture[tex_beam].t2 = 1;
2022 #endif
2023         Mem_Free(particletexturedata);
2024 }
2025
2026 static void r_part_start(void)
2027 {
2028         particletexturepool = R_AllocTexturePool();
2029         R_InitParticleTexture ();
2030 }
2031
2032 static void r_part_shutdown(void)
2033 {
2034         R_FreeTexturePool(&particletexturepool);
2035 }
2036
2037 static void r_part_newmap(void)
2038 {
2039         cl_numparticles = 0;
2040         cl_freeparticle = 0;
2041 }
2042
2043 void R_Particles_Init (void)
2044 {
2045         Cvar_RegisterVariable(&r_drawparticles);
2046 #ifdef WORKINGLQUAKE
2047         r_part_start();
2048 #else
2049         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2050 #endif
2051 }
2052
2053 #ifdef WORKINGLQUAKE
2054 void R_InitParticles(void)
2055 {
2056         CL_Particles_Init();
2057         R_Particles_Init();
2058 }
2059 #endif
2060
2061 float particle_vertex3f[12], particle_texcoord2f[8];
2062
2063 #ifdef WORKINGLQUAKE
2064 void R_DrawParticle(particle_t *p)
2065 {
2066 #else
2067 void R_DrawParticleCallback(const void *calldata1, int calldata2)
2068 {
2069         const particle_t *p = (particle_t *)calldata1;
2070         rmeshstate_t m;
2071 #endif
2072         pblend_t blendmode;
2073         float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
2074         particletexture_t *tex;
2075
2076         VectorCopy(p->org, org);
2077
2078         blendmode = p->type->blendmode;
2079         tex = &particletexture[p->texnum];
2080         cr = p->color[0] * (1.0f / 255.0f);
2081         cg = p->color[1] * (1.0f / 255.0f);
2082         cb = p->color[2] * (1.0f / 255.0f);
2083         ca = p->alpha * (1.0f / 255.0f);
2084         if (blendmode == PBLEND_MOD)
2085         {
2086                 cr *= ca;
2087                 cg *= ca;
2088                 cb *= ca;
2089                 cr = min(cr, 1);
2090                 cg = min(cg, 1);
2091                 cb = min(cb, 1);
2092                 ca = 1;
2093         }
2094 #ifndef WORKINGLQUAKE
2095         if (p->type->lighting)
2096         {
2097                 float ambient[3], diffuse[3], diffusenormal[3];
2098                 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
2099                 cr *= (ambient[0] + 0.5 * diffuse[0]);
2100                 cg *= (ambient[1] + 0.5 * diffuse[1]);
2101                 cb *= (ambient[2] + 0.5 * diffuse[2]);
2102         }
2103         if (fogenabled)
2104         {
2105                 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
2106                 ifog = 1 - fog;
2107                 cr = cr * ifog;
2108                 cg = cg * ifog;
2109                 cb = cb * ifog;
2110                 if (blendmode == PBLEND_ALPHA)
2111                 {
2112                         cr += fogcolor[0] * fog;
2113                         cg += fogcolor[1] * fog;
2114                         cb += fogcolor[2] * fog;
2115                 }
2116         }
2117
2118         R_Mesh_Matrix(&r_identitymatrix);
2119
2120         memset(&m, 0, sizeof(m));
2121         m.tex[0] = R_GetTexture(tex->texture);
2122         m.pointer_texcoord[0] = particle_texcoord2f;
2123         m.pointer_vertex = particle_vertex3f;
2124         R_Mesh_State(&m);
2125
2126         GL_Color(cr, cg, cb, ca);
2127
2128         if (blendmode == PBLEND_ALPHA)
2129                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2130         else if (blendmode == PBLEND_ADD)
2131                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2132         else //if (blendmode == PBLEND_MOD)
2133                 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2134         GL_DepthMask(false);
2135         GL_DepthTest(true);
2136 #endif
2137         size = p->size * cl_particles_size.value;
2138         if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2139         {
2140                 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2141                 {
2142                         // double-sided
2143                         if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
2144                         {
2145                                 VectorNegate(p->vel, v);
2146                                 VectorVectors(v, right, up);
2147                         }
2148                         else
2149                                 VectorVectors(p->vel, right, up);
2150                         VectorScale(right, size, right);
2151                         VectorScale(up, size, up);
2152                 }
2153                 else
2154                 {
2155                         VectorScale(r_viewleft, -size, right);
2156                         VectorScale(r_viewup, size, up);
2157                 }
2158                 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
2159                 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
2160                 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
2161                 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
2162                 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
2163                 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
2164                 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
2165                 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
2166                 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2167                 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2168                 particle_vertex3f[10] = org[1] + right[1] - up[1];
2169                 particle_vertex3f[11] = org[2] + right[2] - up[2];
2170                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2171                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2172                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2173                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2174         }
2175         else if (p->type->orientation == PARTICLE_SPARK)
2176         {
2177                 VectorMA(p->org, -0.02, p->vel, v);
2178                 VectorMA(p->org, 0.02, p->vel, up2);
2179                 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2180                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2181                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2182                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2183                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2184         }
2185         else if (p->type->orientation == PARTICLE_BEAM)
2186         {
2187                 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2188                 VectorSubtract(p->vel, p->org, up);
2189                 VectorNormalize(up);
2190                 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2191                 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2192                 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2193                 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2194                 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2195                 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2196         }
2197         else
2198         {
2199                 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2200                 return;
2201         }
2202
2203 #if WORKINGLQUAKE
2204         if (blendmode == PBLEND_ALPHA)
2205                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2206         else if (blendmode == PBLEND_ADD)
2207                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2208         else //if (blendmode == PBLEND_MOD)
2209                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2210         glColor4f(cr, cg, cb, ca);
2211         glBegin(GL_QUADS);
2212         glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2213         glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2214         glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2215         glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2216         glEnd();
2217 #else
2218         R_Mesh_Draw(0, 4, 2, polygonelements);
2219 #endif
2220 }
2221
2222 void R_DrawParticles (void)
2223 {
2224         int i;
2225         float minparticledist;
2226         particle_t *p;
2227
2228 #ifdef WORKINGLQUAKE
2229         CL_MoveParticles();
2230 #endif
2231
2232         // LordHavoc: early out conditions
2233         if ((!cl_numparticles) || (!r_drawparticles.integer))
2234                 return;
2235
2236         minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2237
2238 #ifdef WORKINGLQUAKE
2239         glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2240         glEnable(GL_BLEND);
2241         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2242         glDepthMask(0);
2243         // LordHavoc: only render if not too close
2244         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2245                 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2246                         R_DrawParticle(p);
2247         glDepthMask(1);
2248         glDisable(GL_BLEND);
2249         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2250 #else
2251         // LordHavoc: only render if not too close
2252         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2253         {
2254                 if (p->type)
2255                 {
2256                         renderstats.particles++;
2257                         if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2258                         {
2259                                 if (p->type == particletype + pt_decal)
2260                                         R_DrawParticleCallback(p, 0);
2261                                 else
2262                                         R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);
2263                         }
2264                 }
2265         }
2266 #endif
2267 }
2268