]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_particles.c
Add GL_ExtensionSupported in vid_null.c because vid_shared.c relies on
[xonotic/darkplaces.git] / cl_particles.c
index e2e9ca08d1235a42b2e45bf07fbd5d544b1b9b3a..6d99fe0156a5ec8ab63d4d2d1c55d7e96d766b65 100644 (file)
@@ -44,6 +44,7 @@ particletype_t particletype[pt_total] =
 
 #define PARTICLEEFFECT_UNDERWATER 1
 #define PARTICLEEFFECT_NOTUNDERWATER 2
+#define PARTICLEEFFECT_DEFINED 2147483648U
 
 typedef struct particleeffectinfo_s
 {
@@ -296,6 +297,7 @@ cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sp
 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
 cvar_t cl_particles_visculling = {CVAR_SAVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
 cvar_t cl_particles_collisions = {CVAR_SAVE, "cl_particles_collisions", "1", "allow costly collision detection on particles (sparks that bounce, particles not going through walls, blood hitting surfaces, etc)"};
+cvar_t cl_particles_forcetraileffects = {0, "cl_particles_forcetraileffects", "0", "force trails to be displayed even if a non-trail draw primitive was used (debug/compat feature)"};
 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
 cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
@@ -313,6 +315,7 @@ static void CL_Particles_ParseEffectInfo(const char *textstart, const char *text
 {
        int arrayindex;
        int argc;
+       int i;
        int linenumber;
        particleeffectinfo_t *info = NULL;
        const char *text = textstart;
@@ -370,17 +373,29 @@ static void CL_Particles_ParseEffectInfo(const char *textstart, const char *text
                                Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
                                break;
                        }
+                       for(i = 0; i < numparticleeffectinfo; ++i)
+                       {
+                               info = particleeffectinfo + i;
+                               if(!(info->flags & PARTICLEEFFECT_DEFINED))
+                                       if(info->effectnameindex == effectnameindex)
+                                               break;
+                       }
+                       if(i < numparticleeffectinfo)
+                               continue;
                        info = particleeffectinfo + numparticleeffectinfo++;
                        // copy entire info from baseline, then fix up the nameindex
                        *info = baselineparticleeffectinfo;
                        info->effectnameindex = effectnameindex;
+                       continue;
                }
                else if (info == NULL)
                {
                        Con_Printf("%s:%i: command %s encountered before effect\n", filename, linenumber, argv[0]);
                        break;
                }
-               else if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
+
+               info->flags |= PARTICLEEFFECT_DEFINED;
+               if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
                else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
                else if (!strcmp(argv[0], "type"))
                {
@@ -595,6 +610,7 @@ void CL_Particles_Init (void)
        Cvar_RegisterVariable (&cl_particles_bubbles);
        Cvar_RegisterVariable (&cl_particles_visculling);
        Cvar_RegisterVariable (&cl_particles_collisions);
+       Cvar_RegisterVariable (&cl_particles_forcetraileffects);
        Cvar_RegisterVariable (&cl_decals);
        Cvar_RegisterVariable (&cl_decals_visculling);
        Cvar_RegisterVariable (&cl_decals_time);
@@ -749,14 +765,13 @@ particle_t *CL_NewParticle(const vec3_t sortorigin, unsigned short ptypeindex, i
        {
                int i;
                particle_t *part2;
-               float lifetime = part->die - cl.time;
                vec3_t endvec;
                trace_t trace;
                // turn raindrop into simple spark and create delayedspawn splash effect
                part->typeindex = pt_spark;
                part->bounce = 0;
                VectorMA(part->org, lifetime, part->vel, endvec);
-               trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false, false);
+               trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, 0, 0, collision_extendmovelength.value, true, false, NULL, false, false);
                part->die = cl.time + lifetime * trace.fraction;
                part2 = CL_NewParticle(endvec, pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0, pqualityreduction, 0, 1, PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, -1, -1, -1, 1, 1, 0, 0, NULL);
                if (part2)
@@ -897,7 +912,7 @@ void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size,
        {
                VectorRandom(org2);
                VectorMA(org, maxdist, org2, org2);
-               trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false, true);
+               trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, 0, collision_extendmovelength.value, true, false, &hitent, false, true);
                // 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))
@@ -914,23 +929,24 @@ 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);
 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
-static 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)
+static void CL_NewParticlesFromEffectinfo(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, float tintmins[4], float tintmaxs[4], float fade, qboolean wanttrail);
+static 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, qboolean wanttrail)
 {
        vec3_t center;
-       matrix4x4_t tempmatrix;
+       matrix4x4_t lightmatrix;
        particle_t *part;
 
        VectorLerp(originmins, 0.5, originmaxs, center);
-       Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
+       Matrix4x4_CreateTranslate(&lightmatrix, center[0], center[1], center[2]);
        if (effectnameindex == EFFECT_SVC_PARTICLE)
        {
                if (cl_particles.integer)
                {
                        // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
                        if (count == 1024)
-                               CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                               CL_NewParticlesFromEffectinfo(EFFECT_TE_EXPLOSION, 1, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
-                               CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                               CL_NewParticlesFromEffectinfo(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        else
                        {
                                count *= cl_particles_quality.value;
@@ -943,9 +959,9 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                }
        }
        else if (effectnameindex == EFFECT_TE_WIZSPIKE)
-               CL_ParticleEffect(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20);
+               CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
        else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
-               CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226);
+               CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
        else if (effectnameindex == EFFECT_TE_SPIKE)
        {
                if (cl_particles_bulletimpacts.integer)
@@ -953,7 +969,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                        if (cl_particles_quake.integer)
                        {
                                if (cl_particles_smoke.integer)
-                                       CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                                       CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        }
                        else
                        {
@@ -973,7 +989,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                        if (cl_particles_quake.integer)
                        {
                                if (cl_particles_smoke.integer)
-                                       CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                                       CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        }
                        else
                        {
@@ -985,7 +1001,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                // bullet hole
                R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
@@ -994,7 +1010,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                        if (cl_particles_quake.integer)
                        {
                                if (cl_particles_smoke.integer)
-                                       CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                                       CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        }
                        else
                        {
@@ -1014,7 +1030,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                        if (cl_particles_quake.integer)
                        {
                                if (cl_particles_smoke.integer)
-                                       CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                                       CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        }
                        else
                        {
@@ -1026,14 +1042,14 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                // bullet hole
                R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
                if (!cl_particles_blood.integer)
                        return;
                if (cl_particles_quake.integer)
-                       CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
+                       CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                else
                {
                        static double bloodaccumulator = 0;
@@ -1058,14 +1074,14 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                // plasma scorch mark
                R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
                CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               CL_AllocLightFlash(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, &lightmatrix, 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)
        {
                if (cl_particles_bulletimpacts.integer)
                {
                        if (cl_particles_quake.integer)
-                               CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                               CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        else
                        {
                                CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
@@ -1082,7 +1098,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                if (cl_particles_bulletimpacts.integer)
                {
                        if (cl_particles_quake.integer)
-                               CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+                               CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
                        else
                        {
                                CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
@@ -1093,17 +1109,17 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                // bullet hole
                R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
                CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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_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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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_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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
@@ -1120,10 +1136,10 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                }
                else
                        CL_ParticleExplosion(center);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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_AllocLightFlash(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, &lightmatrix, 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;
@@ -1178,7 +1194,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                }
                if (!cl_particles_quake.integer)
                        CL_NewParticle(center, pt_static, 0xffffff, 0xffffff, tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
                CL_NewParticle(center, 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, false, 0, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1, 0, 0, NULL);
@@ -1194,7 +1210,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
        else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
        {
                CL_ParticleExplosion(center);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
@@ -1207,7 +1223,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                if (cl_particles_sparks.integer)
                        for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
                                CL_NewParticle(center, 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, true, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1, 0, 0, NULL);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
@@ -1216,7 +1232,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                count *= 300 * cl_particles_quality.value;
                while (count-- > 0)
                        CL_NewParticle(center, 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, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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)
        {
@@ -1225,13 +1241,13 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                count *= 200 * cl_particles_quality.value;
                while (count-- > 0)
                        CL_NewParticle(center, 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, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1, 0, 0, NULL);
-               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);
+               CL_AllocLightFlash(NULL, &lightmatrix, 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))
        {
                vec3_t dir, pos;
                float len, dec, qd;
-               int smoke, blood, bubbles, r, color;
+               int smoke, blood, bubbles, r, color, spawnedcount;
 
                if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
                {
@@ -1252,9 +1268,9 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
 
                        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.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+                               matrix4x4_t traillightmatrix;
+                               Matrix4x4_CreateFromQuakeEntity(&traillightmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
+                               R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &traillightmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
                                r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
                        }
                }
@@ -1267,6 +1283,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
 
                VectorSubtract(originmaxs, originmins, dir);
                len = VectorNormalizeLength(dir);
+
                if (ent)
                {
                        dec = -ent->persistent.trail_time;
@@ -1288,8 +1305,9 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
                blood = cl_particles.integer && cl_particles_blood.integer;
                bubbles = cl_particles.integer && cl_particles_bubbles.integer && !cl_particles_quake.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
                qd = 1.0f / cl_particles_quality.value;
+               spawnedcount = 0;
 
-               while (len >= 0)
+               while (len >= 0 && ++spawnedcount <= 16384)
                {
                        dec = 3;
                        if (blood)
@@ -1440,9 +1458,7 @@ static void CL_ParticleEffect_Fallback(int effectnameindex, float count, const v
 
 // 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
-static void CL_NewParticlesFromEffectinfo(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, float tintmins[4], float tintmaxs[4], float fade, qboolean istrail)
+static void CL_NewParticlesFromEffectinfo(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, float tintmins[4], float tintmaxs[4], float fade, qboolean wanttrail)
 {
        qboolean found = false;
        char vabuf[1024];
@@ -1489,23 +1505,25 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                }
                for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
                {
-                       if (info->effectnameindex == effectnameindex)
+                       if ((info->effectnameindex == effectnameindex) && (info->flags & PARTICLEEFFECT_DEFINED))
                        {
+                               qboolean definedastrail = info->trailspacing > 0;
+
+                               qboolean drawastrail = wanttrail;
+                               if (cl_particles_forcetraileffects.integer)
+                                       drawastrail = drawastrail || definedastrail;
+
                                found = true;
                                if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
                                        continue;
                                if ((info->flags & PARTICLEEFFECT_NOTUNDERWATER) && underwater)
                                        continue;
 
-                               // if trailspacing is set, only ever use this effect as trail
-                               if (info->trailspacing > 0 && !istrail)
-                                       continue;
-
                                // spawn a dlight if requested
                                if (info->lightradiusstart > 0 && spawndlight)
                                {
                                        matrix4x4_t tempmatrix;
-                                       if (istrail)
+                                       if (drawastrail)
                                                Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
                                        else
                                                Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
@@ -1556,6 +1574,9 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                                }
                                else if (info->orientation == PARTICLE_HBEAM)
                                {
+                                       if (!drawastrail)
+                                               continue;
+
                                        AnglesFromVectors(angles, traildir, NULL, false);
                                        AngleVectors(angles, forward, right, up);
                                        VectorMAMAM(info->relativeoriginoffset[0], forward, info->relativeoriginoffset[1], right, info->relativeoriginoffset[2], up, trailpos);
@@ -1564,6 +1585,7 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                                }
                                else
                                {
+                                       float cnt;
                                        if (!cl_particles.integer)
                                                continue;
                                        switch (info->particletype)
@@ -1576,35 +1598,47 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                                        case pt_snow: if (!cl_particles_snow.integer) continue;break;
                                        default: break;
                                        }
-                                       VectorCopy(originmins, trailpos);
-                                       if (istrail)
-                                       {
-                                               float cnt = info->countabsolute;
-                                               cnt += (pcount * info->countmultiplier) * cl_particles_quality.value;
-                                               if (info->trailspacing > 0)
-                                                       cnt += (traillen / info->trailspacing) * cl_particles_quality.value;
-                                               cnt *= fade;
-                                               info->particleaccumulator += cnt;
-                                               trailstep = traillen / cnt;
-                                               immediatebloodstain = false;
 
-                                               AnglesFromVectors(angles, traildir, NULL, false);
-                                       }
+                                       cnt = info->countabsolute;
+                                       cnt += (pcount * info->countmultiplier) * cl_particles_quality.value;
+                                       // if drawastrail is not set, we will
+                                       // use the regular cnt-based random
+                                       // particle spawning at the center; so
+                                       // do NOT apply trailspacing then!
+                                       if (drawastrail && definedastrail)
+                                               cnt += (traillen / info->trailspacing) * cl_particles_quality.value;
+                                       cnt *= fade;
+                                       if (cnt == 0)
+                                               continue;  // nothing to draw
+                                       info->particleaccumulator += cnt;
+
+                                       if (drawastrail || definedastrail)
+                                               immediatebloodstain = false;
                                        else
-                                       {
-                                               float cnt = info->countabsolute;
-                                               cnt += (pcount * info->countmultiplier) * cl_particles_quality.value;
-                                               cnt *= fade;
-                                               info->particleaccumulator += cnt;
-                                               trailstep = 0;
                                                immediatebloodstain =
                                                        ((cl_decals_newsystem_immediatebloodstain.integer >= 1) && (info->particletype == pt_blood))
                                                        ||
                                                        ((cl_decals_newsystem_immediatebloodstain.integer >= 2) && staintex);
 
+                                       if (drawastrail)
+                                       {
+                                               VectorCopy(originmins, trailpos);
+                                               trailstep = traillen / cnt;
+                                       }
+                                       else
+                                       {
+                                               VectorCopy(center, trailpos);
+                                               trailstep = 0;
+                                       }
+
+                                       if (trailstep == 0)
+                                       {
                                                VectorMAM(0.5f, velocitymins, 0.5f, velocitymaxs, velocity);
                                                AnglesFromVectors(angles, velocity, NULL, false);
                                        }
+                                       else
+                                               AnglesFromVectors(angles, traildir, NULL, false);
+
                                        AngleVectors(angles, forward, right, up);
                                        VectorMAMAMAM(1.0f, trailpos, info->relativeoriginoffset[0], forward, info->relativeoriginoffset[1], right, info->relativeoriginoffset[2], up, trailpos);
                                        VectorMAMAM(info->relativevelocityoffset[0], forward, info->relativevelocityoffset[1], right, info->relativevelocityoffset[2], up, velocity);
@@ -1616,7 +1650,7 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                                                        tex = (int)lhrandom(info->tex[0], info->tex[1]);
                                                        tex = min(tex, info->tex[1] - 1);
                                                }
-                                               if (!trailstep)
+                                               if (!(drawastrail || definedastrail))
                                                {
                                                        trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
                                                        trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
@@ -1642,7 +1676,7 @@ static void CL_NewParticlesFromEffectinfo(int effectnameindex, float pcount, con
                }
        }
        if (!found)
-               CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
+               CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles, wanttrail);
 }
 
 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, float tintmins[4], float tintmaxs[4], float fade)
@@ -1655,6 +1689,7 @@ void CL_ParticleBox(int effectnameindex, float pcount, const vec3_t originmins,
        CL_NewParticlesFromEffectinfo(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles, tintmins, tintmaxs, fade, false);
 }
 
+// note: this one ONLY does boxes!
 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_ParticleBox(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true, NULL, NULL, 1);
@@ -1751,6 +1786,11 @@ void CL_ReadPointFile_f (void)
        VectorCopy(leakorg, vecorg);
        Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, leakorg[0], leakorg[1], leakorg[2]);
 
+       if (c == 0)
+       {
+               return;
+       }
+
        CL_NewParticle(vecorg, pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 0, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1, 0, 0, NULL);
        CL_NewParticle(vecorg, pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1, 0, 0, NULL);
        CL_NewParticle(vecorg, pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1, 0, 0, NULL);
@@ -1836,7 +1876,7 @@ void CL_ParticleExplosion (const vec3_t org)
                                        {
                                                VectorRandom(v2);
                                                VectorMA(org, 128, v2, v);
-                                               trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false, false);
+                                               trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, 0, 0, collision_extendmovelength.value, true, false, NULL, false, false);
                                        }
                                        while (k < 16 && trace.fraction < 0.1f);
                                        VectorSubtract(trace.endpos, org, v2);
@@ -2140,7 +2180,7 @@ static void R_InitParticleTexture (void)
        // we invert it again during the blendfunc to make it work...
 
 #ifndef DUMPPARTICLEFONT
-       decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, false);
+       decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, false, false);
        if (decalskinframe)
        {
                particlefonttexture = decalskinframe->base;
@@ -2285,7 +2325,7 @@ static void R_InitParticleTexture (void)
                Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
 #endif
 
-               decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE, false);
+               decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE, 0, 0, 0, false);
                particlefonttexture = decalskinframe->base;
 
                Mem_Free(particletexturedata);
@@ -2387,12 +2427,7 @@ static void R_InitParticleTexture (void)
                                Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES);
                                continue;
                        }
-                       sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true); // note: this loads as sRGB if sRGB is active!
-                       if(!sf)
-                       {
-                               // R_SkinFrame_LoadExternal already complained
-                               continue;
-                       }
+                       sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true, true); // note: this loads as sRGB if sRGB is active!
                        particletexture[i].texture = sf->base;
                        particletexture[i].s1 = s1;
                        particletexture[i].t1 = t1;
@@ -2460,7 +2495,7 @@ static void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rt
        vec_t right[3], up[3], size, ca;
        float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value;
 
-       RSurf_ActiveWorldEntity();
+       RSurf_ActiveModelEntity(r_refdef.scene.worldentity, false, false, false);
 
        r_refdef.stats[r_stat_drawndecals] += numsurfaces;
 //     R_Mesh_ResetTextureState();
@@ -2519,7 +2554,7 @@ static void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rt
        // now render the decals all at once
        // (this assumes they all use one particle font texture!)
        GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
-       R_SetupShader_Generic(particletexture[63].texture, NULL, GL_MODULATE, 1, false, false, true);
+       R_SetupShader_Generic(particletexture[63].texture, false, false, true);
        R_Mesh_PrepareVertices_Generic_Arrays(numsurfaces * 4, particle_vertex3f, particle_color4f, particle_texcoord2f);
        R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, NULL, 0, particle_elements, NULL, 0);
 }
@@ -2532,7 +2567,7 @@ void R_DrawDecals (void)
        float frametime;
        float decalfade;
        float drawdist2;
-       int killsequence = cl.decalsequence - max(0, cl_decals_max.integer);
+       unsigned int killsequence = cl.decalsequence - bound(0, (unsigned int) cl_decals_max.integer, cl.decalsequence);
 
        frametime = bound(0, cl.time - cl.decals_updatetime, 1);
        cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
@@ -2550,7 +2585,7 @@ void R_DrawDecals (void)
                if (!decal->typeindex)
                        continue;
 
-               if (killsequence - decal->decalsequence > 0)
+               if (killsequence > decal->decalsequence)
                        goto killdecal;
 
                if (cl.time > decal->time2 + cl_decals_time.value)
@@ -2577,7 +2612,7 @@ void R_DrawDecals (void)
                if (!drawdecals)
                        continue;
 
-               if (DotProduct(r_refdef.view.origin, decal->normal) > DotProduct(decal->org, decal->normal) && VectorDistance2(decal->org, r_refdef.view.origin) < drawdist2 * (decal->size * decal->size))
+               if (!r_refdef.view.useperspective || (DotProduct(r_refdef.view.origin, decal->normal) > DotProduct(decal->org, decal->normal) && VectorDistance2(decal->org, r_refdef.view.origin) < drawdist2 * (decal->size * decal->size)))
                        R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL);
                continue;
 killdecal:
@@ -2619,7 +2654,7 @@ static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const
        float minparticledist_start, minparticledist_end;
        qboolean dofade;
 
-       RSurf_ActiveWorldEntity();
+       RSurf_ActiveModelEntity(r_refdef.scene.worldentity, false, false, false);
 
        Vector4Set(colormultiplier, r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), cl_particles_alpha.value * (1.0 / 256.0f));
 
@@ -2683,10 +2718,14 @@ static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const
                        // note: lighting is not cheap!
                        if (particletype[p->typeindex].lighting)
                        {
+                               float a[3], c[3], dir[3];
                                vecorg[0] = p->org[0];
                                vecorg[1] = p->org[1];
                                vecorg[2] = p->org[2];
-                               R_LightPoint(c4f, vecorg, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+                               R_CompleteLightPoint(a, c, dir, vecorg, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT, r_refdef.scene.lightmapintensity, r_refdef.scene.ambientintensity);
+                               c4f[0] = p->color[0] * colormultiplier[0] * (a[0] + 0.25f * c[0]);
+                               c4f[1] = p->color[1] * colormultiplier[1] * (a[1] + 0.25f * c[1]);
+                               c4f[2] = p->color[2] * colormultiplier[2] * (a[2] + 0.25f * c[2]);
                        }
                        // mix in the fog color
                        if (r_refdef.fogenabled)
@@ -2823,6 +2862,13 @@ static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const
                        t2f[6] = v[1];t2f[7] = tex->t1;
                        break;
                }
+               if (r_showparticleedges.integer)
+               {
+                       R_DebugLine(v3f, v3f + 3);
+                       R_DebugLine(v3f + 3, v3f + 6);
+                       R_DebugLine(v3f + 6, v3f + 9);
+                       R_DebugLine(v3f + 9, v3f);
+               }
        }
 
        // now render batches of particles based on blendmode and texture
@@ -2838,7 +2884,7 @@ static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const
                if (texture != particletexture[p->texnum].texture)
                {
                        texture = particletexture[p->texnum].texture;
-                       R_SetupShader_Generic(texture, NULL, GL_MODULATE, 1, false, false, false);
+                       R_SetupShader_Generic(texture, false, false, false);
                }
 
                if (p->blendmode == PBLEND_INVMOD)
@@ -2945,7 +2991,7 @@ void R_DrawParticles (void)
 //                             if (p->bounce && cl.time >= p->delayedcollisions)
                                if (p->bounce && cl_particles_collisions.integer && VectorLength(p->vel))
                                {
-                                       trace = CL_TraceLine(oldorg, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false, false);
+                                       trace = CL_TraceLine(oldorg, p->org, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, 0, collision_extendmovelength.value, true, false, &hitent, false, 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
@@ -3049,7 +3095,7 @@ void R_DrawParticles (void)
                                        break;
                                case pt_rain:
                                        a = CL_PointSuperContents(p->org);
-                                       if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
+                                       if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
                                                goto killparticle;
                                        break;
                                case pt_snow:
@@ -3061,7 +3107,7 @@ void R_DrawParticles (void)
                                                p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
                                        }
                                        a = CL_PointSuperContents(p->org);
-                                       if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
+                                       if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
                                                goto killparticle;
                                        break;
                                default:
@@ -3093,7 +3139,7 @@ void R_DrawParticles (void)
                                                                continue;
                                        }
                        // anything else just has to be in front of the viewer and visible at this distance
-                       if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist_start && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
+                       if (!r_refdef.view.useperspective || (DotProduct(p->org, r_refdef.view.forward) >= minparticledist_start && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size)))
                                R_MeshQueue_AddTransparent(TRANSPARENTSORT_DISTANCE, p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
                        break;
                }