]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_particles.c
added .float disableclientprediction field for qc to use to disable prediction intent...
[xonotic/darkplaces.git] / cl_particles.c
index 30278b16eb8cd858609a6e354923bfcf11e92924..39c714d16be0ef8eb2e986062f6ede4f15ea12c9 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "cl_collision.h"
 #include "image.h"
+#include "r_shadow.h"
 
 // must match ptype_t values
 particletype_t particletype[pt_total] =
@@ -178,6 +179,8 @@ cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1
 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_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
+cvar_t cl_particles_snow = {CVAR_SAVE, "cl_particles_snow", "1", "enables snow effects"};
 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"};
@@ -355,7 +358,6 @@ static const char *standardeffectnames[EFFECT_TOTAL] =
        "TE_SUPERSPIKEQUAD",
        "TE_WIZSPIKE",
        "TE_KNIGHTSPIKE",
-       "TE_VORESPIKE",
        "TE_EXPLOSION",
        "TE_EXPLOSIONQUAD",
        "TE_TAREXPLOSION",
@@ -424,6 +426,8 @@ void CL_Particles_Init (void)
        Cvar_RegisterVariable (&cl_particles_explosions_sparks);
        Cvar_RegisterVariable (&cl_particles_explosions_shell);
        Cvar_RegisterVariable (&cl_particles_bulletimpacts);
+       Cvar_RegisterVariable (&cl_particles_rain);
+       Cvar_RegisterVariable (&cl_particles_snow);
        Cvar_RegisterVariable (&cl_particles_smoke);
        Cvar_RegisterVariable (&cl_particles_smoke_alpha);
        Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
@@ -521,7 +525,7 @@ void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size,
        {
                VectorRandom(org2);
                VectorMA(org, maxdist, org2, org2);
-               trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, false);
+               trace = CL_Move(org, vec3_origin, vec3_origin, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
                // take the closest trace result that doesn't end up hitting a NOMARKS
                // surface (sky for example)
                if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
@@ -537,7 +541,7 @@ void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size,
 }
 
 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount, float smokecount);
-void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor)
+void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
 {
        vec3_t center;
        matrix4x4_t tempmatrix;
@@ -603,7 +607,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                // bullet hole
                if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
        {
@@ -636,7 +640,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                // bullet hole
                if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_BLOOD)
        {
@@ -659,7 +663,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                // plasma scorch mark
                if (cl_stainmaps.integer) R_Stain(center, 48, 96, 96, 96, 32, 128, 128, 128, 32);
                CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_GUNSHOT)
        {
@@ -686,17 +690,17 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                // bullet hole
                if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               CL_AllocDlight(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_EXPLOSION)
        {
                CL_ParticleExplosion(center);
-               CL_AllocDlight(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
        {
                CL_ParticleExplosion(center);
-               CL_AllocDlight(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
        {
@@ -713,10 +717,10 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                }
                else
                        CL_ParticleExplosion(center);
-               CL_AllocDlight(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_SMALLFLASH)
-               CL_AllocDlight(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        else if (effectnameindex == EFFECT_TE_FLAMEJET)
        {
                count *= cl_particles_quality.value;
@@ -749,7 +753,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                float i, j, k, inc, vel;
                vec3_t dir;
 
-               inc = 4 / cl_particles_quality.value;
+               inc = 8 / cl_particles_quality.value;
                for (i = -16;i < 16;i += inc)
                {
                        for (j = -16;j < 16;j += inc)
@@ -763,7 +767,8 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                                }
                        }
                }
-               CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 1.0f, 1.0f, 600, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               particle(particletype + pt_static, particlepalette[14], particlepalette[14], tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0);
+               CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_TEI_G3)
                particle(particletype + pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0);
@@ -779,7 +784,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
        else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
        {
                CL_ParticleExplosion(center);
-               CL_AllocDlight(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
        {
@@ -793,21 +798,21 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                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, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465);
-               CL_AllocDlight(NULL, &tempmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_EF_FLAME)
        {
                count *= 300 * cl_particles_quality.value;
                while (count-- > 0)
                        particle(particletype + pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (effectnameindex == EFFECT_EF_STARDUST)
        {
                count *= 200 * cl_particles_quality.value;
                while (count-- > 0)
                        particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128);
-               CL_AllocDlight(NULL, &tempmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+               CL_AllocLightFlash(NULL, &tempmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
        }
        else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
        {
@@ -815,30 +820,51 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                float len, dec, qd;
                int smoke, blood, bubbles, r, color;
 
-               if (effectnameindex == EFFECT_TR_ROCKET)
-                       CL_AllocDlight(&ent->render, &ent->render.matrix, 200, 3.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-               else if (effectnameindex == EFFECT_TR_VORESPIKE)
+               if (spawndlight && r_refdef.numlights < MAX_DLIGHTS)
                {
-                       if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
-                               CL_AllocDlight(&ent->render, &ent->render.matrix, 100, 0.3f, 0.6f, 1.2f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
-                       else
-                               CL_AllocDlight(&ent->render, &ent->render.matrix, 200, 1.2f, 0.5f, 1.0f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       vec4_t light;
+                       Vector4Set(light, 0, 0, 0, 0);
+
+                       if (effectnameindex == EFFECT_TR_ROCKET)
+                               Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
+                       else if (effectnameindex == EFFECT_TR_VORESPIKE)
+                       {
+                               if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
+                                       Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
+                               else
+                                       Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
+                       }
+                       else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
+                               Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
+
+                       if (light[3])
+                       {
+                               matrix4x4_t tempmatrix;
+                               Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
+                               R_RTLight_Update(&r_refdef.lights[r_refdef.numlights++], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                       }
                }
-               else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
-                       CL_AllocDlight(&ent->render, &ent->render.matrix, 200, 0.75f, 1.5f, 3.0f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+
+               if (!spawnparticles)
+                       return;
 
                if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
                        return;
 
                VectorSubtract(originmaxs, originmins, dir);
                len = VectorNormalizeLength(dir);
-               dec = -ent->persistent.trail_time;
-               ent->persistent.trail_time += len;
-               if (ent->persistent.trail_time < 0.01f)
-                       return;
+               if (ent)
+               {
+                       dec = -ent->persistent.trail_time;
+                       ent->persistent.trail_time += len;
+                       if (ent->persistent.trail_time < 0.01f)
+                               return;
 
-               // if we skip out, leave it reset
-               ent->persistent.trail_time = 0.0f;
+                       // if we skip out, leave it reset
+                       ent->persistent.trail_time = 0.0f;
+               }
+               else
+                       dec = 0;
 
                // advance into this frame to reach the first puff location
                VectorMA(originmins, dec, dir, pos);
@@ -991,13 +1017,18 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o
                        len -= dec;
                        VectorMA (pos, dec, dir, pos);
                }
-               ent->persistent.trail_time = len;
+               if (ent)
+                       ent->persistent.trail_time = len;
        }
        else if (developer.integer >= 1)
                Con_Printf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
 }
 
-void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor)
+// this is also called on point effects with spawndlight = true and
+// spawnparticles = true
+// it is called CL_ParticleTrail because most code does not want to supply
+// these parameters, only trail handling does
+void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
 {
        vec3_t center;
        qboolean found = false;
@@ -1039,16 +1070,31 @@ void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmin
                                        continue;
 
                                // spawn a dlight if requested
-                               if (info->lightradiusstart > 0)
+                               if (info->lightradiusstart > 0 && spawndlight)
                                {
                                        matrix4x4_t tempmatrix;
                                        if (info->trailspacing > 0)
                                                Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
                                        else
                                                Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
-                                       CL_AllocDlight(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0], info->lightcolor[1], info->lightcolor[2], info->lightradiusfade, info->lighttime, info->lightcubemapnum, -1, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                                       if (info->lighttime > 0 && info->lightradiusfade > 0)
+                                       {
+                                               // light flash (explosion, etc)
+                                               // called when effect starts
+                                               CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0], info->lightcolor[1], info->lightcolor[2], info->lightradiusfade, info->lighttime, info->lightcubemapnum, -1, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                                       }
+                                       else
+                                       {
+                                               // glowing entity
+                                               // called by CL_LinkNetworkEntity
+                                               Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
+                                               R_RTLight_Update(&r_refdef.lights[r_refdef.numlights++], false, &tempmatrix, info->lightcolor, -1, info->lightcubemapnum > 0 ? va("cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                                       }
                                }
 
+                               if (!spawnparticles)
+                                       continue;
+
                                // spawn particles
                                tex = info->tex[0];
                                if (info->tex[1] > info->tex[0])
@@ -1070,6 +1116,8 @@ void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmin
                                        case pt_spark: if (!cl_particles_sparks.integer) continue;break;
                                        case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
                                        case pt_blood: if (!cl_particles_blood.integer) continue;break;
+                                       case pt_rain: if (!cl_particles_rain.integer) continue;break;
+                                       case pt_snow: if (!cl_particles_snow.integer) continue;break;
                                        default: break;
                                        }
                                        VectorCopy(originmins, trailpos);
@@ -1083,7 +1131,8 @@ void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmin
                                                info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
                                                trailstep = 0;
                                        }
-                                       for (;info->particleaccumulator > 0;info->particleaccumulator--)
+                                       info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
+                                       for (;info->particleaccumulator >= 1;info->particleaccumulator--)
                                        {
                                                if (info->tex[1] > info->tex[0])
                                                {
@@ -1106,7 +1155,12 @@ void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmin
                }
        }
        if (!found)
-               CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor);
+               CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
+}
+
+void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor)
+{
+       CL_ParticleTrail(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true);
 }
 
 /*
@@ -1282,7 +1336,7 @@ void CL_ParticleExplosion (const vec3_t org)
                                                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);
+                                               trace = CL_Move(org, vec3_origin, vec3_origin, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
                                                if (trace.fraction >= 0.1)
                                                        break;
                                        }
@@ -1373,6 +1427,7 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in
        switch(type)
        {
        case 0:
+               if (!cl_particles_rain.integer) break;
                count *= 4; // ick, this should be in the mod or maps?
 
                while(count--)
@@ -1385,6 +1440,7 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in
                }
                break;
        case 1:
+               if (!cl_particles_snow.integer) break;
                while(count--)
                {
                        k = particlepalette[colorbase + (rand()&3)];
@@ -1422,7 +1478,7 @@ void CL_MoveParticles (void)
                return;
        }
 
-       frametime = cl.time - cl.oldtime;
+       frametime = bound(0, cl.time - cl.oldtime, 0.1);
        gravity = frametime * sv_gravity.value;
        dvel = 1+4*frametime;
        decalfade = frametime * 255 / cl_decals_fadetime.value;
@@ -1492,7 +1548,7 @@ void CL_MoveParticles (void)
                        VectorCopy(p->org, org);
                        if (p->bounce)
                        {
-                               trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | (p->type == particletype + pt_rain ? SUPERCONTENTS_LIQUIDSMASK : 0), false);
+                               trace = CL_Move(oldorg, vec3_origin, vec3_origin, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | (p->type == particletype + pt_rain ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false);
                                // if the trace started in or hit something of SUPERCONTENTS_NODROP
                                // or if the trace hit something flagged as NOIMPACT
                                // then remove the particle
@@ -2034,6 +2090,7 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
        R_Mesh_ColorPointer(particle_color4f);
        GL_DepthMask(false);
        GL_DepthTest(true);
+       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
 
        // first generate all the vertices at once
        for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
@@ -2046,9 +2103,9 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
 
                blendmode = p->type->blendmode;
 
-               cr = p->color[0] * (1.0f / 255.0f);
-               cg = p->color[1] * (1.0f / 255.0f);
-               cb = p->color[2] * (1.0f / 255.0f);
+               cr = p->color[0] * (1.0f / 255.0f) * r_view.colorscale;
+               cg = p->color[1] * (1.0f / 255.0f) * r_view.colorscale;
+               cb = p->color[2] * (1.0f / 255.0f) * r_view.colorscale;
                ca = p->alpha * (1.0f / 255.0f);
                if (blendmode == PBLEND_MOD)
                {
@@ -2078,9 +2135,9 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh
                        cb = cb * ifog;
                        if (blendmode == PBLEND_ALPHA)
                        {
-                               cr += r_refdef.fogcolor[0] * fog;
-                               cg += r_refdef.fogcolor[1] * fog;
-                               cb += r_refdef.fogcolor[2] * fog;
+                               cr += r_refdef.fogcolor[0] * fog * r_view.colorscale;
+                               cg += r_refdef.fogcolor[1] * fog * r_view.colorscale;
+                               cb += r_refdef.fogcolor[2] * fog * r_view.colorscale;
                        }
                }
                c4f[0] = c4f[4] = c4f[8] = c4f[12] = cr;