]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_particles.c
added md4four map checksumming, since QW servers seem to be very picky about map...
[xonotic/darkplaces.git] / cl_particles.c
index b772d9ffe8f21abc2475cef2c0c39d77f9ed70a4..a7ea21464198478a09f241f1c5e4fadd174cfffc 100644 (file)
@@ -253,40 +253,44 @@ particle_t;
 
 static int particlepalette[256] =
 {
-       0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
-       0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
-       0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
-       0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
-       0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
-       0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
-       0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
-       0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
-       0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
-       0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
-       0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
-       0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
-       0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
-       0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
-       0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
-       0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
-       0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
-       0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
-       0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
-       0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
-       0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
-       0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
-       0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
-       0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
-       0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
-       0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
-       0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
-       0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
-       0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
-       0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
-       0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
-       0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
+       0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
+       0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
+       0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
+       0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
+       0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
+       0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
+       0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
+       0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
+       0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
+       0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
+       0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
+       0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
+       0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
+       0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
+       0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
+       0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
+       0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
+       0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
+       0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
+       0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
+       0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
+       0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
+       0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
+       0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
+       0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
+       0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
+       0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
+       0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
+       0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
+       0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
+       0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
+       0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53  // 248-255
 };
 
+int            ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
+int            ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
+int            ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
+
 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
 
 // texture numbers in particle font
@@ -305,26 +309,27 @@ static int                        cl_numparticles;
 static int                     cl_freeparticle;
 static particle_t      *particles;
 
-cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
-cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
-cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
-cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
-cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
-cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
-cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
-cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
-cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
-cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
-cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
-cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
-cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
-cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
-cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
-cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
-cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
-cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
-cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
-cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
+cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
+cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles and reduces their alpha"};
+cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
+cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
+cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1", "enables blood shower effects"};
+cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
+cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5", "opacity of blood"};
+cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
+cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
+cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1", "enables bubbles from underwater explosions"};
+cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0", "enables smoke from explosions"};
+cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
+cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
+cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
+cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
+cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
+cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
+cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
+cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0", "enables decals (bullet holes, blood, etc)"};
+cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0", "how long before decals start to fade away"};
+cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20", "how long decals take to fade away"};
 
 void CL_Particles_Clear(void)
 {
@@ -355,11 +360,12 @@ void CL_Particles_Init (void)
        else
                cl_maxparticles = MAX_PARTICLES;
 
-       Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
+       Cmd_AddCommand ("pointfile", CL_ReadPointFile_f, "display point file produced by qbsp when a leak was detected in the map (a line leading through the leak hole, to an entity inside the level)");
 
        Cvar_RegisterVariable (&cl_particles);
        Cvar_RegisterVariable (&cl_particles_quality);
        Cvar_RegisterVariable (&cl_particles_size);
+       Cvar_RegisterVariable (&cl_particles_quake);
        Cvar_RegisterVariable (&cl_particles_bloodshowers);
        Cvar_RegisterVariable (&cl_particles_blood);
        Cvar_RegisterVariable (&cl_particles_blood_alpha);
@@ -406,25 +412,11 @@ void CL_Particles_Shutdown (void)
 // px,py,pz - starting origin of particle
 // pvx,pvy,pvz - starting velocity of particle
 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
-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)
+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)
 {
+       int l1, l2;
        particle_t *part;
-       int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
-       ptempcolor = (pcolor1);
-       ptempcolor2 = (pcolor2);
-       pcr2 = ((ptempcolor2) >> 16) & 0xFF;
-       pcg2 = ((ptempcolor2) >> 8) & 0xFF;
-       pcb2 = (ptempcolor2) & 0xFF;
-       if (ptempcolor != ptempcolor2)
-       {
-               pcr1 = ((ptempcolor) >> 16) & 0xFF;
-               pcg1 = ((ptempcolor) >> 8) & 0xFF;
-               pcb1 = (ptempcolor) & 0xFF;
-               ptempcolor = rand() & 0xFF;
-               pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
-               pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
-               pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
-       }
+       vec3_t v;
        for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
        if (cl_freeparticle >= cl_maxparticles)
                return NULL;
@@ -432,25 +424,28 @@ particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex,
        if (cl_numparticles < cl_freeparticle)
                cl_numparticles = cl_freeparticle;
        memset(part, 0, sizeof(*part));
-       part->type = (ptype);
-       part->color[0] = pcr2;
-       part->color[1] = pcg2;
-       part->color[2] = pcb2;
+       part->type = ptype;
+       l2 = (int)lhrandom(0.5, 256.5);
+       l1 = 256 - l2;
+       part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
+       part->color[1] = ((((pcolor1 >>  8) & 0xFF) * l1 + ((pcolor2 >>  8) & 0xFF) * l2) >> 8) & 0xFF;
+       part->color[2] = ((((pcolor1 >>  0) & 0xFF) * l1 + ((pcolor2 >>  0) & 0xFF) * l2) >> 8) & 0xFF;
        part->color[3] = 0xFF;
        part->texnum = ptex;
-       part->size = (psize);
-       part->alpha = (palpha);
-       part->alphafade = (palphafade);
-       part->gravity = (pgravity);
-       part->bounce = (pbounce);
-       part->org[0] = (px);
-       part->org[1] = (py);
-       part->org[2] = (pz);
-       part->vel[0] = (pvx);
-       part->vel[1] = (pvy);
-       part->vel[2] = (pvz);
+       part->size = psize;
+       part->alpha = palpha;
+       part->alphafade = palphafade;
+       part->gravity = pgravity;
+       part->bounce = pbounce;
+       VectorRandom(v);
+       part->org[0] = px + originjitter * v[0];
+       part->org[1] = py + originjitter * v[1];
+       part->org[2] = pz + originjitter * v[2];
+       part->vel[0] = pvx + velocityjitter * v[0];
+       part->vel[1] = pvy + velocityjitter * v[1];
+       part->vel[2] = pvz + velocityjitter * v[2];
        part->time2 = 0;
-       part->friction = (pfriction);
+       part->friction = pfriction;
        return part;
 }
 
@@ -459,7 +454,7 @@ void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t
        particle_t *p;
        if (!cl_decals.integer)
                return;
-       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);
+       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);
        if (p)
        {
                p->time2 = cl.time;
@@ -505,40 +500,29 @@ CL_EntityParticles
 */
 void CL_EntityParticles (entity_t *ent)
 {
-       int                     i;
-       float           angle;
-       float           sp, sy, cp, cy;
-       vec3_t          forward;
-       float           dist;
-       float           beamlength;
+       int i;
+       float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
        static vec3_t avelocities[NUMVERTEXNORMALS];
        if (!cl_particles.integer) return;
 
-       dist = 64;
-       beamlength = 16;
+#ifdef WORKINGLQUAKE
+       VectorCopy(ent->origin, org);
+#else
+       Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
+#endif
 
        if (!avelocities[0][0])
-               for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
-                       avelocities[0][i] = (rand()&255) * 0.01;
+               for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
+                       avelocities[0][i] = lhrandom(0, 2.55);
 
-       for (i=0 ; i<NUMVERTEXNORMALS ; i++)
+       for (i = 0;i < NUMVERTEXNORMALS;i++)
        {
-               angle = cl.time * avelocities[i][0];
-               sy = sin(angle);
-               cy = cos(angle);
-               angle = cl.time * avelocities[i][1];
-               sp = sin(angle);
-               cp = cos(angle);
-
-               forward[0] = cp*cy;
-               forward[1] = cp*sy;
-               forward[2] = -sp;
-
-#ifdef WORKINGLQUAKE
-               particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
-#else
-               particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
-#endif
+               yaw = cl.time * avelocities[i][0];
+               pitch = cl.time * avelocities[i][1];
+               v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
+               v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
+               v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
+               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);
        }
 }
 
@@ -594,7 +578,7 @@ void CL_ReadPointFile_f (void)
                if (cl_numparticles < cl_maxparticles - 3)
                {
                        s++;
-                       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);
+                       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);
                }
        }
 #ifndef WORKINGLQUAKE
@@ -603,9 +587,9 @@ void CL_ReadPointFile_f (void)
        VectorCopy(leakorg, org);
        Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
 
-       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);
-       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);
-       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);
+       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);
+       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);
+       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);
 }
 
 /*
@@ -620,9 +604,9 @@ void CL_ParseParticleEffect (void)
        vec3_t org, dir;
        int i, count, msgcount, color;
 
-       MSG_ReadVector(org, cl.protocol);
+       MSG_ReadVector(org, cls.protocol);
        for (i=0 ; i<3 ; i++)
-               dir[i] = MSG_ReadChar () * (1.0/16);
+               dir[i] = MSG_ReadChar ();
        msgcount = MSG_ReadByte ();
        color = MSG_ReadByte ();
 
@@ -631,7 +615,7 @@ void CL_ParseParticleEffect (void)
        else
                count = msgcount;
 
-       if (cl_particles_blood_bloodhack.integer)
+       if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer)
        {
                if (color == 73)
                {
@@ -665,47 +649,68 @@ void CL_ParticleExplosion (vec3_t org)
                R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
        CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
 
-       i = CL_PointSuperContents(org);
-       if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
+       if (cl_particles_quake.integer)
        {
-               if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
-                       for (i = 0;i < 128 * cl_particles_quality.value;i++)
-                               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] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), (1.0 / 16.0));
+               for (i = 0;i < 1024;i++)
+               {
+                       int r, color;
+                       r = rand()&3;
+                       if (i & 1)
+                       {
+                               color = particlepalette[ramp1[r]];
+                               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);
+                       }
+                       else
+                       {
+                               color = particlepalette[ramp2[r]];
+                               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);
+                       }
+               }
        }
        else
        {
-               // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
-               // smoke puff
-               if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
+               i = CL_PointSuperContents(org);
+               if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
                {
-                       for (i = 0;i < 32;i++)
+                       if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
+                               for (i = 0;i < 128 * cl_particles_quality.value;i++)
+                                       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);
+               }
+               else
+               {
+                       // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
+                       // smoke puff
+                       if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
                        {
-                               int k;
-                               vec3_t v, v2;
+                               for (i = 0;i < 32;i++)
+                               {
+                                       int k;
+                                       vec3_t v, v2;
 #ifdef WORKINGLQUAKE
-                               v2[0] = lhrandom(-48, 48);
-                               v2[1] = lhrandom(-48, 48);
-                               v2[2] = lhrandom(-48, 48);
+                                       v2[0] = lhrandom(-48, 48);
+                                       v2[1] = lhrandom(-48, 48);
+                                       v2[2] = lhrandom(-48, 48);
 #else
-                               for (k = 0;k < 16;k++)
-                               {
-                                       v[0] = org[0] + lhrandom(-48, 48);
-                                       v[1] = org[1] + lhrandom(-48, 48);
-                                       v[2] = org[2] + lhrandom(-48, 48);
-                                       trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
-                                       if (trace.fraction >= 0.1)
-                                               break;
-                               }
-                               VectorSubtract(trace.endpos, org, v2);
+                                       for (k = 0;k < 16;k++)
+                                       {
+                                               v[0] = org[0] + lhrandom(-48, 48);
+                                               v[1] = org[1] + lhrandom(-48, 48);
+                                               v[2] = org[2] + lhrandom(-48, 48);
+                                               trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
+                                               if (trace.fraction >= 0.1)
+                                                       break;
+                                       }
+                                       VectorSubtract(trace.endpos, org, v2);
 #endif
-                               VectorScale(v2, 2.0f, v2);
-                               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);
+                                       VectorScale(v2, 2.0f, v2);
+                                       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);
+                               }
                        }
-               }
 
-               if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
-                       for (i = 0;i < 128 * cl_particles_quality.value;i++)
-                               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], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
+                       if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
+                               for (i = 0;i < 128 * cl_particles_quality.value;i++)
+                                       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);
+               }
        }
 
        if (cl_particles_explosions_shell.integer)
@@ -720,20 +725,16 @@ CL_ParticleExplosion2
 */
 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
 {
-       vec3_t vel;
-       vec3_t offset;
        int i, k;
-       float pscale;
        if (!cl_particles.integer) return;
 
        for (i = 0;i < 512 * cl_particles_quality.value;i++)
        {
-               VectorRandom (offset);
-               VectorScale (offset, 192, vel);
-               VectorScale (offset, 8, offset);
                k = particlepalette[colorStart + (i % colorLength)];
-               pscale = lhrandom(0.5, 1.5);
-               particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
+               if (cl_particles_quake.integer)
+                       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);
+               else
+                       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);
        }
 }
 
@@ -745,7 +746,28 @@ CL_BlobExplosion
 */
 void CL_BlobExplosion (vec3_t org)
 {
-       CL_ParticleExplosion(org);
+       int i, k;
+       if (!cl_particles.integer) return;
+
+       if (!cl_particles_quake.integer)
+       {
+               CL_ParticleExplosion(org);
+               return;
+       }
+
+       for (i = 0;i < 1024 * cl_particles_quality.value;i++)
+       {
+               if (i & 1)
+               {
+                       k = particlepalette[66 + rand()%6];
+                       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);
+               }
+               else
+               {
+                       k = particlepalette[150 + rand()%6];
+                       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);
+               }
+       }
 }
 
 /*
@@ -764,14 +786,26 @@ void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
                return;
        }
        if (!cl_particles.integer) return;
-       count *= cl_particles_quality.value;
-       while (count--)
+       if (cl_particles_quake.integer)
        {
-               k = particlepalette[color + (rand()&7)];
-               if (gamemode == GAME_GOODVSBAD2)
-                       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] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-10, 10), lhrandom(-10, 10), lhrandom(-10, 10), 0);
-               else
-                       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] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), dir[0] + lhrandom(-15, 15), dir[1] + lhrandom(-15, 15), dir[2] + lhrandom(-15, 15), 0);
+               count *= cl_particles_quality.value;
+               while (count--)
+               {
+                       k = particlepalette[color + (rand()&7)];
+                       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);
+               }
+       }
+       else
+       {
+               count *= cl_particles_quality.value;
+               while (count--)
+               {
+                       k = particlepalette[color + (rand()&7)];
+                       if (gamemode == GAME_GOODVSBAD2)
+                               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);
+                       else
+                               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);
+               }
        }
 }
 
@@ -781,7 +815,7 @@ void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
 CL_SparkShower
 ===============
 */
-void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
+void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius)
 {
        int k;
 
@@ -794,12 +828,12 @@ void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
                while(count--)
                {
                        k = particlepalette[0x68 + (rand() & 7)];
-                       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], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
+                       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] + sv_gravity.value * 0.1, 0, radius, 64);
                }
        }
 }
 
-void CL_Smoke (vec3_t org, vec3_t dir, int count)
+void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius)
 {
        vec3_t org2;
        int k;
@@ -817,7 +851,7 @@ void CL_Smoke (vec3_t org, vec3_t dir, int count)
                        org2[1] = org[1] + 0.125f * lhrandom(-count, count);
                        org2[2] = org[2] + 0.125f * lhrandom(-count, count);
                        trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
-                       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], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
+                       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, radius, 8);
                }
        }
 }
@@ -844,6 +878,11 @@ void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
        trace_t trace;
        // bloodcount is used to accumulate counts too small to cause a blood particle
        if (!cl_particles.integer) return;
+       if (cl_particles_quake.integer)
+       {
+               CL_RunParticleEffect(org, vel, 73, count * 2);
+               return;
+       }
        if (!cl_particles_blood.integer) return;
 
        s = count + 64.0f;
@@ -857,7 +896,7 @@ void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
                org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
                org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
                trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
-               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] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
+               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);
                bloodcount -= 16 / cl_particles_quality.value;
        }
 }
@@ -887,7 +926,7 @@ void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
                vel[1] = (org[1] - center[1]) * velscale[1];
                vel[2] = (org[2] - center[2]) * velscale[2];
                bloodcount -= 16 / cl_particles_quality.value;
-               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);
+               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);
        }
 }
 
@@ -904,7 +943,7 @@ void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int color
        while (count--)
        {
                k = particlepalette[colorbase + (rand()&3)];
-               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] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0);
+               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);
        }
 }
 
@@ -938,9 +977,9 @@ void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int color
                {
                        k = particlepalette[colorbase + (rand()&3)];
                        if (gamemode == GAME_GOODVSBAD2)
-                               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);
+                               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);
                        else
-                               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);
+                               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);
                }
                break;
        case 1:
@@ -948,9 +987,9 @@ void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int color
                {
                        k = particlepalette[colorbase + (rand()&3)];
                        if (gamemode == GAME_GOODVSBAD2)
-                               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);
+                               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);
                        else
-                               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);
+                               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);
                        if (p)
                                VectorCopy(p->vel, p->relativedirection);
                }
@@ -986,7 +1025,7 @@ void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
                VectorNormalize(v);
                VectorScale(v, 100, v);
                v[2] += sv_gravity.value * 0.15f;
-               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);
+               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);
        }
 }
 
@@ -1003,9 +1042,9 @@ void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
        while (count--)
        {
                k = particlepalette[224 + (rand()&15)];
-               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]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 1);
+               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);
                if (count & 1)
-                       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]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0);
+                       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);
        }
 }
 
@@ -1018,7 +1057,7 @@ void CL_Flames (vec3_t org, vec3_t vel, int count)
        while (count--)
        {
                k = particlepalette[224 + (rand()&15)];
-               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] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
+               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);
        }
 }
 
@@ -1037,28 +1076,50 @@ void CL_LavaSplash (vec3_t origin)
        vec3_t          dir, org;
        if (!cl_particles.integer) return;
 
-       inc = 32 / cl_particles_quality.value;
-       for (i = -128;i < 128;i += inc)
+       if (cl_particles_quake.integer)
        {
-               for (j = -128;j < 128;j += inc)
+               inc = 8 / cl_particles_quality.value;
+               for (i = -128;i < 128;i += inc)
                {
-                       dir[0] = j + lhrandom(0, 8);
-                       dir[1] = i + lhrandom(0, 8);
-                       dir[2] = 256;
-                       org[0] = origin[0] + dir[0];
-                       org[1] = origin[1] + dir[1];
-                       org[2] = origin[2] + lhrandom(0, 64);
-                       vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
-                       if (gamemode == GAME_GOODVSBAD2)
+                       for (j = -128;j < 128;j += inc)
                        {
-                               k = particlepalette[0 + (rand()&255)];
-                               l = particlepalette[0 + (rand()&255)];
-                               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);
+                               dir[0] = j + lhrandom(0, inc);
+                               dir[1] = i + lhrandom(0, inc);
+                               dir[2] = 256;
+                               org[0] = origin[0] + dir[0];
+                               org[1] = origin[1] + dir[1];
+                               org[2] = origin[2] + lhrandom(0, 64);
+                               vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
+                               k = l = particlepalette[224 + (rand()&7)];
+                               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);
                        }
-                       else
+               }
+       }
+       else
+       {
+               inc = 32 / cl_particles_quality.value;
+               for (i = -128;i < 128;i += inc)
+               {
+                       for (j = -128;j < 128;j += inc)
                        {
-                               k = l = particlepalette[224 + (rand()&7)];
-                               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);
+                               dir[0] = j + lhrandom(0, inc);
+                               dir[1] = i + lhrandom(0, inc);
+                               dir[2] = 256;
+                               org[0] = origin[0] + dir[0];
+                               org[1] = origin[1] + dir[1];
+                               org[2] = origin[2] + lhrandom(0, 64);
+                               vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
+                               if (gamemode == GAME_GOODVSBAD2)
+                               {
+                                       k = particlepalette[0 + (rand()&255)];
+                                       l = particlepalette[0 + (rand()&255)];
+                                       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);
+                               }
+                               else
+                               {
+                                       k = l = particlepalette[224 + (rand()&7)];
+                                       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);
+                               }
                        }
                }
        }
@@ -1075,11 +1136,33 @@ void CL_TeleportSplash (vec3_t org)
        float i, j, k, inc;
        if (!cl_particles.integer) return;
 
-       inc = 8 / cl_particles_quality.value;
-       for (i = -16;i < 16;i += inc)
-               for (j = -16;j < 16;j += inc)
-                       for (k = -24;k < 32;k += inc)
-                               particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 1);
+       if (cl_particles_quake.integer)
+       {
+               inc = 4 / cl_particles_quality.value;
+               for (i = -16;i < 16;i += inc)
+               {
+                       for (j = -16;j < 16;j += inc)
+                       {
+                               for (k = -24;k < 32;k += inc)
+                               {
+                                       vec3_t dir;
+                                       float vel;
+                                       VectorSet(dir, i*8, j*8, k*8);
+                                       VectorNormalize(dir);
+                                       vel = lhrandom(50, 113);
+                                       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);
+                               }
+                       }
+               }
+       }
+       else
+       {
+               inc = 8 / cl_particles_quality.value;
+               for (i = -16;i < 16;i += inc)
+                       for (j = -16;j < 16;j += inc)
+                               for (k = -24;k < 32;k += inc)
+                                       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);
+       }
 }
 
 #ifdef WORKINGLQUAKE
@@ -1090,7 +1173,7 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *en
 {
        vec3_t vec, dir, vel, pos;
        float len, dec, speed, qd;
-       int smoke, blood, bubbles;
+       int smoke, blood, bubbles, r;
 #ifdef WORKINGLQUAKE
        int contents;
 #endif
@@ -1144,75 +1227,160 @@ void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *en
                switch (type)
                {
                        case 0: // rocket trail
-                               dec = qd*3;
-                               if (smoke)
+                               if (cl_particles_quake.integer)
                                {
-                                       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], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
-                                       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], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
+                                       dec = qd*3;
+                                       r = rand()&3;
+                                       color = particlepalette[ramp3[r]];
+                                       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);
+                               }
+                               else
+                               {
+                                       dec = qd*3;
+                                       if (smoke)
+                                       {
+                                               particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*62, qd*cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
+                                               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);
+                                       }
+                                       if (bubbles)
+                                               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);
                                }
-                               if (bubbles)
-                                       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], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
                                break;
 
                        case 1: // grenade trail
-                               // FIXME: make it gradually stop smoking
-                               dec = qd*3;
-                               if (smoke)
-                                       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], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
+                               if (cl_particles_quake.integer)
+                               {
+                                       dec = qd*3;
+                                       r = 2 + (rand()%5);
+                                       color = particlepalette[ramp3[r]];
+                                       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);
+                               }
+                               else
+                               {
+                                       dec = qd*3;
+                                       if (smoke)
+                                               particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*50, qd*cl_particles_smoke_alphafade.value*50, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
+                               }
                                break;
 
 
                        case 2: // blood
                        case 4: // slight blood
-                               dec = qd*16;
-                               if (blood)
-                                       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 + lhrandom(-64, 64), vel[1] * 0.5f + lhrandom(-64, 64), vel[2] * 0.5f + lhrandom(-64, 64), 1);
+                               if (cl_particles_quake.integer)
+                               {
+                                       if (type == 2)
+                                       {
+                                               dec = qd*3;
+                                               color = particlepalette[67 + (rand()&3)];
+                                               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);
+                                       }
+                                       else
+                                       {
+                                               dec = qd*6;
+                                               color = particlepalette[67 + (rand()&3)];
+                                               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);
+                                       }
+                               }
+                               else
+                               {
+                                       dec = qd*16;
+                                       if (blood)
+                                               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);
+                               }
                                break;
 
                        case 3: // green tracer
-                               dec = qd*6;
-                               if (smoke)
+                               if (cl_particles_quake.integer)
                                {
-                                       if (gamemode == GAME_GOODVSBAD2)
-                                               particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
-                                       else
-                                               particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
+                                       dec = qd*6;
+                                       color = particlepalette[52 + (rand()&7)];
+                                       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);
+                                       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);
+                               }
+                               else
+                               {
+                                       dec = qd*16;
+                                       if (smoke)
+                                       {
+                                               if (gamemode == GAME_GOODVSBAD2)
+                                               {
+                                                       dec = qd*6;
+                                                       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);
+                                               }
+                                               else
+                                               {
+                                                       dec = qd*3;
+                                                       color = particlepalette[20 + (rand()&7)];
+                                                       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);
+                                               }
+                                       }
                                }
                                break;
 
                        case 5: // flame tracer
-                               dec = qd*6;
-                               if (smoke)
-                                       particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
+                               if (cl_particles_quake.integer)
+                               {
+                                       dec = qd*6;
+                                       color = particlepalette[230 + (rand()&7)];
+                                       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);
+                                       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);
+                               }
+                               else
+                               {
+                                       dec = qd*3;
+                                       if (smoke)
+                                       {
+                                               color = particlepalette[226 + (rand()&7)];
+                                               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);
+                                       }
+                               }
                                break;
 
                        case 6: // voor trail
-                               dec = qd*6;
-                               if (smoke)
+                               if (cl_particles_quake.integer)
                                {
-                                       if (gamemode == GAME_GOODVSBAD2)
-                                               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], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
-                                       else if (gamemode == GAME_PRYDON)
-                                               particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
-                                       else
-                                               particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
+                                       dec = qd*3;
+                                       color = particlepalette[152 + (rand()&3)];
+                                       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);
+                               }
+                               else
+                               {
+                                       dec = qd*16;
+                                       if (smoke)
+                                       {
+                                               if (gamemode == GAME_GOODVSBAD2)
+                                               {
+                                                       dec = qd*6;
+                                                       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);
+                                               }
+                                               else if (gamemode == GAME_PRYDON)
+                                               {
+                                                       dec = qd*6;
+                                                       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);
+                                               }
+                                               else
+                                               {
+                                                       dec = qd*3;
+                                                       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);
+                                               }
+                                       }
                                }
                                break;
 #ifndef WORKINGLQUAKE
                        case 7: // Nehahra smoke tracer
                                dec = qd*7;
                                if (smoke)
-                                       particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
+                                       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);
                                break;
                        case 8: // Nexuiz plasma trail
                                dec = qd*4;
                                if (smoke)
-                                       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);
+                                       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);
                                break;
                        case 9: // glow trail
                                dec = qd*3;
                                if (smoke)
-                                       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);
+                                       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);
                                break;
 #endif
                        default:
@@ -1235,7 +1403,7 @@ void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float
        cg = green * 255;
        cb = blue * 255;
        tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
-       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);
+       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);
 }
 
 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
@@ -1246,7 +1414,7 @@ void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
        // smoke puff
        if (cl_particles_smoke.integer)
                for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
-                       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] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
+                       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);
 }
 
 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
@@ -1261,12 +1429,12 @@ void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
        // smoke puff
        if (cl_particles_smoke.integer)
                for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
-                       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] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
+                       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);
 
        // sparks
        if (cl_particles_sparks.integer)
                for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
-                       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], lhrandom(-count, count) * 3.0f + dir[0], lhrandom(-count, count) * 3.0f + dir[1], lhrandom(-count, count) * 3.0f + dir[2], 0);
+                       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);
 }
 
 /*
@@ -1328,6 +1496,7 @@ void CL_MoveParticles (void)
                                        trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
                                        if (trace.fraction < 1)
                                        {
+                                               int count;
                                                // convert from a raindrop particle to a rainsplash decal
                                                VectorCopy(trace.endpos, p->org);
                                                VectorCopy(trace.plane.normal, p->vel);
@@ -1340,6 +1509,9 @@ void CL_MoveParticles (void)
                                                p->friction = 0;
                                                p->gravity = 0;
                                                p->size = 8.0;
+                                               count = rand() & 3;
+                                               while(count--)
+                                                       particle(particletype + pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, 32 + p->vel[2]*16, 0, 0, 32);
                                        }
                                }
                                else if (p->type == particletype + pt_blood)
@@ -1537,7 +1709,7 @@ static rtexture_t *particlefonttexture;
 #endif
 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
 
-static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
+static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
 
 #define PARTICLETEXTURESIZE 64
 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
@@ -1896,9 +2068,9 @@ float particle_vertex3f[12], particle_texcoord2f[8];
 void R_DrawParticle(particle_t *p)
 {
 #else
-void R_DrawParticleCallback(const void *calldata1, int calldata2)
+void R_DrawParticle_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
 {
-       const particle_t *p = (particle_t *)calldata1;
+       const particle_t *p = particles + surfacenumber;
        rmeshstate_t m;
 #endif
        pblend_t blendmode;
@@ -2089,9 +2261,9 @@ void R_DrawParticles (void)
                        if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
                        {
                                if (p->type == particletype + pt_decal)
-                                       R_DrawParticleCallback(p, 0);
+                                       R_DrawParticle_TransparentCallback(0, i, 0);
                                else
-                                       R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);
+                                       R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
                        }
                }
        }