From ccf0849cbfe3013a00dff7f37cc68e774b241cb5 Mon Sep 17 00:00:00 2001
From: lordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Fri, 23 Feb 2001 23:39:54 +0000
Subject: [PATCH] improved TraceLine in chase.c to be more generally useful
 (should move it to another file, though) error checking in qmalloc/qfree
 expanded COM_LoadFile max filename length from 32 to 1024 improved
 COM_LoadFile error messages added COM_ToLowerString and COM_ToUpperString
 .zym model support (not quite complete, in development) unfinished transition
 to hardware model transforms (not used yet) changed polylists to draw 5+
 point polygons as GL_POLYGON instead of GL_TRIANGLE_FAN (I thought I had
 reverted to GL_POLYGON a long time ago... strange) for a minor speedup new
 explosion effect (no particles) reverted to lightmapped dynamic lighting
 effects (vertex hybrid still available as r_dlightmaps 0) extreme speed
 improvements in which wall polygons are lit minor optimization to lightmap
 building (memset to clear blocklights) added and disabled texture caching to
 disk (it turned out to be a slowdown and a massive waste of space, but kept
 in source form incase it is ever useful) much improved missing QC function
 errors (most notably think) partial redesign of the skin colormapping engine
 (not used yet) more consistent light falloff on different objects created and
 scrapped a new particle explosion effect (way too fillrate intensive),
 superseded by the new explosion effect only dynamically lights smoke and
 blood particles unless r_dynamicparticles is 2 possible fix for alt-tab bug
 in NVidia drivers (from Dabb)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@152 d7cf8633-e32d-0410-b094-e92efae38249
---
 chase.c        |  11 +-
 cl_main.c      |   1 +
 common.c       |  49 ++++-
 common.h       |   4 +
 gl_models.c    | 572 +++++++++++++++++++++++++++++++++++++++++++++----
 gl_poly.c      |  12 +-
 gl_rmain.c     |  15 +-
 gl_rsurf.c     | 154 +++++++++++--
 gl_screen.c    |   2 +-
 gl_textures.c  | 136 ++++++++----
 glquake.h      |   2 +-
 host.c         |   2 +-
 host_cmd.c     |  70 ++++--
 model_alias.c  | 263 +++++++++++++++++++++--
 model_alias.h  |   4 +
 model_brush.h  |   3 +-
 model_shared.c |  13 +-
 pr_edict.c     |   2 +-
 pr_exec.c      |   4 +-
 progs.h        |   2 +-
 r_light.c      | 223 ++++++++++++++++---
 r_light.h      |   9 +-
 r_part.c       | 335 ++++++++++++++++-------------
 render.h       |   4 +
 sv_main.c      |   4 +-
 sv_phys.c      |  20 +-
 sv_user.c      |   2 +-
 transform.c    |  12 ++
 transform.h    |   1 +
 vid_wgl.c      |   3 +
 world.c        |   6 +-
 31 files changed, 1568 insertions(+), 372 deletions(-)

diff --git a/chase.c b/chase.c
index 8a46d76f..21893960 100644
--- a/chase.c
+++ b/chase.c
@@ -38,19 +38,22 @@ void Chase_Reset (void)
 //	start position 12 units behind head
 }
 
-void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
+float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
 {
 	trace_t	trace;
 
 	memset (&trace, 0, sizeof(trace));
-	VectorCopy (end, trace.endpos); // fix TraceLine endpoint bug
+	VectorCopy (end, trace.endpos);
+	trace.fraction = 1;
 	SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
 	VectorCopy (trace.endpos, impact);
+	VectorCopy (trace.plane.normal, normal);
+	return trace.fraction;
 }
 
 void Chase_Update (void)
 {
-	vec3_t	forward, up, right, stop, chase_dest;
+	vec3_t	forward, up, right, stop, chase_dest, normal;
 	float	dist;
 
 	chase_back.value = bound(0, chase_back.value, 128);
@@ -63,7 +66,7 @@ void Chase_Update (void)
 	chase_dest[1] = r_refdef.vieworg[1] + forward[1] * dist;
 	chase_dest[2] = r_refdef.vieworg[2] + forward[2] * dist + chase_up.value;
 
-	TraceLine (r_refdef.vieworg, chase_dest, stop);
+	TraceLine (r_refdef.vieworg, chase_dest, stop, normal);
 	chase_dest[0] = stop[0] + forward[0] * 8;
 	chase_dest[1] = stop[1] + forward[1] * 8;
 	chase_dest[2] = stop[2] + forward[2] * 8;
diff --git a/cl_main.c b/cl_main.c
index 56d77abb..1abbfd2f 100644
--- a/cl_main.c
+++ b/cl_main.c
@@ -345,6 +345,7 @@ void CL_DecayLights (void)
 	
 	time = cl.time - cl.oldtime;
 
+	c_dlights = 0;
 	dl = cl_dlights;
 	for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
 	{
diff --git a/common.c b/common.c
index a3baf195..a1e0a2e3 100644
--- a/common.c
+++ b/common.c
@@ -1175,6 +1175,8 @@ void *qmalloc(unsigned int size)
 	qmalloctotal_alloc += size;
 	qmalloctotal_alloccount++;
 	mem = malloc(size+sizeof(unsigned int));
+	if (!mem)
+		return mem;
 	*mem = size;
 	return (void *)(mem + 1);
 }
@@ -1182,6 +1184,8 @@ void *qmalloc(unsigned int size)
 void qfree(void *mem)
 {
 	unsigned int *m;
+	if (!mem)
+		return;
 	m = mem;
 	m--; // back up to size
 	qmalloctotal_free += *m; // size
@@ -1621,7 +1625,7 @@ byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
 {
 	int             h;
 	byte    *buf;
-	char    base[32];
+	char    base[1024];
 	int             len;
 
 	buf = NULL;     // quiet compiler warning
@@ -1638,24 +1642,29 @@ byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
 	{
 	case 1:
 		buf = Hunk_AllocName (len+1, va("%s (file)", path));
+		if (!buf)
+			Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
 		break;
 //	case 0:
 //		buf = Z_Malloc (len+1);
+//		if (!buf)
+//			Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
 //		break;
 	case 3:
 		buf = Cache_Alloc (loadcache, len+1, base);
+		if (!buf)
+			Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
 		break;
 	case 5:
 		buf = qmalloc (len+1);
+		if (!buf)
+			Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
+		break;
+	default:
+		Sys_Error ("COM_LoadFile: bad usehunk");
 		break;
-//	default:
-//		Sys_Error ("COM_LoadFile: bad usehunk");
-//		break;
 	}
 
-	if (!buf)
-		Sys_Error ("COM_LoadFile: not enough space for %s", path);
-		
 	((byte *)buf)[len] = 0;
 
 	Sys_FileRead (h, buf, len);                     
@@ -1938,3 +1947,29 @@ int COM_FileExists(char *filename)
 
 	return false;
 }
+
+
+//======================================
+// LordHavoc: added these because they are useful
+
+void COM_ToLowerString(char *in, char *out)
+{
+	while (*in)
+	{
+		if (*in >= 'A' && *in <= 'Z')
+			*out++ = *in++ + 'a' - 'A';
+		else
+			*out++ = *in++;
+	}
+}
+
+void COM_ToUpperString(char *in, char *out)
+{
+	while (*in)
+	{
+		if (*in >= 'a' && *in <= 'z')
+			*out++ = *in++ + 'A' - 'a';
+		else
+			*out++ = *in++;
+	}
+}
diff --git a/common.h b/common.h
index ad55254f..da25cafa 100644
--- a/common.h
+++ b/common.h
@@ -212,3 +212,7 @@ int COM_FileExists(char *filename);
 extern	struct cvar_s	registered;
 
 extern qboolean		standard_quake, rogue, hipnotic, nehahra;
+
+// LordHavoc: useful...
+extern void COM_ToLowerString(char *in, char *out);
+extern void COM_ToUpperString(char *in, char *out);
diff --git a/gl_models.c b/gl_models.c
index 8b2d2823..a496b1b3 100644
--- a/gl_models.c
+++ b/gl_models.c
@@ -1,11 +1,19 @@
 
 #include "quakedef.h"
 
+typedef struct
+{
+	float m[3][4];
+} zymbonematrix;
+
 // LordHavoc: vertex array
 float *aliasvert;
+float *modelaliasvert;
 float *aliasvertnorm;
 byte *aliasvertcolor;
 byte *aliasvertcolor2;
+zymbonematrix *zymbonepose;
+int *aliasvertusage;
 
 int chrometexture;
 
@@ -31,9 +39,12 @@ void gl_models_start()
 {
 	// allocate vertex processing arrays
 	aliasvert = qmalloc(sizeof(float[MD2MAX_VERTS][3]));
+	modelaliasvert = qmalloc(sizeof(float[MD2MAX_VERTS][3]));
 	aliasvertnorm = qmalloc(sizeof(float[MD2MAX_VERTS][3]));
 	aliasvertcolor = qmalloc(sizeof(byte[MD2MAX_VERTS][4]));
 	aliasvertcolor2 = qmalloc(sizeof(byte[MD2MAX_VERTS][4])); // used temporarily for tinted coloring
+	zymbonepose = qmalloc(sizeof(zymbonematrix[256]));
+	aliasvertusage = qmalloc(sizeof(int[MD2MAX_VERTS]));
 	makechrometexture();
 }
 
@@ -43,6 +54,8 @@ void gl_models_shutdown()
 	qfree(aliasvertnorm);
 	qfree(aliasvertcolor);
 	qfree(aliasvertcolor2);
+	qfree(zymbonepose);
+	qfree(aliasvertusage);
 }
 
 void GL_Models_Init()
@@ -55,6 +68,7 @@ extern vec3_t softwaretransform_y;
 extern vec3_t softwaretransform_z;
 extern vec_t softwaretransform_scale;
 extern vec3_t softwaretransform_offset;
+extern cvar_t r_modelsdonttransformnormals;
 void R_AliasLerpVerts(int vertcount, float lerp, trivert2 *verts1, vec3_t scale1, vec3_t translate1, trivert2 *verts2, vec3_t scale2, vec3_t translate2)
 {
 	int i;
@@ -78,45 +92,107 @@ void R_AliasLerpVerts(int vertcount, float lerp, trivert2 *verts1, vec3_t scale1
 		scaley1 = scale1[1] * ilerp;scaley2 = scale2[1] *  lerp;translatey = translate1[1] * ilerp + translate2[1] *  lerp;
 		scalez1 = scale1[2] * ilerp;scalez2 = scale2[2] *  lerp;translatez = translate1[2] * ilerp + translate2[2] *  lerp;
 		// generate vertices
-		for (i = 0;i < vertcount;i++)
-		{
-			// rotate, scale, and translate the vertex locations
-			point[0] = verts1->v[0] * scalex1 + verts2->v[0] * scalex2 + translatex;
-			point[1] = verts1->v[1] * scaley1 + verts2->v[1] * scaley2 + translatey;
-			point[2] = verts1->v[2] * scalez1 + verts2->v[2] * scalez2 + translatez;
-			*av++ = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
-			*av++ = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
-			*av++ = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
-			// rotate the normals
-			point[0] = verts1->n[0] * ilerp127 + verts2->n[0] * lerp127;
-			point[1] = verts1->n[1] * ilerp127 + verts2->n[1] * lerp127;
-			point[2] = verts1->n[2] * ilerp127 + verts2->n[2] * lerp127;
-			*avn++ = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
-			*avn++ = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
-			*avn++ = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
-			verts1++;verts2++;
+		if (r_modelsdonttransformnormals.value)
+		{
+			float *modelav = modelaliasvert;
+			for (i = 0;i < vertcount;i++)
+			{
+				// rotate, scale, and translate the vertex locations
+				point[0] = verts1->v[0] * scalex1 + verts2->v[0] * scalex2 + translatex;
+				point[1] = verts1->v[1] * scaley1 + verts2->v[1] * scaley2 + translatey;
+				point[2] = verts1->v[2] * scalez1 + verts2->v[2] * scalez2 + translatez;
+				// save mostly un-transformed copy for lighting
+				modelav[0] = point[0] * softwaretransform_scale;
+				modelav[1] = point[1] * softwaretransform_scale;
+				modelav[2] = point[2] * softwaretransform_scale;
+				av[0] = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+				av[1] = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+				av[2] = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+				// decompress but do not transform the normals
+				avn[0] = verts1->n[0] * ilerp127 + verts2->n[0] * lerp127;
+				avn[1] = verts1->n[1] * ilerp127 + verts2->n[1] * lerp127;
+				avn[2] = verts1->n[2] * ilerp127 + verts2->n[2] * lerp127;
+				modelav += 3;
+				av += 3;
+				avn += 3;
+				verts1++;verts2++;
+			}
+		}
+		else
+		{
+			for (i = 0;i < vertcount;i++)
+			{
+				// rotate, scale, and translate the vertex locations
+				point[0] = verts1->v[0] * scalex1 + verts2->v[0] * scalex2 + translatex;
+				point[1] = verts1->v[1] * scaley1 + verts2->v[1] * scaley2 + translatey;
+				point[2] = verts1->v[2] * scalez1 + verts2->v[2] * scalez2 + translatez;
+				av[0] = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+				av[1] = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+				av[2] = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+				// rotate the normals
+				point[0] = verts1->n[0] * ilerp127 + verts2->n[0] * lerp127;
+				point[1] = verts1->n[1] * ilerp127 + verts2->n[1] * lerp127;
+				point[2] = verts1->n[2] * ilerp127 + verts2->n[2] * lerp127;
+				avn[0] = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
+				avn[1] = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
+				avn[2] = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
+				av += 3;
+				avn += 3;
+				verts1++;verts2++;
+			}
 		}
 	}
 	else
 	{
 		// generate vertices
-		for (i = 0;i < vertcount;i++)
-		{
-			// rotate, scale, and translate the vertex locations
-			point[0] = verts1->v[0] * scale1[0] + translate1[0];
-			point[1] = verts1->v[1] * scale1[1] + translate1[1];
-			point[2] = verts1->v[2] * scale1[2] + translate1[2];
-			*av++ = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
-			*av++ = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
-			*av++ = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
-			// rotate the normals
-			point[0] = verts1->n[0] * (1.0f / 127.0f);
-			point[1] = verts1->n[1] * (1.0f / 127.0f);
-			point[2] = verts1->n[2] * (1.0f / 127.0f);
-			*avn++ = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
-			*avn++ = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
-			*avn++ = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
-			verts1++;
+		if (r_modelsdonttransformnormals.value)
+		{
+			float *modelav = modelaliasvert;
+			for (i = 0;i < vertcount;i++)
+			{
+				// rotate, scale, and translate the vertex locations
+				point[0] = verts1->v[0] * scale1[0] + translate1[0];
+				point[1] = verts1->v[1] * scale1[1] + translate1[1];
+				point[2] = verts1->v[2] * scale1[2] + translate1[2];
+				// save mostly un-transformed copy for lighting
+				modelav[0] = point[0] * softwaretransform_scale;
+				modelav[1] = point[1] * softwaretransform_scale;
+				modelav[2] = point[2] * softwaretransform_scale;
+				av[0] = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+				av[1] = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+				av[2] = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+				// decompress normal but do not rotate it
+				avn[0] = verts1->n[0] * (1.0f / 127.0f);
+				avn[1] = verts1->n[1] * (1.0f / 127.0f);
+				avn[2] = verts1->n[2] * (1.0f / 127.0f);
+				modelav += 3;
+				av += 3;
+				avn += 3;
+				verts1++;
+			}
+		}
+		else
+		{
+			for (i = 0;i < vertcount;i++)
+			{
+				// rotate, scale, and translate the vertex locations
+				point[0] = verts1->v[0] * scale1[0] + translate1[0];
+				point[1] = verts1->v[1] * scale1[1] + translate1[1];
+				point[2] = verts1->v[2] * scale1[2] + translate1[2];
+				av[0] = point[0] * matrix_x[0] + point[1] * matrix_y[0] + point[2] * matrix_z[0] + softwaretransform_offset[0];
+				av[1] = point[0] * matrix_x[1] + point[1] * matrix_y[1] + point[2] * matrix_z[1] + softwaretransform_offset[1];
+				av[2] = point[0] * matrix_x[2] + point[1] * matrix_y[2] + point[2] * matrix_z[2] + softwaretransform_offset[2];
+				// rotate the normals
+				point[0] = verts1->n[0] * (1.0f / 127.0f);
+				point[1] = verts1->n[1] * (1.0f / 127.0f);
+				point[2] = verts1->n[2] * (1.0f / 127.0f);
+				avn[0] = point[0] * softwaretransform_x[0] + point[1] * softwaretransform_y[0] + point[2] * softwaretransform_z[0];
+				avn[1] = point[0] * softwaretransform_x[1] + point[1] * softwaretransform_y[1] + point[2] * softwaretransform_z[1];
+				avn[2] = point[0] * softwaretransform_x[2] + point[1] * softwaretransform_y[2] + point[2] * softwaretransform_z[2];
+				av += 3;
+				avn += 3;
+				verts1++;
+			}
 		}
 	}
 }
@@ -223,7 +299,7 @@ R_DrawAliasFrame
 */
 extern vec3_t lightspot;
 void R_LightModel(int numverts, vec3_t center, vec3_t basecolor);
-void R_DrawAliasFrame (maliashdr_t *maliashdr, float alpha, vec3_t color, entity_t *ent, int shadow, vec3_t org, int frame, int *skin, int colormap, int effects, int flags)
+void R_DrawAliasFrame (maliashdr_t *maliashdr, float alpha, vec3_t color, entity_t *ent, int shadow, vec3_t org, vec3_t angles, int frame, int *skin, int colormap, int effects, int flags)
 {
 	int		i, pose;
 	float	lerpscale, lerp;
@@ -398,7 +474,7 @@ R_DrawQ2AliasFrame
 
 =================
 */
-void R_DrawQ2AliasFrame (md2mem_t *pheader, float alpha, vec3_t color, entity_t *ent, int shadow, vec3_t org, int frame, int skin, int effects, int flags)
+void R_DrawQ2AliasFrame (md2mem_t *pheader, float alpha, vec3_t color, entity_t *ent, int shadow, vec3_t org, vec3_t angles, int frame, int skin, int effects, int flags)
 {
 	int *order, count;
 	float lerp;
@@ -624,6 +700,420 @@ void R_DrawQ2AliasFrame (md2mem_t *pheader, float alpha, vec3_t color, entity_t
 	glDepthMask(1);
 }
 
+void ZymoticLerpBones(int count, float lerp2, zymbonematrix *bone1, zymbonematrix *bone2, zymbone_t *bone, float rootorigin[3], float rootangles[3])
+{
+	float lerp1;
+	zymbonematrix *out, rootmatrix, m;
+	lerp1 = 1 - lerp2;
+	out = zymbonepose;
+	AngleVectors(rootangles, rootmatrix.m[0], rootmatrix.m[1], rootmatrix.m[2]);
+	rootmatrix.m[0][3] = rootorigin[0];
+	rootmatrix.m[1][3] = rootorigin[1];
+	rootmatrix.m[2][3] = rootorigin[2];
+	if (lerp1 != 1) // interpolation
+	{
+		while(count--)
+		{
+			// interpolate matrices
+			m.m[0][0] = bone1->m[0][0] * lerp1 + bone2->m[0][0] * lerp2;
+			m.m[0][1] = bone1->m[0][1] * lerp1 + bone2->m[0][1] * lerp2;
+			m.m[0][2] = bone1->m[0][2] * lerp1 + bone2->m[0][2] * lerp2;
+			m.m[0][3] = bone1->m[0][3] * lerp1 + bone2->m[0][3] * lerp2;
+			m.m[1][0] = bone1->m[1][0] * lerp1 + bone2->m[1][0] * lerp2;
+			m.m[1][1] = bone1->m[1][1] * lerp1 + bone2->m[1][1] * lerp2;
+			m.m[1][2] = bone1->m[1][2] * lerp1 + bone2->m[1][2] * lerp2;
+			m.m[1][3] = bone1->m[1][3] * lerp1 + bone2->m[1][3] * lerp2;
+			m.m[2][0] = bone1->m[2][0] * lerp1 + bone2->m[2][0] * lerp2;
+			m.m[2][1] = bone1->m[2][1] * lerp1 + bone2->m[2][1] * lerp2;
+			m.m[2][2] = bone1->m[2][2] * lerp1 + bone2->m[2][2] * lerp2;
+			m.m[2][3] = bone1->m[2][3] * lerp1 + bone2->m[2][3] * lerp2;
+			if (bone->parent >= 0)
+				R_ConcatTransforms(&zymbonepose[bone->parent].m[0], &m.m[0], &out->m[0]);
+			else
+				R_ConcatTransforms(&rootmatrix.m[0], &m.m[0], &out->m[0]);
+			bone1++;
+			bone2++;
+			bone++;
+			out++;
+		}
+	}
+	else // no interpolation
+	{
+		while(count--)
+		{
+			if (bone->parent >= 0)
+				R_ConcatTransforms(&zymbonepose[bone->parent].m[0], &bone1->m[0], &out->m[0]);
+			else
+				R_ConcatTransforms(&rootmatrix.m[0], &bone1->m[0], &out->m[0]);
+			bone1++;
+			bone++;
+			out++;
+		}
+	}
+}
+
+void ZymoticTransformVerts(int vertcount, int *bonecounts, zymvertex_t *vert)
+{
+	int c;
+	float *out = aliasvert;
+	zymbonematrix *matrix;
+	while(vertcount--)
+	{
+		c = *bonecounts++;
+		if (c == 1)
+		{
+			matrix = &zymbonepose[vert->bonenum];
+			out[0] = vert->origin[0] * matrix->m[0][0] + vert->origin[1] * matrix->m[0][1] + vert->origin[2] * matrix->m[0][2] + matrix->m[0][3];
+			out[1] = vert->origin[0] * matrix->m[1][0] + vert->origin[1] * matrix->m[1][1] + vert->origin[2] * matrix->m[1][2] + matrix->m[1][3];
+			out[2] = vert->origin[0] * matrix->m[2][0] + vert->origin[1] * matrix->m[2][1] + vert->origin[2] * matrix->m[2][2] + matrix->m[2][3];
+			vert++;
+		}
+		else
+		{
+			VectorClear(out);
+			while(c--)
+			{
+				matrix = &zymbonepose[vert->bonenum];
+				out[0] += vert->origin[0] * matrix->m[0][0] + vert->origin[1] * matrix->m[0][1] + vert->origin[2] * matrix->m[0][2] + matrix->m[0][3];
+				out[1] += vert->origin[0] * matrix->m[1][0] + vert->origin[1] * matrix->m[1][1] + vert->origin[2] * matrix->m[1][2] + matrix->m[1][3];
+				out[2] += vert->origin[0] * matrix->m[2][0] + vert->origin[1] * matrix->m[2][1] + vert->origin[2] * matrix->m[2][2] + matrix->m[2][3];
+				vert++;
+			}
+		}
+		out += 3;
+	}
+}
+
+float ixtable[4096];
+
+void ZymoticCalcNormals(int vertcount, int shadercount, int *renderlist)
+{
+	int a, b, c, d;
+	float *out, v1[3], v2[3], normal[3];
+	int *u;
+	if (!ixtable[1])
+	{
+		ixtable[0] = 0;
+		for (a = 1;a < 4096;a++)
+			ixtable[a] = 1.0f / a;
+	}
+	// clear normals
+	memset(aliasvertnorm, 0, sizeof(float[3]) * vertcount);
+	memset(aliasvertusage, 0, sizeof(int) * vertcount);
+	// parse render list and accumulate surface normals
+	while(shadercount--)
+	{
+		d = *renderlist++;
+		while (d--)
+		{
+			a = renderlist[0]*3;
+			b = renderlist[1]*3;
+			c = renderlist[2]*3;
+			v1[0] = aliasvert[a+0] - aliasvert[b+0];
+			v1[1] = aliasvert[a+1] - aliasvert[b+1];
+			v1[2] = aliasvert[a+2] - aliasvert[b+2];
+			v2[0] = aliasvert[c+0] - aliasvert[b+0];
+			v2[1] = aliasvert[c+1] - aliasvert[b+1];
+			v2[2] = aliasvert[c+2] - aliasvert[b+2];
+			CrossProduct(v1, v2, normal);
+			VectorNormalize(normal);
+			// add surface normal to vertices
+			aliasvertnorm[a+0] += normal[0];
+			aliasvertnorm[a+1] += normal[1];
+			aliasvertnorm[a+2] += normal[2];
+			aliasvertusage[a]++;
+			aliasvertnorm[b+0] += normal[0];
+			aliasvertnorm[b+1] += normal[1];
+			aliasvertnorm[b+2] += normal[2];
+			aliasvertusage[b]++;
+			aliasvertnorm[c+0] += normal[0];
+			aliasvertnorm[c+1] += normal[1];
+			aliasvertnorm[c+2] += normal[2];
+			aliasvertusage[c]++;
+			renderlist += 3;
+		}
+	}
+	// average surface normals
+	out = aliasvertnorm;
+	u = aliasvertusage;
+	while(vertcount--)
+	{
+		if (*u > 1)
+		{
+			a = ixtable[*u];
+			out[0] *= a;
+			out[1] *= a;
+			out[2] *= a;
+		}
+		u++;
+		out += 3;
+	}
+}
+
+void GL_DrawZymoticModelMesh(byte *colors, zymtype1header_t *m)
+{
+	int i, c, *renderlist, *texturenum;
+	if (!r_render.value)
+		return;
+	renderlist = (int *)(m->lump_render.start + (int) m);
+	texturenum = (int *)(m->lump_shaders.start + (int) m);
+	if (gl_vertexarrays.value)
+	{
+		qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+		glEnableClientState(GL_VERTEX_ARRAY);
+
+		qglColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
+		glEnableClientState(GL_COLOR_ARRAY);
+
+		qglTexCoordPointer(2, GL_FLOAT, 0, (float *)(m->lump_texcoords.start + (int) m));
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = (*renderlist++) * 3;
+			glBindTexture(GL_TEXTURE_2D, *texturenum++);
+			qglDrawElements(GL_TRIANGLES, c, GL_UNSIGNED_INT, renderlist);
+			renderlist += c;
+		}
+
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+		glDisableClientState(GL_COLOR_ARRAY);
+
+		glDisableClientState(GL_VERTEX_ARRAY);
+	}
+	else
+	{
+		int index;
+		float *tex;
+		tex = (float *)(m->lump_texcoords.start + (int) m);
+
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = *renderlist++;
+			glBindTexture(GL_TEXTURE_2D, *texturenum++);
+			glBegin(GL_TRIANGLES);
+			while (c--)
+			{
+				index = *renderlist++;
+				glTexCoord2fv(tex + index*2);
+				glColor4ubv(colors + index*4);
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glTexCoord2fv(tex + index*2);
+				glColor4ubv(colors + index*4);
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glTexCoord2fv(tex + index*2);
+				glColor4ubv(colors + index*4);
+				glVertex3fv(aliasvert + index*3);
+			}
+			glEnd();
+		}
+	}
+}
+
+void GL_DrawZymoticModelMeshFog(vec3_t org, zymtype1header_t *m)
+{
+	vec3_t diff;
+	int i, c, *renderlist;
+	if (!r_render.value)
+		return;
+	renderlist = (int *)(m->lump_render.start + (int) m);
+	glDisable(GL_TEXTURE_2D);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glEnable (GL_BLEND);
+	glDepthMask(0); // disable zbuffer updates
+
+	VectorSubtract(org, r_refdef.vieworg, diff);
+	glColor4f(fogcolor[0], fogcolor[1], fogcolor[2], exp(fogdensity/DotProduct(diff,diff)));
+	if (gl_vertexarrays.value)
+	{
+		qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+		glEnableClientState(GL_VERTEX_ARRAY);
+
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = (*renderlist++) * 3;
+			qglDrawElements(GL_TRIANGLES, c, GL_UNSIGNED_INT, renderlist);
+			renderlist += c;
+		}
+
+		glDisableClientState(GL_VERTEX_ARRAY);
+	}
+	else
+	{
+		int index;
+		float *tex;
+		tex = (float *)(m->lump_texcoords.start + (int) m);
+
+		glBegin(GL_TRIANGLES);
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = *renderlist++;
+			while (c--)
+			{
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+			}
+		}
+		glEnd();
+	}
+	glEnable(GL_TEXTURE_2D);
+	glColor3f (1,1,1);
+}
+
+void GL_DrawZymoticModelMeshShadow(zymtype1header_t *m)
+{
+	int i, c, *renderlist;
+	float *av, l;
+	if (!r_render.value)
+		return;
+
+	// flatten it to make a shadow
+	av = aliasvert + 2;
+	l = lightspot[2] + 0.125;
+	for (i = 0;i < m->numverts;i++, av+=3)
+		if (*av > l)
+			*av = l;
+
+	renderlist = (int *)(m->lump_render.start + (int) m);
+	glDisable(GL_TEXTURE_2D);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glEnable (GL_BLEND);
+	glDepthMask(0); // disable zbuffer updates
+
+	glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
+	if (gl_vertexarrays.value)
+	{
+		qglVertexPointer(3, GL_FLOAT, 0, aliasvert);
+		glEnableClientState(GL_VERTEX_ARRAY);
+
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = (*renderlist++) * 3;
+			qglDrawElements(GL_TRIANGLES, c, GL_UNSIGNED_INT, renderlist);
+			renderlist += c;
+		}
+
+		glDisableClientState(GL_VERTEX_ARRAY);
+	}
+	else
+	{
+		int index;
+		float *tex;
+		tex = (float *)(m->lump_texcoords.start + (int) m);
+
+		glBegin(GL_TRIANGLES);
+		for (i = 0;i < m->numshaders;i++)
+		{
+			c = *renderlist++;
+			while (c--)
+			{
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+				index = *renderlist++;
+				glVertex3fv(aliasvert + index*3);
+			}
+		}
+		glEnd();
+	}
+	glEnable(GL_TEXTURE_2D);
+	glColor3f (1,1,1);
+}
+
+/*
+=================
+R_DrawZymoticFrame
+=================
+*/
+void R_DrawZymoticFrame (zymtype1header_t *m, float alpha, vec3_t color, entity_t *ent, int shadow, vec3_t org, vec3_t angles, int frame, int skinblah, int effects, int flags)
+{
+	zymscene_t *scene;
+	float scenetime, scenefrac;
+	int sceneframe1, sceneframe2;
+	zymbonematrix *basebonepose;
+	if ((frame >= m->numscenes) || (frame < 0))
+	{
+		Con_DPrintf ("R_ZymoticSetupFrame: no such frame %d\n", frame);
+		frame = 0;
+	}
+
+	scene = (zymscene_t *)(m->lump_scenes.start + (int) m) + frame;
+	if (ent->draw_lastmodel != ent->model || ent->draw_pose != frame || ent->draw_lerpstart >= cl.time)
+	{
+		ent->draw_lastmodel = ent->model;
+		ent->draw_lastpose = -1;
+		ent->draw_pose = frame;
+		ent->draw_lerpstart = cl.time;
+	}
+	scenetime = (cl.time - ent->draw_lerpstart) * scene->framerate;
+	sceneframe1 = (int) scenetime;
+	sceneframe2 = sceneframe1 + 1;
+	scenefrac = scenetime - sceneframe1;
+	if (scene->flags & ZYMSCENEFLAG_NOLOOP)
+	{
+		if (sceneframe1 > (scene->length - 1))
+			sceneframe1 = (scene->length - 1);
+		if (sceneframe2 > (scene->length - 1))
+			sceneframe2 = (scene->length - 1);
+	}
+	else
+	{
+		sceneframe1 %= scene->length;
+		sceneframe2 %= scene->length;
+	}
+	if (sceneframe2 == sceneframe1)
+		scenefrac = 0;
+
+	basebonepose = (zymbonematrix *)(m->lump_poses.start + (int) m);
+	ZymoticLerpBones(m->numbones, scenefrac, basebonepose + sceneframe1 * m->numbones, basebonepose + sceneframe2 * m->numbones, (zymbone_t *)(m->lump_bones.start + (int) m), org, angles);
+	ZymoticTransformVerts(m->numverts, (int *)(m->lump_vertbonecounts.start + (int) m), (zymvertex_t *)(m->lump_verts.start + (int) m));
+	ZymoticCalcNormals(m->numverts, m->numshaders, (int *)(m->lump_render.start + (int) m));
+
+	R_LightModel(m->numverts, org, color);
+
+	if (!r_render.value)
+		return;
+	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glShadeModel(GL_SMOOTH);
+	if (effects & EF_ADDITIVE)
+	{
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE); // additive rendering
+		glEnable(GL_BLEND);
+		glDepthMask(0);
+	}
+	else if (alpha != 1.0)
+	{
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		glEnable(GL_BLEND);
+		glDepthMask(0);
+	}
+	else
+	{
+		glDisable(GL_BLEND);
+		glDepthMask(1);
+	}
+
+	GL_DrawZymoticModelMesh(aliasvertcolor, m);
+
+	if (fogenabled)
+		GL_DrawZymoticModelMeshFog(org, m);
+
+	if (!fogenabled && r_shadows.value && !(effects & EF_ADDITIVE) && shadow)
+		GL_DrawZymoticModelMeshShadow(m);
+
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glEnable (GL_BLEND);
+	glDepthMask(1);
+}
+
 int modeldlightbits[8];
 extern int r_dlightframecount;
 
@@ -633,7 +1123,7 @@ R_DrawAliasModel
 
 =================
 */
-void R_DrawAliasModel (entity_t *ent, int cull, float alpha, model_t *clmodel, int frame, int skin, vec3_t org, int effects, int flags, int colormap)
+void R_DrawAliasModel (entity_t *ent, int cull, float alpha, model_t *clmodel, int frame, int skin, vec3_t org, vec3_t angles, int effects, int flags, int colormap)
 {
 	int			i;
 	vec3_t		mins, maxs, color;
@@ -699,8 +1189,10 @@ void R_DrawAliasModel (entity_t *ent, int cull, float alpha, model_t *clmodel, i
 		glEnable (GL_TEXTURE_2D);
 
 	c_alias_polys += clmodel->numtris;
-	if (clmodel->aliastype == ALIASTYPE_MD2)
-		R_DrawQ2AliasFrame (modelheader, alpha, color, ent, ent != &cl.viewent, org, frame, skinset[0], effects, flags);
+	if (clmodel->aliastype == ALIASTYPE_ZYM)
+		R_DrawZymoticFrame (modelheader, alpha, color, ent, ent != &cl.viewent, org, angles, frame, 0, effects, flags);
+	else if (clmodel->aliastype == ALIASTYPE_MD2)
+		R_DrawQ2AliasFrame (modelheader, alpha, color, ent, ent != &cl.viewent, org, angles, frame, skinset[0], effects, flags);
 	else
-		R_DrawAliasFrame (modelheader, alpha, color, ent, ent != &cl.viewent, org, frame, skinset, colormap, effects, flags);
+		R_DrawAliasFrame (modelheader, alpha, color, ent, ent != &cl.viewent, org, angles, frame, skinset, colormap, effects, flags);
 }
diff --git a/gl_poly.c b/gl_poly.c
index 2631e352..2f53d5cd 100644
--- a/gl_poly.c
+++ b/gl_poly.c
@@ -413,14 +413,14 @@ void transpolyrender()
 						glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 				}
 			}
-			qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
+			qglDrawArrays(GL_POLYGON, p->firstvert, p->verts);
 			if (p->glowtexnum)
 			{
 				texnum = p->glowtexnum; // highly unlikely to match next poly, but...
 				glBindTexture(GL_TEXTURE_2D, texnum);
 				tpolytype = TPOLYTYPE_ADD; // might match next poly
 				glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-				qglDrawArrays(GL_TRIANGLE_FAN, p->firstvert, p->verts);
+				qglDrawArrays(GL_POLYGON, p->firstvert, p->verts);
 			}
 		}
 		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -473,7 +473,7 @@ void transpolyrender()
 						glBegin(GL_QUADS);
 						break;
 					default:
-						glBegin(GL_TRIANGLE_FAN);
+						glBegin(GL_POLYGON);
 						points = -1; // to force a reinit on the next poly
 						break;
 					}
@@ -497,7 +497,7 @@ void transpolyrender()
 						glBlendFunc(GL_SRC_ALPHA, GL_ONE);
 					}
 					points = -1;
-					glBegin(GL_TRIANGLE_FAN);
+					glBegin(GL_POLYGON);
 					for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
 					{
 						glColor4ub(255,255,255,vert->a);
@@ -524,7 +524,7 @@ void transpolyrender()
 							texnum = p->fogtexnum;
 							glBindTexture(GL_TEXTURE_2D, texnum);
 						}
-						glBegin(GL_TRIANGLE_FAN);
+						glBegin(GL_POLYGON);
 						for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
 						{
 							VectorSubtract(vert->v, r_refdef.vieworg,diff);
@@ -537,7 +537,7 @@ void transpolyrender()
 					else
 					{
 						glDisable(GL_TEXTURE_2D);
-						glBegin(GL_TRIANGLE_FAN);
+						glBegin(GL_POLYGON);
 						for (j = 0,vert = &transvert[p->firstvert];j < p->verts;j++, vert++)
 						{
 							VectorSubtract(vert->v, r_refdef.vieworg,diff);
diff --git a/gl_rmain.c b/gl_rmain.c
index 92fa54c4..24a08cdb 100644
--- a/gl_rmain.c
+++ b/gl_rmain.c
@@ -256,6 +256,7 @@ extern void GL_Misc_Init();
 extern void R_Crosshairs_Init();
 extern void R_Light_Init();
 extern void R_Particles_Init();
+extern void R_Explosion_Init();
 
 void Render_Init()
 {
@@ -270,6 +271,7 @@ void Render_Init()
 	R_Crosshairs_Init();
 	R_Light_Init();
 	R_Particles_Init();
+	R_Explosion_Init();
 	R_StartModules();
 }
 
@@ -380,7 +382,7 @@ void R_DrawEntitiesOnList2 (void)
 		switch (currententity->model->type)
 		{
 		case mod_alias:
-			R_DrawAliasModel (currententity, true, modelalpha, currententity->model, currententity->frame, currententity->skinnum, currententity->origin, currententity->effects, currententity->model->flags, currententity->colormap);
+			R_DrawAliasModel (currententity, true, modelalpha, currententity->model, currententity->frame, currententity->skinnum, currententity->origin, currententity->angles, currententity->effects, currententity->model->flags, currententity->colormap);
 			break;
 
 		case mod_sprite:
@@ -411,7 +413,7 @@ void R_DrawViewModel (void)
 
 	// hack the depth range to prevent view model from poking into walls
 	glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
-	R_DrawAliasModel (currententity, FALSE, modelalpha, currententity->model, currententity->frame, currententity->skinnum, currententity->origin, currententity->effects, currententity->model->flags, currententity->colormap);
+	R_DrawAliasModel (currententity, FALSE, modelalpha, currententity->model, currententity->frame, currententity->skinnum, currententity->origin, currententity->angles, currententity->effects, currententity->model->flags, currententity->colormap);
 	glDepthRange (gldepthmin, gldepthmax);
 }
 
@@ -499,8 +501,7 @@ void R_SetupFrame (void)
 	c_bmodels = 0;
 	c_sprites = 0;
 	c_particles = 0;
-	c_dlights = 0;
-
+//	c_dlights = 0;
 }
 
 
@@ -725,7 +726,7 @@ char r_speeds2_string1[81], r_speeds2_string2[81], r_speeds2_string3[81], r_spee
 void R_RenderView (void)
 {
 	double starttime, currtime, temptime;
-	int time_clear, time_setup, time_world, time_bmodels, time_upload, time_sky, time_wall, time_models, time_moveparticles, time_drawparticles, time_transpoly, time_blend, time_total;
+	int time_clear, time_setup, time_world, time_bmodels, time_upload, time_sky, time_wall, time_models, time_moveparticles, time_drawparticles, time_moveexplosions, time_drawexplosions, time_transpoly, time_blend, time_total;
 //	double currtime, temptime;
 //	if (r_norefresh.value)
 //		return;
@@ -787,6 +788,10 @@ void R_RenderView (void)
 	TIMEREPORT(time_moveparticles)
 	R_DrawParticles ();
 	TIMEREPORT(time_drawparticles)
+	R_MoveExplosions();
+	TIMEREPORT(time_moveexplosions)
+	R_DrawExplosions();
+	TIMEREPORT(time_drawexplosions)
 
 	transpolyrender();
 	TIMEREPORT(time_transpoly)
diff --git a/gl_rsurf.c b/gl_rsurf.c
index 6ab30c4d..9084da32 100644
--- a/gl_rsurf.c
+++ b/gl_rsurf.c
@@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 int		lightmap_textures;
 
-signed blocklights[18*18*3]; // LordHavoc: *3 for colored lighting
+signed int blocklights[18*18*3]; // LordHavoc: *3 for colored lighting
 
 // LordHavoc: skinny but tall lightmaps for quicker subimage uploads
 #define	BLOCK_WIDTH		128
@@ -50,6 +50,7 @@ cvar_t gl_texsort = {"gl_texsort", "1"};
 //cvar_t gl_funnywalls = {"gl_funnywalls", "0"}; // LordHavoc: see BuildSurfaceDisplayList
 cvar_t r_newworldnode = {"r_newworldnode", "0"};
 cvar_t r_oldclip = {"r_oldclip", "1"};
+cvar_t r_dlightmap = {"r_dlightmap", "1"};
 
 qboolean lightmaprgba, nosubimagefragments, nosubimage, skyisvisible;
 int lightmapbytes;
@@ -81,6 +82,7 @@ void GL_Surf_Init()
 	Cvar_RegisterVariable(&gl_texsort);
 	Cvar_RegisterVariable(&r_newworldnode);
 	Cvar_RegisterVariable(&r_oldclip);
+	Cvar_RegisterVariable(&r_dlightmap);
 	// check if it's the glquake minigl driver
 	if (strncasecmp(gl_vendor,"3Dfx",4)==0)
 	if (!gl_arrays)
@@ -93,6 +95,113 @@ void GL_Surf_Init()
 	R_RegisterModule("GL_Surf", gl_surf_start, gl_surf_shutdown);
 }
 
+int         dlightdivtable[32768];
+
+/*
+	R_AddDynamicLights
+*/
+int R_AddDynamicLights (msurface_t *surf)
+{
+	int         sdtable[18], lnum, td, maxdist, maxdist2, maxdist3, i, j, s, t, smax, tmax, red, green, blue, lit, dist2, impacts, impactt;
+	unsigned int *bl;
+	float       dist;
+	vec3_t      impact, local;
+
+	// LordHavoc: use 64bit integer...  shame it's not very standardized...
+//#if _MSC_VER || __BORLANDC__
+//	__int64     k;
+//#else
+//	long long   k;
+//#endif
+
+	// LordHavoc: later note: MSVC and hopefully all other C compilers use a 64bit result for 32bit*32bit multiply, so that was not necessary
+	int			k;
+
+	lit = false;
+
+	if (!dlightdivtable[1])
+	{
+		dlightdivtable[0] = 4194304;
+		for (s = 1; s < 32768; s++)
+			dlightdivtable[s] = 4194304 / (s << 7);
+	}
+
+	smax = (surf->extents[0] >> 4) + 1;
+	tmax = (surf->extents[1] >> 4) + 1;
+
+	for (lnum = 0; lnum < MAX_DLIGHTS; lnum++)
+	{
+		if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31))))
+			continue;					// not lit by this light
+
+		VectorSubtract (cl_dlights[lnum].origin, currententity->origin, local);
+		dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
+
+		// for comparisons to minimum acceptable light
+		maxdist = (int) ((cl_dlights[lnum].radius * cl_dlights[lnum].radius) * LIGHTSCALE);
+
+		// clamp radius to avoid exceeding 32768 entry division table
+		if (maxdist > 4194304)
+			maxdist = 4194304;
+
+		dist2 = dist * dist;
+		if (dist2 >= maxdist)
+			continue;
+
+		impact[0] = cl_dlights[lnum].origin[0] - surf->plane->normal[0] * dist;
+		impact[1] = cl_dlights[lnum].origin[1] - surf->plane->normal[1] * dist;
+		impact[2] = cl_dlights[lnum].origin[2] - surf->plane->normal[2] * dist;
+
+		impacts = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+		impactt = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+
+		s = bound(0, impacts, smax * 16) - impacts;
+		t = bound(0, impactt, tmax * 16) - impactt;
+		i = s * s + t * t + dist2;
+		if (i > maxdist)
+			continue;
+
+		// reduce calculations
+		for (s = 0, i = impacts; s < smax; s++, i -= 16)
+			sdtable[s] = i * i + dist2;
+
+		maxdist3 = maxdist - (int) (dist * dist);
+
+		// convert to 8.8 blocklights format and scale up by radius
+		red = cl_dlights[lnum].color[0] * maxdist;
+		green = cl_dlights[lnum].color[1] * maxdist;
+		blue = cl_dlights[lnum].color[2] * maxdist;
+		bl = blocklights;
+
+		i = impactt;
+		for (t = 0; t < tmax; t++, i -= 16)
+		{
+			td = i * i;
+			// make sure some part of it is visible on this line
+			if (td < maxdist3)
+			{
+				maxdist2 = maxdist - td;
+				for (s = 0; s < smax; s++)
+				{
+					if (sdtable[s] < maxdist2)
+					{
+						k = dlightdivtable[(sdtable[s] + td) >> 7];
+						j = (red   * k) >> 9;bl[0] += j;
+						j = (green * k) >> 9;bl[1] += j;
+						j = (blue  * k) >> 9;bl[2] += j;
+						lit = true;
+					}
+					bl += 3;
+				}
+			}
+			else // skip line
+				bl += smax * 3;
+		}
+	}
+	return lit;
+}
+
+
 /*
 ===============
 R_BuildLightMap
@@ -103,18 +212,20 @@ Combine and scale multiple lightmaps into the 8.8 format in blocklights
 void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 {
 	int			smax, tmax;
-	int			i, j, size;
+	int			i, j, size, size3;
 	byte		*lightmap;
 	int			scale;
 	int			maps;
 	int			*bl;
 
+	surf->cached_dlight = 0;
 	surf->cached_lighthalf = lighthalf;
 	surf->cached_ambient = r_ambient.value;
 
 	smax = (surf->extents[0]>>4)+1;
 	tmax = (surf->extents[1]>>4)+1;
 	size = smax*tmax;
+	size3 = size*3;
 	lightmap = surf->samples;
 
 // set to full bright if no light data
@@ -131,14 +242,15 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 	else
 	{
 // clear to no light
-		bl = blocklights;
 		j = r_ambient.value * 512.0f; // would be 256.0f logically, but using 512.0f to match winquake style
-		for (i=0 ; i<size ; i++)
+		if (j)
 		{
-			*bl++ = j;
-			*bl++ = j;
-			*bl++ = j;
+			bl = blocklights;
+			for (i = 0;i < size3;i++)
+				*bl++ = j;
 		}
+		else
+			memset(&blocklights[0], 0, size*3*sizeof(int));
 
 // add all the lightmaps
 		if (lightmap)
@@ -148,14 +260,13 @@ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
 				scale = d_lightstylevalue[surf->styles[maps]];
 				surf->cached_light[maps] = scale;	// 8.8 fraction
 				bl = blocklights;
-				for (i=0 ; i<size ; i++)
-				{
-					*bl++ += *lightmap++ * scale;
+				for (i = 0;i < size3;i++)
 					*bl++ += *lightmap++ * scale;
-					*bl++ += *lightmap++ * scale;
-				}
 			}
 		}
+		if (r_dlightmap.value && surf->dlightframe == r_dlightframecount)
+			if ((surf->cached_dlight = R_AddDynamicLights(surf)))
+				c_light_polys++;
 	}
 	stride -= (smax*lightmapbytes);
 	bl = blocklights;
@@ -464,7 +575,7 @@ void RSurf_DrawWater(msurface_t *s, texture_t *t, int transform, int alpha)
 			wv += 6;
 		}
 	}
-	if (s->dlightframe == r_dlightframecount && r_dynamic.value)
+	if (s->dlightframe == r_dlightframecount)
 		RSurf_Light(s->dlightbits, s->polys);
 	wv = wvert;
 	// FIXME: make fog texture if water texture is transparent?
@@ -487,11 +598,14 @@ void RSurf_DrawWall(msurface_t *s, texture_t *t, int transform)
 	// check for lightmap modification
 	if (r_dynamic.value)
 	{
-		if (r_ambient.value != s->cached_ambient || lighthalf != s->cached_lighthalf
-		|| (s->styles[0] != 255 && d_lightstylevalue[s->styles[0]] != s->cached_light[0])
-		|| (s->styles[1] != 255 && d_lightstylevalue[s->styles[1]] != s->cached_light[1])
-		|| (s->styles[2] != 255 && d_lightstylevalue[s->styles[2]] != s->cached_light[2])
-		|| (s->styles[3] != 255 && d_lightstylevalue[s->styles[3]] != s->cached_light[3]))
+		if (s->cached_dlight
+		 || (r_dlightmap.value && s->dlightframe == r_dlightframecount)
+		 || r_ambient.value != s->cached_ambient
+		 || lighthalf != s->cached_lighthalf
+		 || (s->styles[0] != 255 && d_lightstylevalue[s->styles[0]] != s->cached_light[0])
+		 || (s->styles[1] != 255 && d_lightstylevalue[s->styles[1]] != s->cached_light[1])
+		 || (s->styles[2] != 255 && d_lightstylevalue[s->styles[2]] != s->cached_light[2])
+		 || (s->styles[3] != 255 && d_lightstylevalue[s->styles[3]] != s->cached_light[3]))
 			R_UpdateLightmap(s, s->lightmaptexturenum);
 	}
 	wv = wvert;
@@ -511,7 +625,7 @@ void RSurf_DrawWall(msurface_t *s, texture_t *t, int transform)
 	}
 	if ((currentwallpoly + polys > MAX_WALLPOLYS) || (currentwallvert+verts > MAX_WALLVERTS))
 		return;
-	if (s->dlightframe == r_dlightframecount && r_dynamic.value)
+	if ((!r_dlightmap.value) && s->dlightframe == r_dlightframecount)
 		lit = RSurf_Light(s->dlightbits, s->polys);
 	wv = wvert;
 	wp = &wallpoly[currentwallpoly];
@@ -600,7 +714,7 @@ void RSurf_DrawWallVertex(msurface_t *s, texture_t *t, int transform, int isbmod
 			wv += 6;
 		}
 	}
-	if (s->dlightframe == r_dlightframecount && r_dynamic.value)
+	if (s->dlightframe == r_dlightframecount)
 		RSurf_Light(s->dlightbits, s->polys);
 	wv = wvert;
 	if (isbmodel && (currententity->colormod[0] != 1 || currententity->colormod[1] != 1 || currententity->colormod[2] != 1))
diff --git a/gl_screen.c b/gl_screen.c
index 0e593950..23b7047c 100644
--- a/gl_screen.c
+++ b/gl_screen.c
@@ -968,7 +968,7 @@ void SCR_UpdateScreen (void)
 	GL_EndRendering ();
 }
 
-// for profiling, this is seperated
+// for profiling, this is separated
 void GL_Finish()
 {
 	if (!r_render.value)
diff --git a/gl_textures.c b/gl_textures.c
index 8afef82c..e7c19f38 100644
--- a/gl_textures.c
+++ b/gl_textures.c
@@ -1,6 +1,6 @@
 #include "quakedef.h"
 
-cvar_t		gl_max_size = {"gl_max_size", "1024"};
+cvar_t		gl_max_size = {"gl_max_size", "2048"};
 cvar_t		gl_picmip = {"gl_picmip", "0"};
 cvar_t		gl_lerpimages = {"gl_lerpimages", "1"};
 cvar_t		r_upload = {"r_upload", "1"};
@@ -11,27 +11,29 @@ int		gl_filter_max = GL_LINEAR;
 
 int		texels;
 
-// 4096x4096
-#define MAXMIPS 12
+// 65536x65536
+#define MAXMIPS 16
 
 typedef struct
 {
+	char	identifier[64];
 	int		texnum;
 	int		texeldatasize;
 	byte	*texels[MAXMIPS];
 	unsigned short texelsize[MAXMIPS][2];
-	char	identifier[64];
-	short	width, height;
+	unsigned short width, height;
 // LordHavoc: CRC to identify cache mismatchs
 	unsigned short crc;
 	char	mipmap;
 	char	alpha;
 	char	bytesperpixel;
 	char	lerped; // whether this texture was uploaded with or without interpolation
+	char	inuse; // cleared during texture purge when loading new level
+	char	pad; // unused
 } gltexture_t;
 
 #define	MAX_GLTEXTURES	4096
-gltexture_t	gltextures[MAX_GLTEXTURES];
+gltexture_t	*gltextures;
 int			numgltextures;
 
 typedef struct
@@ -150,13 +152,13 @@ void GL_TextureStats_PrintTotal(void)
 
 char engineversion[40];
 
-void GL_UploadTexture (gltexture_t *glt);
+//void GL_UploadTexture (gltexture_t *glt);
 void gl_textures_start()
 {
-	int i;
-	gltexture_t *glt;
-	for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
-		GL_UploadTexture(glt);
+//	int i;
+//	gltexture_t *glt;
+//	for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
+//		GL_UploadTexture(glt);
 }
 
 void gl_textures_shutdown()
@@ -178,6 +180,8 @@ void GL_Textures_Init (void)
 	if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
 		Cvar_Set ("gl_max_size", "256");
 
+	gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
+	memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
 	Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
 
 	R_RegisterModule("GL_Textures", gl_textures_start, gl_textures_shutdown);
@@ -479,11 +483,15 @@ int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolea
 {
 	unsigned short	crc;
 	int				i, width2, height2, width3, height3, w, h, mip;
-	gltexture_t		*glt;
+	gltexture_t		*glt, *freeglt;
+	// LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
+	//char			cachefilename[1024], *cachefile;
 
 	if (isDedicated)
 		return 1;
 
+	freeglt = NULL;
+
 	// LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
 	crc = CRC_Block(data, width*height*bytesperpixel);
 	// see if the texture is already present
@@ -491,30 +499,45 @@ int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolea
 	{
 		for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
 		{
-			if (!strcmp (identifier, glt->identifier))
+			if (glt->inuse)
 			{
-				// LordHavoc: everyone hates cache mismatchs, so I fixed it
-				if (crc != glt->crc || width != glt->width || height != glt->height)
+				if (!strcmp (identifier, glt->identifier))
 				{
-					Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
-					goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
+					// LordHavoc: everyone hates cache mismatchs, so I fixed it
+					if (crc != glt->crc || width != glt->width || height != glt->height)
+					{
+						Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
+						goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
+					}
+					if ((gl_lerpimages.value != 0) != glt->lerped)
+						goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
+					return glt->texnum;
 				}
-				if ((gl_lerpimages.value != 0) != glt->lerped)
-					goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
-				return glt->texnum;
 			}
+			else
+				freeglt = glt;
 		}
 	}
+	else
+		i = 0;
 	// LordHavoc: although this could be an else condition as it was in the original id code,
 	//            it is more clear this way
-	// LordHavoc: check if there are still slots available
-	if (numgltextures >= MAX_GLTEXTURES)
-		Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
-	glt = &gltextures[numgltextures++];
-
-	strcpy (glt->identifier, identifier);
-	glt->texnum = texture_extension_number;
-	texture_extension_number++;
+	if (freeglt)
+	{
+		glt = freeglt;
+		strcpy (glt->identifier, identifier);
+	}
+	else
+	{
+		// LordHavoc: check if there are still slots available
+		if (numgltextures >= MAX_GLTEXTURES)
+			Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
+		glt = &gltextures[numgltextures++];
+		glt->texnum = texture_extension_number;
+		texture_extension_number++;
+		strcpy (glt->identifier, identifier);
+	}
+
 // LordHavoc: label to drop out of the loop into the setup code
 GL_LoadTexture_setup:
 	// calculate power of 2 size
@@ -537,6 +560,31 @@ GL_LoadTexture_setup:
 	glt->bytesperpixel = bytesperpixel;
 	glt->lerped = gl_lerpimages.value != 0;
 	glt->alpha = false; // updated later
+	glt->inuse = true;
+	/*
+	// LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
+	sprintf(cachefilename, "%s%x%x%x.texels", identifier, width3, height3, crc);
+	for (i = 0;cachefilename[i];i++)
+	{
+		if (cachefilename[i] <= ' ' || cachefilename[i] >= 127 || cachefilename[i] == '/' || cachefilename[i] == '\\' || cachefilename[i] == ':' || cachefilename[i] == '*' || cachefilename[i] == '?')
+			cachefilename[i] = '@';
+		if (cachefilename[i] >= 'A' && cachefilename[i] <= 'Z')
+			cachefilename[i] += 'a' - 'A';
+	}
+	cachefile = COM_LoadMallocFile(cachefilename, true);
+	if (cachefile)
+	{
+		if (cachefile[0] == 'D' && cachefile[1] == 'P' && cachefile[2] == 'C' && cachefile[3] == 'T')
+		{
+			memcpy(glt->texels[0], cachefile + 4, width3*height3*4);
+			qfree(cachefile);
+//			Con_Printf("loaded cache texture %s\n", cachefilename);
+			goto cacheloaded;
+		}
+		else
+			qfree(cachefile);
+	}
+	*/
 	if (width == width3 && height == height3) // perfect match
 	{
 		if (bytesperpixel == 1) // 8bit
@@ -594,6 +642,19 @@ GL_LoadTexture_setup:
 			GL_ResampleTexture(temptexels, width, height, glt->texels[0], width2, height2);
 		qfree(temptexels);
 	}
+	/*
+	// LordHavoc: texture caching, turned out to be a waste of time (and immense waste of diskspace)
+	Con_Printf("writing cache texture %s\n", cachefilename);
+	cachefile = qmalloc(width3*height3*4 + 4);
+	cachefile[0] = 'D';
+	cachefile[1] = 'P';
+	cachefile[2] = 'C';
+	cachefile[3] = 'T';
+	memcpy(cachefile + 4, glt->texels[0], width3*height3*4);
+	COM_WriteFile(cachefilename, cachefile, width3*height3*4 + 4);
+	qfree(cachefile);
+cacheloaded:
+	*/
 	if (alpha)
 	{
 		byte	*in = glt->texels[0] + 3;
@@ -617,22 +678,3 @@ GL_LoadTexture_setup:
 
 	return glt->texnum;
 }
-
-int GL_GetTextureSlots (int count)
-{
-	gltexture_t		*glt, *first;
-
-	first = glt = &gltextures[numgltextures];
-	while (count--)
-	{
-		glt->identifier[0] = 0;
-		glt->texnum = texture_extension_number++;
-		glt->crc = 0;
-		glt->width = 0;
-		glt->height = 0;
-		glt->bytesperpixel = 0;
-		glt++;
-		numgltextures++;
-	}
-	return first->texnum;
-}
diff --git a/glquake.h b/glquake.h
index 89dc8c25..b6d2d32b 100644
--- a/glquake.h
+++ b/glquake.h
@@ -223,7 +223,7 @@ extern vec_t fogdensity;
 extern cvar_t gl_vertexarrays;
 extern qboolean lighthalf;
 
-extern void R_DrawAliasModel (entity_t *ent, int cull, float alpha, model_t *clmodel, int frame, int skin, vec3_t org, int effects, int flags, int colormap);
+extern void R_DrawAliasModel (entity_t *ent, int cull, float alpha, model_t *clmodel, int frame, int skin, vec3_t org, vec3_t angles, int effects, int flags, int colormap);
 
 extern cvar_t r_render;
 extern cvar_t r_upload;
diff --git a/host.c b/host.c
index 25240ec3..e179da68 100644
--- a/host.c
+++ b/host.c
@@ -373,7 +373,7 @@ void SV_DropClient (qboolean crash)
 		// this will set the body to a dead frame, among other things
 			saveSelf = pr_global_struct->self;
 			pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
-			PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
+			PR_ExecuteProgram (pr_global_struct->ClientDisconnect, "QC function ClientDisconnect is missing");
 			pr_global_struct->self = saveSelf;
 		}
 
diff --git a/host_cmd.c b/host_cmd.c
index f757184a..4783279d 100644
--- a/host_cmd.c
+++ b/host_cmd.c
@@ -894,7 +894,7 @@ void Host_Color_f(void)
 		pr_global_struct->time = sv.time;
 		pr_globals[0] = playercolor;
 		pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
-		PR_ExecuteProgram (SV_ChangeTeam);
+		PR_ExecuteProgram (SV_ChangeTeam, "");
 	}
 	else
 	{
@@ -929,7 +929,7 @@ void Host_Kill_f (void)
 	
 	pr_global_struct->time = sv.time;
 	pr_global_struct->self = EDICT_TO_PROG(sv_player);
-	PR_ExecuteProgram (pr_global_struct->ClientKill);
+	PR_ExecuteProgram (pr_global_struct->ClientKill, "QC function ClientKill is missing");
 }
 
 
@@ -1036,7 +1036,7 @@ void Host_Spawn_f (void)
 			Con_DPrintf("Calling RestoreGame\n");
 			pr_global_struct->time = sv.time;
 			pr_global_struct->self = EDICT_TO_PROG(sv_player);
-			PR_ExecuteProgram (RestoreGame);
+			PR_ExecuteProgram (RestoreGame, "");
 		}
 	}
 	else
@@ -1061,12 +1061,12 @@ void Host_Spawn_f (void)
 
 		pr_global_struct->time = sv.time;
 		pr_global_struct->self = EDICT_TO_PROG(sv_player);
-		PR_ExecuteProgram (pr_global_struct->ClientConnect);
+		PR_ExecuteProgram (pr_global_struct->ClientConnect, "QC function ClientConnect is missing");
 
 		if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
 			Sys_Printf ("%s entered the game\n", host_client->name);
 
-		PR_ExecuteProgram (pr_global_struct->PutClientInServer);	
+		PR_ExecuteProgram (pr_global_struct->PutClientInServer, "QC function PutClientInServer is missing");
 	}
 
 
@@ -1469,15 +1469,59 @@ void Host_Viewframe_f (void)
 
 void PrintFrameName (model_t *m, int frame)
 {
-	maliashdr_t 	*mheader;
-	maliasframe_t	*frameinfo;
-
-	mheader = (maliashdr_t *)Mod_Extradata (m);
-	if (!mheader)
+	if (m->type != mod_alias)
 		return;
-	frameinfo = &((maliasframe_t *)(mheader->framedata + (int) mheader))[frame];
-	
-	Con_Printf ("frame %i: %s\n", frame, frameinfo->name);
+	switch(m->aliastype)
+	{
+	case ALIASTYPE_MDL:
+		{
+			maliashdr_t *mheader;
+			maliasframe_t *frameinfo;
+
+			mheader = (maliashdr_t *)Mod_Extradata (m);
+			if (!mheader)
+				return;
+			if (frame < 0 || frame >= mheader->numframes)
+				frame = 0;
+			frameinfo = &((maliasframe_t *)(mheader->framedata + (int) mheader))[frame];
+			
+			Con_Printf ("frame %i: %s\n", frame, frameinfo->name);
+		}
+		break;
+	case ALIASTYPE_MD2:
+		{
+			md2mem_t *mheader;
+			md2memframe_t *frameinfo;
+
+			mheader = (md2mem_t *)Mod_Extradata (m);
+			if (!mheader)
+				return;
+			if (frame < 0 || frame >= mheader->num_frames)
+				frame = 0;
+			frameinfo = (md2memframe_t *)(mheader->ofs_frames + (int) mheader) + frame;
+			
+			Con_Printf ("frame %i: %s\n", frame, frameinfo->name);
+		}
+		break;
+	case ALIASTYPE_ZYM:
+		{
+			zymtype1header_t *mheader;
+			zymscene_t *scene;
+
+			mheader = (zymtype1header_t *)Mod_Extradata (m);
+			if (!mheader)
+				return;
+			if (frame < 0 || frame >= mheader->numscenes)
+				frame = 0;
+			scene = (zymscene_t *)(mheader->lump_scenes.start + (int) mheader) + frame;
+			
+			Con_Printf ("frame %i: %s\n", frame, scene->name);
+		}
+		break;
+	default:
+		Con_Printf("frame %i: (unknown alias model type)\n", frame);
+		break;
+	}
 }
 
 /*
diff --git a/model_alias.c b/model_alias.c
index d3cb6e78..de5b65e7 100644
--- a/model_alias.c
+++ b/model_alias.c
@@ -275,12 +275,86 @@ int GL_SkinSplit(byte *in, byte *out, int width, int height, unsigned short bits
 		return 0;
 }
 
+int GL_SkinCheck(byte *in, int width, int height, unsigned short bits)
+{
+	int i, pixels, passed;
+	byte pixeltest[16];
+	for (i = 0;i < 16;i++)
+		pixeltest[i] = (bits & (1 << i)) != 0;
+	pixels = width*height;
+	passed = 0;
+	while(pixels--)
+	{
+		if (pixeltest[*in >> 4] && *in != 0 && *in != 255)
+			return true;
+		in++;
+	}
+	return false;
+}
+
+void Mod_LoadSkin (maliashdr_t *mheader, char *basename, byte *skindata, byte *skintemp, int width, int height, int *skintexnum)
+{
+#if 0
+	int skin_normal, skin_pants, skin_shirt, skin_glow, skin_body, temp;
+	skin_normal = loadtextureimage(va("%s_normal", basename));
+	skin_pants  = loadtextureimage(va("%s_pants" , basename));
+	skin_shirt  = loadtextureimage(va("%s_shirt" , basename));
+	skin_glow   = loadtextureimage(va("%s_glow"  , basename));
+	skin_body   = loadtextureimage(va("%s_body"  , basename));
+	if (!(skin_normal || skin_pants || skin_shirt || skin_glow || skin_body))
+		skin_body = loadtextureimage(name);
+	if (skin_normal || skin_pants || skin_shirt || skin_glow || skin_body)
+	{
+		skintexnum[0] = skin_normal;
+		skintexnum[1] = skin_pants;
+		skintexnum[2] = skin_shirt;
+		skintexnum[3] = skin_glow;
+		skintexnum[4] = skin_body;
+	}
+	else
+	{
+		Mod_FloodFillSkin(skin, width, height);
+		skin_normal = GL_SkinCheck((byte *)pskintype, width, height, 0x3FBD);
+		skin_pants = GL_SkinCheck((byte *)pskintype, width, height, 0x0040);
+		skin_shirt = GL_SkinCheck((byte *)pskintype, width, height, 0x0002);
+		skin_glow = GL_SkinCheck((byte *)pskintype, width, height, 0xC000);
+		skin_body = GL_SkinCheck((byte *)pskintype, width, height, 0x3FFF);
+		if (skin_pants || skin_shirt)
+		{
+			byte *saveskin;
+			saveskin = Hunk_AllocName(width*height, va("%s skin", loadname));
+			memcpy((saveskin, byte *)pskintype, width*height);
+			temp = (int) saveskin - (int) mheader;
+			skintexnum[0] = skin_normal ? -temp : 0;
+			skintexnum[1] = skin_pants ? -temp : 0;
+			skintexnum[2] = skin_shirt ? -temp : 0;
+			skintexnum[3] = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0xC000, va("%s_glow", basename)); // glow
+			skintexnum[4] = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FFF, va("%s_body", basename)); // body (normal + pants + shirt, but not glow)
+		}
+		else
+		{
+			skintexnum[0] = 0;
+			skintexnum[1] = 0;
+			skintexnum[2] = 0;
+			skintexnum[3] = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0xC000, va("%s_glow", basename)); // glow
+			skintexnum[4] = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FFF, va("%s_body", basename)); // body (normal + pants + shirt, but not glow)
+		}
+	}
+#else
+	skintexnum[0] = GL_SkinSplit(skindata, skintemp, width, height, 0x3FBD, va("&%s_normal", basename)); // normal (no special colors)
+	skintexnum[1] = GL_SkinSplitShirt(skindata, skintemp, width, height, 0x0040, va("&%s_pants", basename)); // pants
+	skintexnum[2] = GL_SkinSplitShirt(skindata, skintemp, width, height, 0x0002, va("&%s_shirt", basename)); // shirt
+	skintexnum[3] = GL_SkinSplit(skindata, skintemp, width, height, 0xC000, va("%s_glow", basename)); // glow
+	skintexnum[4] = GL_SkinSplit(skindata, skintemp, width, height, 0x3FFF, va("%s_body", basename)); // body (normal + pants + shirt, but not glow)
+#endif
+}
+
 /*
 ===============
 Mod_LoadAllSkins
 ===============
 */
-void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int width, int height)
+void *Mod_LoadAllSkins (maliashdr_t *mheader, int numskins, daliasskintype_t *pskintype, int width, int height)
 {
 	int		i, j;
 	char	name[32];
@@ -338,13 +412,8 @@ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int width, in
 			*skinrange++ = 1; // single skin
 			skinnum++;
 			sprintf (name, "%s_%i", loadmodel->name, i);
-
-			Mod_FloodFillSkin( skin, width, height );
-			*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FBD, va("&%s_normal", name)); // normal (no special colors)
-			*skintexnum++ = GL_SkinSplitShirt((byte *)pskintype, skintemp, width, height, 0x0040, va("&%s_pants",  name)); // pants
-			*skintexnum++ = GL_SkinSplitShirt((byte *)pskintype, skintemp, width, height, 0x0002, va("&%s_shirt",  name)); // shirt
-			*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0xC000, va("%s_glow",   name)); // glow
-			*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FFF, va("%s_body",   name)); // body (normal + pants + shirt, but not glow)
+			Mod_LoadSkin(mheader, name, (byte *)pskintype, skintemp, width, height, skintexnum);
+			skintexnum += 5;
 			pskintype = (daliasskintype_t *)((byte *)(pskintype) + s);
 		}
 		else
@@ -360,14 +429,9 @@ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int width, in
 			skinnum += groupskins;
 			for (j = 0;j < groupskins;j++)
 			{
-				sprintf (name, "%s_%i_%i", loadmodel->name, i,j);
-
-				Mod_FloodFillSkin( skin, width, height );
-				*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FBD, va("&%s_normal", name)); // normal (no special colors)
-				*skintexnum++ = GL_SkinSplitShirt((byte *)pskintype, skintemp, width, height, 0x0040, va("&%s_pants",  name)); // pants
-				*skintexnum++ = GL_SkinSplitShirt((byte *)pskintype, skintemp, width, height, 0x0002, va("&%s_shirt",  name)); // shirt
-				*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0xC000, va("%s_glow",   name)); // glow
-				*skintexnum++ = GL_SkinSplit((byte *)pskintype, skintemp, width, height, 0x3FFF, va("%s_body",   name)); // body (normal + pants + shirt, but not glow)
+				sprintf (name, "%s_%i_%i", loadmodel->name, i, j);
+				Mod_LoadSkin(mheader, name, (byte *)pskintype, skintemp, width, height, skintexnum);
+				skintexnum += 5;
 				pskintype = (daliasskintype_t *)((byte *)(pskintype) + s);
 			}
 		}
@@ -426,7 +490,8 @@ void Mod_LoadAliasModel (model_t *mod, void *buffer)
 		Host_Error ("%s has wrong version number (%i should be %i)",
 				 mod->name, version, ALIAS_VERSION);
 
-	mod->type = ALIASTYPE_MDL;
+	mod->type = mod_alias;
+	mod->aliastype = ALIASTYPE_MDL;
 
 	numframes = LittleLong(pinmodel->numframes);
 	BOUNDI(numframes,0,65536);
@@ -483,7 +548,7 @@ void Mod_LoadAliasModel (model_t *mod, void *buffer)
 
 	// load the skins
 	pskintype = (daliasskintype_t *)&pinmodel[1];
-	pskintype = Mod_LoadAllSkins(numskins, pskintype, skinwidth, skinheight);
+	pskintype = Mod_LoadAllSkins(mheader, numskins, pskintype, skinwidth, skinheight);
 
 	// store texture coordinates into temporary array, they will be stored after usage is determined (triangle data)
 	pinstverts = (stvert_t *)pskintype;
@@ -805,6 +870,7 @@ void Mod_LoadQ2AliasModel (model_t *mod, void *buffer)
 	{
 		for (j = 0;j < 3;j++)
 		{
+			strcpy(poutframe->name, pinframe->name);
 			poutframe->scale[j] = LittleFloat(pinframe->scale[j]);
 			poutframe->translate[j] = LittleFloat(pinframe->translate[j]);
 		}
@@ -838,3 +904,164 @@ void Mod_LoadQ2AliasModel (model_t *mod, void *buffer)
 
 	Hunk_FreeToLowMark (start);
 }
+
+void swapintblock(int *m, int size)
+{
+	size /= 4;
+	while(size--)
+		*m++ = BigLong(*m);
+}
+
+void Mod_LoadZymoticModel (model_t *mod, void *buffer)
+{
+	int i, pbase, start, end, total, *skin, *skinrange, *texturenum;
+	char *shadername;
+	zymtype1header_t *pinmodel, *pheader;
+	zymscene_t *scene;
+	zymbone_t *bone;
+
+	start = Hunk_LowMark ();
+
+	pinmodel = (void *)buffer;
+
+	if (memcmp(pinmodel->id, "ZYMOTICMODEL", 12))
+		Host_Error ("Mod_LoadZymoticModel: %s is not a zymotic model\n");
+
+	if (BigLong(pinmodel->type) != 1)
+		Host_Error ("Mod_LoadZymoticModel: only type 1 (skeletal pose) models are currently supported\n");
+
+	mod->type = mod_alias;
+	mod->aliastype = ALIASTYPE_ZYM;
+
+	pheader = Hunk_AllocName (BigLong(pinmodel->filesize), va("%s Zymotic model", loadname));
+
+	pbase = (int) pheader;
+
+	memcpy(pheader, pinmodel, BigLong(pinmodel->filesize));
+
+	// byteswap header
+	memcpy(pheader->id, pinmodel->id, 12);
+	pheader->type = BigLong(pheader->type);
+	pheader->filesize = BigLong(pheader->filesize);
+	pheader->mins[0] = BigFloat(pheader->mins[0]);
+	pheader->mins[1] = BigFloat(pheader->mins[1]);
+	pheader->mins[2] = BigFloat(pheader->mins[2]);
+	pheader->maxs[0] = BigFloat(pheader->maxs[0]);
+	pheader->maxs[1] = BigFloat(pheader->maxs[1]);
+	pheader->maxs[2] = BigFloat(pheader->maxs[2]);
+	pheader->radius = BigFloat(pheader->radius);
+	pheader->numverts = BigLong(pheader->numverts);
+	pheader->numtris = BigLong(pheader->numtris);
+	pheader->numshaders = BigLong(pheader->numshaders);
+	pheader->numbones = BigLong(pheader->numbones);
+	pheader->numscenes = BigLong(pheader->numscenes);
+
+
+	pheader->lump_scenes.start = BigLong(pheader->lump_scenes.start);pheader->lump_scenes.length = BigLong(pheader->lump_scenes.length);
+	pheader->lump_poses.start = BigLong(pheader->lump_poses.start);pheader->lump_poses.length = BigLong(pheader->lump_poses.length);
+	pheader->lump_bones.start = BigLong(pheader->lump_bones.start);pheader->lump_bones.length = BigLong(pheader->lump_bones.length);
+	pheader->lump_vertbonecounts.start = BigLong(pheader->lump_vertbonecounts.start);pheader->lump_vertbonecounts.length = BigLong(pheader->lump_vertbonecounts.length);
+	pheader->lump_verts.start = BigLong(pheader->lump_verts.start);pheader->lump_verts.length = BigLong(pheader->lump_verts.length);
+	pheader->lump_texcoords.start = BigLong(pheader->lump_texcoords.start);pheader->lump_texcoords.length = BigLong(pheader->lump_texcoords.length);
+	pheader->lump_render.start = BigLong(pheader->lump_render.start);pheader->lump_render.length = BigLong(pheader->lump_render.length);
+	pheader->lump_shaders.start = BigLong(pheader->lump_shaders.start);pheader->lump_shaders.length = BigLong(pheader->lump_shaders.length);
+	pheader->lump_trizone.start = BigLong(pheader->lump_trizone.start);pheader->lump_trizone.length = BigLong(pheader->lump_trizone.length);
+
+	mod->flags = 0; // there are no flags
+	mod->numframes = pheader->numscenes;
+	mod->synctype = ST_SYNC;
+	mod->numtris = pheader->numtris;
+
+	// FIXME: add skin support and texturing and shaders and...
+// load the skins
+	skinrange = loadmodel->skinanimrange;
+	skin = loadmodel->skinanim;
+//	skinrange = Hunk_AllocName (sizeof(int) * (pheader->num_skins * 2), loadname);	
+//	skin = skinrange + pheader->num_skins * 2;
+//	loadmodel->skinanimrange = (int) skinrange - (int) pheader;
+//	loadmodel->skinanim = (int) skin - (int) pheader;
+	*skinrange++ = 0;
+	*skinrange++ = 1;
+	*skin++ = 0;
+	*skin++ = 0;
+	*skin++ = 0;
+	*skin++ = 0;
+	*skin++ = 0;
+	loadmodel->numskins = 1;
+
+	// go through the lumps, swapping things
+
+//	zymlump_t lump_scenes; // zymscene_t scene[numscenes]; // name and other information for each scene (see zymscene struct)
+	scene = (void *) (pheader->lump_scenes.start + pbase);
+	for (i = 0;i < pheader->numscenes;i++)
+	{
+		scene->mins[0] = BigFloat(scene->mins[0]);
+		scene->mins[1] = BigFloat(scene->mins[1]);
+		scene->mins[2] = BigFloat(scene->mins[2]);
+		scene->maxs[0] = BigFloat(scene->maxs[0]);
+		scene->maxs[1] = BigFloat(scene->maxs[1]);
+		scene->maxs[2] = BigFloat(scene->maxs[2]);
+		scene->radius = BigFloat(scene->radius);
+		scene->framerate = BigFloat(scene->framerate);
+		scene->flags = BigLong(scene->flags);
+		scene->start = BigLong(scene->start);
+		scene->length = BigLong(scene->length);
+		scene++;
+	}
+
+//	zymlump_t lump_poses; // float pose[numposes][numbones][6]; // animation data
+	swapintblock((void *) (pheader->lump_poses.start + pbase), pheader->lump_poses.length);
+
+//	zymlump_t lump_bones; // zymbone_t bone[numbones];
+	bone = (void *) (pheader->lump_bones.start + pbase);
+	for (i = 0;i < pheader->numbones;i++)
+	{
+		bone->flags = BigLong(bone->flags);
+		bone->parent = BigLong(bone->parent);
+		bone++;
+	}
+
+//	zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)
+	swapintblock((void *) (pheader->lump_vertbonecounts.start + pbase), pheader->lump_vertbonecounts.length);
+
+//	zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
+	swapintblock((void *) (pheader->lump_verts.start + pbase), pheader->lump_verts.length);
+
+//	zymlump_t lump_texcoords; // float texcoords[numvertices][2];
+	swapintblock((void *) (pheader->lump_texcoords.start + pbase), pheader->lump_texcoords.length);
+
+//	zymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int shader, count), each run can be used with glDrawElements (each triangle is 3 int indices)
+	swapintblock((void *) (pheader->lump_render.start + pbase), pheader->lump_render.length);
+
+//	zymlump_t lump_shaders; // char shadername[numshaders][32]; // shaders used on this model
+	shadername = (void *) (pheader->lump_shaders.start + pbase);
+	texturenum = (void *) shadername;
+	for (i = 0;i < pheader->numshaders;i++)
+	{
+		int j;
+		j = loadtextureimage(shadername, 0, 0, true, true);
+		shadername += 32;
+		*texturenum++ = j; // reuse shader name list for texture numbers
+	}
+
+//	zymlump_t lump_trizone; // byte trizone[numtris]; // see trizone explanation
+	swapintblock((void *) (pheader->lump_trizone.start + pbase), pheader->lump_trizone.length);
+
+	// model bbox
+	for (i = 0;i < 3;i++)
+	{
+		mod->mins[i] = pheader->mins[i];
+		mod->maxs[i] = pheader->maxs[i];
+	}
+
+// move the complete, relocatable alias model to the cache
+	end = Hunk_LowMark ();
+	total = end - start;
+	
+	Cache_Alloc (&mod->cache, total, loadname);
+	if (!mod->cache.data)
+		return;
+	memcpy (mod->cache.data, pheader, total);
+
+	Hunk_FreeToLowMark (start);
+}
diff --git a/model_alias.h b/model_alias.h
index 2b0031e2..73e83107 100644
--- a/model_alias.h
+++ b/model_alias.h
@@ -159,6 +159,7 @@ typedef struct
 {
 	float		scale[3];	// multiply byte verts by this
 	float		translate[3];	// then add this
+	char		name[16];	// LordHavoc: kept for viewthing
 	trivert2	verts[1];	// variable sized
 } md2memframe_t;
 
@@ -221,3 +222,6 @@ typedef struct
 
 #define ALIASTYPE_MDL 1
 #define ALIASTYPE_MD2 2
+#define ALIASTYPE_ZYM 3
+
+#include "model_zymotic.h"
\ No newline at end of file
diff --git a/model_brush.h b/model_brush.h
index 172ae413..2ca09612 100644
--- a/model_brush.h
+++ b/model_brush.h
@@ -135,7 +135,8 @@ typedef struct msurface_s
 	int			lightmaptexturenum;
 	byte		styles[MAXLIGHTMAPS];
 	unsigned short	cached_light[MAXLIGHTMAPS];	// values currently used in lightmap
-	int			cached_lighthalf;			// LordHavoc: to cause lightmap to be rerendered when lighthalf changes
+	short		cached_dlight;				// LordHavoc: if lightmap was lit by dynamic lights, update on frame after end of effect to erase it
+	short		cached_lighthalf;			// LordHavoc: to cause lightmap to be rerendered when lighthalf changes
 	float		cached_ambient;				// LordHavoc: rerender lightmaps when r_ambient changes
 	byte		*samples;		// [numstyles*surfsize]
 } msurface_t;
diff --git a/model_shared.c b/model_shared.c
index 236a5d60..65acc092 100644
--- a/model_shared.c
+++ b/model_shared.c
@@ -27,10 +27,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 model_t	*loadmodel;
 char	loadname[32];	// for hunk tags
 
-void Mod_LoadSpriteModel (model_t *mod, void *buffer);
-void Mod_LoadBrushModel (model_t *mod, void *buffer);
-void Mod_LoadAliasModel (model_t *mod, void *buffer);
-void Mod_LoadQ2AliasModel (model_t *mod, void *buffer);
+extern void Mod_LoadSpriteModel (model_t *mod, void *buffer);
+extern void Mod_LoadBrushModel (model_t *mod, void *buffer);
+extern void Mod_LoadAliasModel (model_t *mod, void *buffer);
+extern void Mod_LoadQ2AliasModel (model_t *mod, void *buffer);
+extern void Mod_LoadZymoticModel (model_t *mod, void *buffer);
 model_t *Mod_LoadModel (model_t *mod, qboolean crash);
 
 #define	MAX_MOD_KNOWN	512
@@ -194,6 +195,10 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash)
 	case MD2IDALIASHEADER: // LordHavoc: added Quake2 model support
 		Mod_LoadQ2AliasModel (mod, buf);
 		break;
+
+	case (('O'<<24)+('M'<<16)+('Y'<<8)+'Z'):
+		Mod_LoadZymoticModel(mod, buf);
+		break;
 		
 	case IDSPRITEHEADER:
 		Mod_LoadSpriteModel (mod, buf);
diff --git a/pr_edict.c b/pr_edict.c
index 0bb47d99..0488c176 100644
--- a/pr_edict.c
+++ b/pr_edict.c
@@ -1086,7 +1086,7 @@ void ED_LoadFromFile (char *data)
 		}
 
 		pr_global_struct->self = EDICT_TO_PROG(ent);
-		PR_ExecuteProgram (func - pr_functions);
+		PR_ExecuteProgram (func - pr_functions, "");
 	}	
 
 	Con_DPrintf ("%i entities inhibited\n", inhibit);
diff --git a/pr_exec.c b/pr_exec.c
index a608a074..e70f1581 100644
--- a/pr_exec.c
+++ b/pr_exec.c
@@ -363,7 +363,7 @@ PR_ExecuteProgram
 #define OPB ((eval_t *)&pr_globals[(unsigned short) st->b])
 #define OPC ((eval_t *)&pr_globals[(unsigned short) st->c])
 extern cvar_t pr_boundscheck;
-void PR_ExecuteProgram (func_t fnum)
+void PR_ExecuteProgram (func_t fnum, char *errormessage)
 {
 	dstatement_t	*st;
 	dfunction_t	*f, *newf;
@@ -376,7 +376,7 @@ void PR_ExecuteProgram (func_t fnum)
 	{
 		if (pr_global_struct->self)
 			ED_Print (PROG_TO_EDICT(pr_global_struct->self));
-		Host_Error ("PR_ExecuteProgram: NULL function");
+		Host_Error ("PR_ExecuteProgram: %s", errormessage);
 	}
 	
 	f = &pr_functions[fnum];
diff --git a/progs.h b/progs.h
index 2d39385a..278447b2 100644
--- a/progs.h
+++ b/progs.h
@@ -110,7 +110,7 @@ extern	int				pr_edictareasize; // LordHavoc: for bounds checking
 
 void PR_Init (void);
 
-void PR_ExecuteProgram (func_t fnum);
+void PR_ExecuteProgram (func_t fnum, char *errormessage);
 void PR_LoadProgs (void);
 
 void PR_Profile_f (void);
diff --git a/r_light.c b/r_light.c
index 04504d21..7d259b76 100644
--- a/r_light.c
+++ b/r_light.c
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 
 cvar_t r_lightmodels = {"r_lightmodels", "1"};
+cvar_t r_modelsdonttransformnormals = {"r_modelsdonttransformnormals", "0"};
 
 void r_light_start()
 {
@@ -34,6 +35,7 @@ void r_light_shutdown()
 void R_Light_Init()
 {
 	Cvar_RegisterVariable(&r_lightmodels);
+	Cvar_RegisterVariable(&r_modelsdonttransformnormals);
 	R_RegisterModule("R_Light", r_light_start, r_light_shutdown);
 }
 
@@ -81,17 +83,27 @@ R_MarkLights
 */
 void R_OldMarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex, mnode_t *node)
 {
-	float		dist;
+	float		ndist, maxdist;
 	msurface_t	*surf;
 	int			i;
 
+	if (!r_dynamic.value)
+		return;
+
+	// for comparisons to minimum acceptable light
+	maxdist = light->radius * light->radius * LIGHTSCALE;
+
+	// clamp radius to avoid exceeding 32768 entry division table
+	if (maxdist > 4194304)
+		maxdist = 4194304;
+
 loc0:
 	if (node->contents < 0)
 		return;
 
-	dist = PlaneDiff(lightorigin, node->plane);
+	ndist = PlaneDiff(lightorigin, node->plane);
 	
-	if (dist > light->radius)
+	if (ndist > light->radius)
 	{
 		if (node->children[0]->contents >= 0) // LordHavoc: save some time by not pushing another stack frame
 		{
@@ -100,7 +112,7 @@ loc0:
 		}
 		return;
 	}
-	if (dist < -light->radius)
+	if (ndist < -light->radius)
 	{
 		if (node->children[1]->contents >= 0) // LordHavoc: save some time by not pushing another stack frame
 		{
@@ -121,6 +133,69 @@ loc0:
 	surf = cl.worldmodel->surfaces + node->firstsurface;
 	for (i=0 ; i<node->numsurfaces ; i++, surf++)
 	{
+		int d;
+		float dist, dist2, impact[3];
+		dist = ndist;
+		if (surf->flags & SURF_PLANEBACK)
+			dist = -dist;
+
+		if (dist < -0.25f)
+			continue;
+
+		dist2 = dist * dist;
+		if (dist2 >= maxdist)
+			continue;
+
+		impact[0] = light->origin[0] - surf->plane->normal[0] * dist;
+		impact[1] = light->origin[1] - surf->plane->normal[1] * dist;
+		impact[2] = light->origin[2] - surf->plane->normal[2] * dist;
+
+		d = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+
+		if (d < 0)
+		{
+			dist2 += d * d;
+			if (dist2 >= maxdist)
+				continue;
+		}
+		else
+		{
+			d -= surf->extents[0] + 16;
+			if (d > 0)
+			{
+				dist2 += d * d;
+				if (dist2 >= maxdist)
+					continue;
+			}
+		}
+
+		d = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+
+		if (d < 0)
+		{
+			dist2 += d * d;
+			if (dist2 >= maxdist)
+				continue;
+		}
+		else
+		{
+			d -= surf->extents[1] + 16;
+			if (d > 0)
+			{
+				dist2 += d * d;
+				if (dist2 >= maxdist)
+					continue;
+			}
+		}
+
+		if (surf->dlightframe != r_dlightframecount) // not dynamic until now
+		{
+			surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
+			surf->dlightframe = r_dlightframecount;
+		}
+		surf->dlightbits[bitindex] |= bit;
+
+		/*
 		if (((surf->flags & SURF_PLANEBACK) == 0) == ((PlaneDist(lightorigin, surf->plane)) >= surf->plane->dist))
 		{
 			if (surf->dlightframe != r_dlightframecount) // not dynamic until now
@@ -130,6 +205,7 @@ loc0:
 			}
 			surf->dlightbits[bitindex] |= bit;
 		}
+		*/
 	}
 
 	if (node->children[0]->contents >= 0)
@@ -163,6 +239,9 @@ void R_VisMarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex
 {
 	mleaf_t *pvsleaf = Mod_PointInLeaf (lightorigin, model);
 
+	if (!r_dynamic.value)
+		return;
+
 	if (!pvsleaf->compressed_vis)
 	{	// no vis info, so make all visible
 		R_OldMarkLights(lightorigin, light, bit, bitindex, model->nodes + model->hulls[0].firstclipnode);
@@ -175,12 +254,20 @@ void R_VisMarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex
 		mleaf_t *leaf;
 		byte	*in = pvsleaf->compressed_vis;
 		int		row = (model->numleafs+7)>>3;
-		float	low[3], high[3], radius;
+		float	low[3], high[3], radius, dist, maxdist;
+
+		radius = light->radius * LIGHTSCALE1;
+
+		// clamp radius to avoid exceeding 32768 entry division table
+		if (radius > 2048)
+			radius = 2048;
 
-		radius = light->radius * 4.0f;
 		low[0] = lightorigin[0] - radius;low[1] = lightorigin[1] - radius;low[2] = lightorigin[2] - radius;
 		high[0] = lightorigin[0] + radius;high[1] = lightorigin[1] + radius;high[2] = lightorigin[2] + radius;
 
+		// for comparisons to minimum acceptable light
+		maxdist = radius*radius;
+
 		lightframe++;
 		k = 0;
 		while (k < row)
@@ -202,12 +289,10 @@ void R_VisMarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex
 						if (leaf->contents == CONTENTS_SOLID)
 							continue;
 						// if out of the light radius, skip
-						/*
 						if (leaf->minmaxs[0] > high[0] || leaf->minmaxs[3] < low[0]
 						 || leaf->minmaxs[1] > high[1] || leaf->minmaxs[4] < low[1]
 						 || leaf->minmaxs[2] > high[2] || leaf->minmaxs[5] < low[2])
 							continue;
-						*/
 						if (leaf->dlightframe != r_dlightframecount) // not dynamic until now
 						{
 							leaf->dlightbits[0] = leaf->dlightbits[1] = leaf->dlightbits[2] = leaf->dlightbits[3] = leaf->dlightbits[4] = leaf->dlightbits[5] = leaf->dlightbits[6] = leaf->dlightbits[7] = 0;
@@ -223,8 +308,59 @@ void R_VisMarkLights (vec3_t lightorigin, dlight_t *light, int bit, int bitindex
 								if (surf->visframe != r_framecount || surf->lightframe == lightframe)
 									continue;
 								surf->lightframe = lightframe;
-								if (((surf->flags & SURF_PLANEBACK) == 0) == ((PlaneDist(lightorigin, surf->plane)) >= surf->plane->dist))
+								dist = PlaneDiff(lightorigin, surf->plane);
+								if (surf->flags & SURF_PLANEBACK)
+									dist = -dist;
+								// LordHavoc: make sure it is infront of the surface and not too far away
+								if (dist >= -0.25f && dist < radius)
 								{
+									int d;
+									float dist2, impact[3];
+
+									dist2 = dist * dist;
+
+									impact[0] = light->origin[0] - surf->plane->normal[0] * dist;
+									impact[1] = light->origin[1] - surf->plane->normal[1] * dist;
+									impact[2] = light->origin[2] - surf->plane->normal[2] * dist;
+
+									d = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
+
+									if (d < 0)
+									{
+										dist2 += d * d;
+										if (dist2 >= maxdist)
+											continue;
+									}
+									else
+									{
+										d -= surf->extents[0] + 16;
+										if (d > 0)
+										{
+											dist2 += d * d;
+											if (dist2 >= maxdist)
+												continue;
+										}
+									}
+
+									d = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
+
+									if (d < 0)
+									{
+										dist2 += d * d;
+										if (dist2 >= maxdist)
+											continue;
+									}
+									else
+									{
+										d -= surf->extents[1] + 16;
+										if (d > 0)
+										{
+											dist2 += d * d;
+											if (dist2 >= maxdist)
+												continue;
+										}
+									}
+
 									if (surf->dlightframe != r_dlightframecount) // not dynamic until now
 									{
 										surf->dlightbits[0] = surf->dlightbits[1] = surf->dlightbits[2] = surf->dlightbits[3] = surf->dlightbits[4] = surf->dlightbits[5] = surf->dlightbits[6] = surf->dlightbits[7] = 0;
@@ -625,6 +761,7 @@ void R_CompleteLightPoint (vec3_t color, vec3_t p)
 }
 
 extern float *aliasvert;
+extern float *modelaliasvert;
 extern float *aliasvertnorm;
 extern byte *aliasvertcolor;
 extern float modelalpha;
@@ -681,7 +818,7 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 	basecolor[0] *= mod[0];
 	basecolor[1] *= mod[1];
 	basecolor[2] *= mod[2];
-	if (r_lightmodels.value)
+	if (!r_lightmodels.value)
 	{
 		for (i = 0;i < MAX_DLIGHTS;i++)
 		{
@@ -694,20 +831,28 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 				continue;
 			VectorSubtract (center, cl_dlights[i].origin, dist);
 			t1 = cl_dlights[i].radius*cl_dlights[i].radius*LIGHTSCALE;
-			t2 = DotProduct(dist,dist) + LIGHTOFFSET;
+			t2 = DotProduct(dist,dist) * (1.0f / LIGHTSCALE) + LIGHTOFFSET;
 			if (t2 < t1)
 			{
-				VectorCopy(cl_dlights[i].origin, nearlight[nearlights].origin);
-				nearlight[nearlights].color[0] = cl_dlights[i].color[0] * cl_dlights[i].radius * cl_dlights[i].radius * mod[0];
-				nearlight[nearlights].color[1] = cl_dlights[i].color[1] * cl_dlights[i].radius * cl_dlights[i].radius * mod[1];
-				nearlight[nearlights].color[2] = cl_dlights[i].color[2] * cl_dlights[i].radius * cl_dlights[i].radius * mod[2];
-				t1 = (128.0f / LIGHTSCALE2) / t2;
-				basecolor[0] += nearlight[nearlights].color[0] * t1;
-				basecolor[1] += nearlight[nearlights].color[1] * t1;
-				basecolor[2] += nearlight[nearlights].color[2] * t1;
-				nearlights++;
+				dist[0] = cl_dlights[i].color[0] * t1 * mod[0];
+				dist[1] = cl_dlights[i].color[1] * t1 * mod[1];
+				dist[2] = cl_dlights[i].color[2] * t1 * mod[2];
+				t1 = (224.0f / LIGHTSCALE / LIGHTSCALE) / t2;
+				basecolor[0] += dist[0] * t1;
+				basecolor[1] += dist[1] * t1;
+				basecolor[2] += dist[2] * t1;
 			}
 		}
+		((byte *)&color)[0] = bound(0, basecolor[0], 255);
+		((byte *)&color)[1] = bound(0, basecolor[1], 255);
+		((byte *)&color)[2] = bound(0, basecolor[2], 255);
+		((byte *)&color)[3] = a;
+		for (i = 0;i < numverts;i++)
+		{
+			*((int *)avc) = color;
+			avc += 4;
+		}
+		return;
 	}
 	else
 	{
@@ -722,16 +867,23 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 				continue;
 			VectorSubtract (center, cl_dlights[i].origin, dist);
 			t1 = cl_dlights[i].radius*cl_dlights[i].radius*LIGHTSCALE;
-			t2 = DotProduct(dist,dist) + LIGHTOFFSET;
+			t2 = DotProduct(dist,dist) * (1.0f / LIGHTSCALE) + LIGHTOFFSET;
 			if (t2 < t1)
 			{
-				dist[0] = cl_dlights[i].color[0] * cl_dlights[i].radius * cl_dlights[i].radius * mod[0];
-				dist[1] = cl_dlights[i].color[1] * cl_dlights[i].radius * cl_dlights[i].radius * mod[1];
-				dist[2] = cl_dlights[i].color[2] * cl_dlights[i].radius * cl_dlights[i].radius * mod[2];
-				t1 = (224.0f / LIGHTSCALE2) / t2;
-				basecolor[0] += dist[0] * t1;
-				basecolor[1] += dist[1] * t1;
-				basecolor[2] += dist[2] * t1;
+				if (r_modelsdonttransformnormals.value)
+					softwareuntransform(cl_dlights[i].origin, nearlight[nearlights].origin);
+				else
+				{
+					VectorCopy(cl_dlights[i].origin, nearlight[nearlights].origin);
+				}
+				nearlight[nearlights].color[0] = cl_dlights[i].color[0] * t1 * mod[0];
+				nearlight[nearlights].color[1] = cl_dlights[i].color[1] * t1 * mod[1];
+				nearlight[nearlights].color[2] = cl_dlights[i].color[2] * t1 * mod[2];
+//				t1 = (128.0f / LIGHTSCALE2) / t2;
+//				basecolor[0] += nearlight[nearlights].color[0] * t1;
+//				basecolor[1] += nearlight[nearlights].color[1] * t1;
+//				basecolor[2] += nearlight[nearlights].color[2] * t1;
+				nearlights++;
 			}
 		}
 	}
@@ -747,7 +899,10 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 		int temp;
 		vec3_t v;
 		float *av;
-		av = aliasvert;
+		if (r_modelsdonttransformnormals.value)
+			av = modelaliasvert;
+		else
+			av = aliasvert;
 		if (nearlights == 1)
 		{
 			for (i = 0;i < numverts;i++)
@@ -765,8 +920,8 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 				else
 					*((int *)avc) = color;
 				avc += 4;
-				av+=3;
-				avn+=3;
+				av += 3;
+				avn += 3;
 			}
 		}
 		else
@@ -791,16 +946,18 @@ void R_LightModel(int numverts, vec3_t center, vec3_t basecolor)
 						k = true;
 					}
 				}
-				if (k) // dodge the costly float -> int conversions
+				if (k)
 				{
 					i1 = t1;if (i1 < 0) i1 = 0;else if (i1 > 255) i1 = 255;avc[0] = i1;
 					i2 = t2;if (i2 < 0) i2 = 0;else if (i2 > 255) i2 = 255;avc[1] = i2;
 					i3 = t3;if (i3 < 0) i3 = 0;else if (i3 > 255) i3 = 255;avc[2] = i3;
 					avc[3] = a;
 				}
-				else
+				else // dodge the costly float -> int conversions
 					*((int *)avc) = color;
 				avc += 4;
+				av += 3;
+				avn += 3;
 			}
 		}
 	}
diff --git a/r_light.h b/r_light.h
index bd8167be..509fe644 100644
--- a/r_light.h
+++ b/r_light.h
@@ -13,6 +13,9 @@ typedef struct
 } dlight_t;
 
 // LordHavoc: this affects the lighting scale of the whole game
-#define LIGHTOFFSET 16384.0f
-#define LIGHTSCALE 4.0f
-#define LIGHTSCALE2 4.0f
+//#define LIGHTOFFSET 16384.0f
+//#define LIGHTSCALE1 2.0f
+#define LIGHTOFFSET 4096.0f
+#define LIGHTSCALE1 1.0f
+#define LIGHTSCALE (LIGHTSCALE1*LIGHTSCALE1)
+#define LIGHTSCALE2 LIGHTSCALE
diff --git a/r_part.c b/r_part.c
index 7f8627c9..1acfbc20 100644
--- a/r_part.c
+++ b/r_part.c
@@ -24,9 +24,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define ABSOLUTE_MIN_PARTICLES	512		// no fewer than this no matter what's on the command line
 
 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
-typedef enum {
-	pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash
-} ptype_t;
+typedef enum
+{
+	pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash, pt_flame, pt_flamingdebris, pt_smokingdebris, pt_flamefall
+}
+ptype_t;
 
 typedef struct particle_s
 {
@@ -37,11 +39,15 @@ typedef struct particle_s
 	ptype_t		type;
 	float		scale;
 	short		texnum;
+	short		dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
 	float		alpha; // 0-255
 	float		time2; // used for various things (snow fluttering, for example)
 	vec3_t		oldorg;
 	vec3_t		vel2; // used for snow fluttering (base velocity, wind for instance)
-} particle_t;
+}
+particle_t;
+
+float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
 
 int		ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
 int		ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
@@ -51,6 +57,8 @@ int		particletexture;
 int		smokeparticletexture[8];
 int		rainparticletexture;
 int		bubbleparticletexture;
+int		explosiontexture;
+int		explosiontexturefog;
 
 particle_t	*particles;
 int			r_numparticles;
@@ -61,12 +69,13 @@ int			numparticles;
 particle_t	**freeparticles; // list used only in compacting particles array
 
 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
-#define ALLOCPARTICLE \
+#define ALLOCPARTICLE(part) \
 	if (numparticles >= r_numparticles)\
 		return;\
-	p = &particles[numparticles++];
+	(part) = &particles[numparticles++];
 
 cvar_t r_particles = {"r_particles", "1"};
+cvar_t r_drawparticles = {"r_drawparticles", "1"};
 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
 
 byte shadebubble(float dx, float dy, vec3_t light)
@@ -191,11 +200,13 @@ void r_part_start()
 {
 	particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
 	freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
+	numparticles = 0;
 	R_InitParticleTexture ();
 }
 
 void r_part_shutdown()
 {
+	numparticles = 0;
 	qfree(particles);
 	qfree(freeparticles);
 }
@@ -226,118 +237,66 @@ void R_Particles_Init (void)
 	Cmd_AddCommand ("pointfile", R_ReadPointFile_f);	
 
 	Cvar_RegisterVariable (&r_particles);
+	Cvar_RegisterVariable (&r_drawparticles);
 	Cvar_RegisterVariable (&r_dynamicparticles);
 
 	R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
 }
 
-#define particle(ptype, pcolor, ptex, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
+#define particle(ptype, pcolor, ptex, plight, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
 {\
-	particle_t	*p;\
-	ALLOCPARTICLE\
-	p->type = (ptype);\
-	p->color = (pcolor);\
-	p->texnum = (ptex);\
-	p->scale = (pscale);\
-	p->alpha = (palpha);\
-	p->die = cl.time + (ptime);\
-	p->org[0] = (px);\
-	p->org[1] = (py);\
-	p->org[2] = (pz);\
-	p->vel[0] = (pvx);\
-	p->vel[1] = (pvy);\
-	p->vel[2] = (pvz);\
+	particle_t	*part;\
+	ALLOCPARTICLE(part)\
+	part->type = (ptype);\
+	part->color = (pcolor);\
+	part->texnum = (ptex);\
+	part->dynlight = (plight);\
+	part->scale = (pscale);\
+	part->alpha = (palpha);\
+	part->die = cl.time + (ptime);\
+	part->org[0] = (px);\
+	part->org[1] = (py);\
+	part->org[2] = (pz);\
+	part->vel[0] = (pvx);\
+	part->vel[1] = (pvy);\
+	part->vel[2] = (pvz);\
 }
-#define particle2(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, poscale, pvscale)\
+#define particle2(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, poscale, pvscale)\
 {\
-	particle_t	*p;\
-	ALLOCPARTICLE\
-	p->type = (ptype);\
-	p->color = (pcolor);\
-	p->texnum = (ptex);\
-	p->scale = (pscale);\
-	p->alpha = (palpha);\
-	p->die = cl.time + (ptime);\
-	p->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
-	p->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
-	p->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
-	p->vel[0] = lhrandom(-(pvscale), (pvscale));\
-	p->vel[1] = lhrandom(-(pvscale), (pvscale));\
-	p->vel[2] = lhrandom(-(pvscale), (pvscale));\
+	particle_t	*part;\
+	ALLOCPARTICLE(part)\
+	part->type = (ptype);\
+	part->color = (pcolor);\
+	part->texnum = (ptex);\
+	part->dynlight = (plight);\
+	part->scale = (pscale);\
+	part->alpha = (palpha);\
+	part->die = cl.time + (ptime);\
+	part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
+	part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
+	part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
+	part->vel[0] = lhrandom(-(pvscale), (pvscale));\
+	part->vel[1] = lhrandom(-(pvscale), (pvscale));\
+	part->vel[2] = lhrandom(-(pvscale), (pvscale));\
 }
-#define particle3(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
+#define particle3(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
 {\
-	particle_t	*p;\
-	ALLOCPARTICLE\
-	p->type = (ptype);\
-	p->color = (pcolor);\
-	p->texnum = (ptex);\
-	p->scale = (pscale);\
-	p->alpha = (palpha);\
-	p->die = cl.time + (ptime);\
-	p->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
-	p->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
-	p->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
-	p->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
-	p->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
-	p->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
+	particle_t	*part;\
+	ALLOCPARTICLE(part)\
+	part->type = (ptype);\
+	part->color = (pcolor);\
+	part->texnum = (ptex);\
+	part->dynlight = (plight);\
+	part->scale = (pscale);\
+	part->alpha = (palpha);\
+	part->die = cl.time + (ptime);\
+	part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
+	part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
+	part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
+	part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
+	part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
+	part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
 }
-/*
-void particle(int type, int color, int tex, float scale, int alpha, float time, float x, float y, float z, float vx, float vy, float vz)
-{
-	particle_t	*p;
-	ALLOCPARTICLE
-
-	p->type = type;
-	p->color = color;
-	p->texnum = tex;
-	p->scale = scale;
-	p->alpha = alpha;
-	p->die = cl.time + time;
-	p->org[0] = x;
-	p->org[1] = y;
-	p->org[2] = z;
-	p->vel[0] = vx;
-	p->vel[1] = vy;
-	p->vel[2] = vz;
-}
-void particle2(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float oscale, float vscale)
-{
-	particle_t	*p;
-	ALLOCPARTICLE
-
-	p->type = type;
-	p->color = color;
-	p->texnum = tex;
-	p->scale = scale;
-	p->alpha = alpha;
-	p->die = cl.time + time;
-	p->org[0] = lhrandom(-oscale, oscale) + base[0];
-	p->org[1] = lhrandom(-oscale, oscale) + base[1];
-	p->org[2] = lhrandom(-oscale, oscale) + base[2];
-	p->vel[0] = lhrandom(-vscale, vscale);
-	p->vel[1] = lhrandom(-vscale, vscale);
-	p->vel[2] = lhrandom(-vscale, vscale);
-}
-void particle3(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float scalex, float scaley, float scalez, float vscalex, float vscaley, float vscalez)
-{
-	particle_t	*p;
-	ALLOCPARTICLE
-
-	p->type = type;
-	p->color = color;
-	p->texnum = tex;
-	p->scale = scale;
-	p->alpha = alpha;
-	p->die = cl.time + time;
-	p->org[0] = lhrandom(-scalex, scalex) + base[0];
-	p->org[1] = lhrandom(-scaley, scaley) + base[1];
-	p->org[2] = lhrandom(-scalez, scalez) + base[2];
-	p->vel[0] = lhrandom(-vscalex, vscalex);
-	p->vel[1] = lhrandom(-vscaley, vscaley);
-	p->vel[2] = lhrandom(-vscalez, vscalez);
-}
-*/
 
 /*
 ===============
@@ -383,7 +342,7 @@ void R_EntityParticles (entity_t *ent)
 		forward[1] = cp*sy;
 		forward[2] = -sp;
 
-		particle(pt_static, 0x6f, particletexture, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
+		particle(pt_static, 0x6f, particletexture, false, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
 	}
 }
 
@@ -438,7 +397,7 @@ void R_ReadPointFile_f (void)
 			Con_Printf ("Not enough free particles\n");
 			break;
 		}
-		particle(pt_static, (-c)&15, particletexture, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
+		particle(pt_static, (-c)&15, particletexture, false, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
 	}
 
 	fclose (f);
@@ -480,23 +439,58 @@ R_ParticleExplosion
 */
 void R_ParticleExplosion (vec3_t org, int smoke)
 {
-	int			i;
+	int i;
 	if (!r_particles.value) return; // LordHavoc: particles are optional
 
-	particle(pt_smokecloud, (rand()&7) + 8, smokeparticletexture[rand()&7], 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
+//	particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
 
 	i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
 	if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
 	{
 		for (i=0 ; i<128 ; i++)
-			particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, lhrandom(1, 2), 255, 2, org, 16, 96);
+			particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, false, lhrandom(1, 2), 255, 2, org, 16, 96);
 	}
+	else
+		R_NewExplosion(org);
+	/*
 	else
 	{
-		for (i = 0;i < 256;i++)
-			particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
+		int j;
+//		int color;
+		float f, forg[3], fvel[3], fvel2[3];
+//		for (i = 0;i < 256;i++)
+//			particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
+//		for (i = 0;i < 256;i++)
+//			particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, org, 15, 150);
+		for (i = 0;i < 32;i++)
+		{
+			fvel[0] = lhrandom(-150, 150);
+			fvel[1] = lhrandom(-150, 150);
+			fvel[2] = lhrandom(-150, 150) + 80;
+//			particle(pt_flamefall, 106 + (rand()%6), particletexture, false, 3, 255, 5, forg[0] + lhrandom(-5, 5), forg[1] + lhrandom(-5, 5), forg[2] + lhrandom(-5, 5), fvel2[0], fvel2[1], fvel2[2]);
+			for (j = 0;j < 64;j++)
+			{
+				forg[0] = lhrandom(-20, 20) + org[0];
+				forg[1] = lhrandom(-20, 20) + org[1];
+				forg[2] = lhrandom(-20, 20) + org[2];
+				fvel2[0] = fvel[0] + lhrandom(-30, 30);
+				fvel2[1] = fvel[1] + lhrandom(-30, 30);
+				fvel2[2] = fvel[2] + lhrandom(-30, 30);
+				f = lhrandom(0.2, 1);
+				fvel2[0] *= f;
+				fvel2[1] *= f;
+				fvel2[2] *= f;
+				particle(pt_flamefall, 106 + (rand()%6), particletexture, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
+			}
+		}
+//		for (i = 0;i < 16;i++)
+//			particle2(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 20, 192, 99, org, 20, 0);
+//		for (i = 0;i < 50;i++)
+//			particle2(pt_flamingdebris, ramp3[rand()%6], particletexture, false, 3, 255, 99, org, 10, 200);
+//		for (i = 0;i < 30;i++)
+//			particle2(pt_smokingdebris, 10 + (rand()%6), particletexture, false, 2, 255, 99, org, 10, 100);
 	}
-
+	*/
 }
 
 /*
@@ -511,7 +505,7 @@ void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
 	if (!r_particles.value) return; // LordHavoc: particles are optional
 
 	for (i = 0;i < 512;i++)
-		particle2(pt_fade, colorStart + (i % colorLength), particletexture, 1.5, 255, 0.3, org, 8, 192);
+		particle2(pt_fade, colorStart + (i % colorLength), particletexture, false, 1.5, 255, 0.3, org, 8, 192);
 }
 
 /*
@@ -526,9 +520,9 @@ void R_BlobExplosion (vec3_t org)
 	if (!r_particles.value) return; // LordHavoc: particles are optional
 	
 	for (i=0 ; i<512 ; i++)
-		particle3(pt_blob, 66+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
+		particle3(pt_blob, 66+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
 	for (i=0 ; i<512 ; i++)
-		particle3(pt_blob2, 150+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
+		particle3(pt_blob2, 150+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
 }
 
 /*
@@ -549,12 +543,12 @@ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
 	color &= ~7;
 	if (count & 7)
 	{
-		particle2(pt_fade, color + (rand()&7), particletexture, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
+		particle2(pt_fade, color + (rand()&7), particletexture, false, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
 		count &= ~7;
 	}
 	count >>= 3;
 	while (count--)
-		particle2(pt_fade, color + (rand()&7), particletexture, 6, 128, 1, org, 8, 15);
+		particle2(pt_fade, color + (rand()&7), particletexture, false, 6, 128, 1, org, 8, 15);
 }
 
 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
@@ -568,20 +562,20 @@ void R_SparkShower (vec3_t org, vec3_t dir, int count)
 	if (!r_particles.value) return; // LordHavoc: particles are optional
 
 	// smoke puff
-	particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
+	particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
 	// sparks
 	while(count--)
-//		particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, org, 4, 96);
-		particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
+//		particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, org, 4, 96);
+		particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
 }
 
 void R_BloodPuff (vec3_t org)
 {
 	if (!r_particles.value) return; // LordHavoc: particles are optional
 
-	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
-	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
-	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
+	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
+	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
+	particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
 }
 
 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
@@ -604,9 +598,10 @@ void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
 	
 	while (count--)
 	{
-		ALLOCPARTICLE
+		ALLOCPARTICLE(p)
 
 		p->texnum = smokeparticletexture[rand()&7];
+		p->dynlight = true;
 		p->scale = lhrandom(4, 6);
 		p->alpha = 96 + (rand()&63);
 		p->die = cl.time + 2;
@@ -635,9 +630,10 @@ void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorb
 	
 	while (count--)
 	{
-		ALLOCPARTICLE
+		ALLOCPARTICLE(p)
 
 		p->texnum = particletexture;
+		p->dynlight = false;
 		p->scale = 6;
 		p->alpha = 255;
 		p->die = cl.time + 1 + (rand()&15)*0.0625;
@@ -687,7 +683,7 @@ void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorb
 	
 	for (i=0 ; i<count ; i++)
 	{
-		ALLOCPARTICLE
+		ALLOCPARTICLE(p)
 
 		vel[0] = dir[0] + (rand()&31) - 16;
 		vel[1] = dir[1] + (rand()&31) - 16;
@@ -702,12 +698,14 @@ void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorb
 		{
 			p->scale = 2;
 			p->texnum = particletexture;
+			p->dynlight = false;
 			p->type = pt_snow;
 		}
 		else // 0
 		{
 			p->scale = 3;
 			p->texnum = rainparticletexture;
+			p->dynlight = true;
 			p->type = pt_rain;
 		}
 		p->color = colorbase + (rand()&3);
@@ -735,9 +733,10 @@ void R_LavaSplash (vec3_t org)
 	for (i=-128 ; i<128 ; i+=16)
 		for (j=-128 ; j<128 ; j+=16)
 		{
-			ALLOCPARTICLE
+			ALLOCPARTICLE(p)
 		
 			p->texnum = particletexture;
+			p->dynlight = false;
 			p->scale = 10;
 			p->alpha = 128;
 			p->die = cl.time + 2 + (rand()&31) * 0.02;
@@ -774,9 +773,10 @@ void R_TeleportSplash (vec3_t org)
 		for (j=-16 ; j<16 ; j+=8)
 			for (k=-24 ; k<32 ; k+=8)
 			{
-				ALLOCPARTICLE
+				ALLOCPARTICLE(p)
 		
 				p->texnum = particletexture;
+				p->dynlight = false;
 				p->scale = 1;
 				p->alpha = lhrandom(32,128);
 				p->die = cl.time + 5;
@@ -824,7 +824,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 
 	while (t < nt)
 	{
-		ALLOCPARTICLE
+		ALLOCPARTICLE(p)
 		
 		p->vel[0] = p->vel[1] = p->vel[2] = 0;
 		p->die = cl.time + 2;
@@ -837,6 +837,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 				{
 					dec = type == 0 ? 0.01f : 0.02f;
 					p->texnum = bubbleparticletexture;
+					p->dynlight = false;
 					p->scale = lhrandom(1,2);
 					p->alpha = 255;
 					p->color = 254;
@@ -850,21 +851,24 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 				}
 				else
 				{
-					dec = type == 0 ? 0.01f : 0.02f;
+					dec = type == 0 ? 0.02f : 0.04f;
 					p->texnum = smokeparticletexture[rand()&7];
+					p->dynlight = true;
 					p->scale = lhrandom(4, 8);
 					p->alpha = 160; //128 + (rand()&63);
 					p->color = 254;
 					p->type = pt_smoke;
 					p->die = cl.time + 10000;
 					VectorCopy(start, p->org);
+					/*
 					if (type == 0)
 					{
-						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
-						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
-						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
-						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
+						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
+						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
+						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
+						particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
 					}
+					*/
 				}
 				break;
 
@@ -872,6 +876,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 			case 1:	// smoke smoke
 				dec = 0.016f;
 				p->texnum = smokeparticletexture;
+				p->dynlight = true;
 				p->scale = lhrandom(6,9);
 				p->alpha = 64;
 				if (r_smokecolor.value)
@@ -888,6 +893,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 			case 4:	// slight blood
 				dec = 0.025f;
 				p->texnum = smokeparticletexture[rand()&7];
+				p->dynlight = true;
 				p->scale = lhrandom(4, 6);
 				p->alpha = type == 4 ? 192 : 255;
 				p->color = 247; //(rand()&3)+68;
@@ -904,6 +910,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 			case 5:	// tracer
 				dec = 0.02f;
 				p->texnum = smokeparticletexture[rand()&7];
+				p->dynlight = false;
 				p->scale = 4;
 				p->alpha = 64 + (rand()&31);
 				p->color = type == 3 ? 56 : 234;
@@ -915,6 +922,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 			case 6:	// voor trail
 				dec = 0.05f; // sparse trail
 				p->texnum = smokeparticletexture[rand()&7];
+				p->dynlight = false;
 				p->scale = lhrandom(3, 5);
 				p->alpha = 255;
 				p->color = 9*16 + 8 + (rand()&3);
@@ -930,6 +938,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
 			case 7:	// Nehahra smoke tracer
 				dec = 0.14f;
 				p->texnum = smokeparticletexture[rand()&7];
+				p->dynlight = true;
 				p->scale = lhrandom(8, 12);
 				p->alpha = 64;
 				p->color = (rand()&3)+12;
@@ -958,7 +967,7 @@ void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
 	VectorScale(vec, 3, vec);
 	while (len--)
 	{
-		particle(pt_smoke, color, particletexture, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
+		particle(pt_smoke, color, particletexture, false, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
 		VectorAdd (start, vec, start);
 	}
 }
@@ -971,8 +980,6 @@ R_DrawParticles
 */
 extern	cvar_t	sv_gravity;
 
-void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
-
 void R_MoveParticles (void)
 {
 	particle_t		*p;
@@ -998,8 +1005,6 @@ void R_MoveParticles (void)
 			freeparticles[j++] = p;
 			continue;
 		}
-		maxparticle = i;
-		activeparticles++;
 
 		VectorCopy(p->org, p->oldorg);
 		p->org[0] += p->vel[0]*frametime;
@@ -1089,6 +1094,7 @@ void R_MoveParticles (void)
 			a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
 			if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
 			{
+				vec3_t normal;
 				if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
 					break; // still in solid
 				p->die = cl.time + 1000;
@@ -1112,7 +1118,7 @@ void R_MoveParticles (void)
 //					p->vel[2] *= 0.1;
 					break;
 				default: // CONTENTS_SOLID and any others
-					TraceLine(p->oldorg, p->org, v);
+					TraceLine(p->oldorg, p->org, v, normal);
 					VectorCopy(v, p->org);
 					p->texnum = smokeparticletexture[rand()&7];
 					p->type = pt_splash;
@@ -1124,6 +1130,35 @@ void R_MoveParticles (void)
 				}
 			}
 			break;
+		case pt_flame:
+			p->alpha -= frametime * 512;
+			break;
+		case pt_flamingdebris:
+			if (cl.time >= p->time2)
+			{
+				p->time2 = cl.time + 0.01;
+				particle2(pt_flame, p->color, particletexture, false, 4, p->alpha, 999, p->org, 0, 50);
+			}
+			p->alpha -= frametime * 512;
+			p->vel[2] -= gravity * 0.5f;
+			if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
+				p->die = -1;
+			break;
+		case pt_smokingdebris:
+			if (cl.time >= p->time2)
+			{
+				p->time2 = cl.time + 0.01;
+				particle2(pt_flame, 15, smokeparticletexture[rand()&7], false, 4, p->alpha, 999, p->org, 0, 50);
+			}
+			p->alpha -= frametime * 512;
+			p->vel[2] -= gravity * 0.5f;
+			if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
+				p->die = -1;
+			break;
+		case pt_flamefall:
+			p->alpha -= frametime * 512;
+			p->vel[2] -= gravity * 0.5f;
+			break;
 		}
 
 		// LordHavoc: most particles did this check anyway, consistency...
@@ -1133,6 +1168,11 @@ void R_MoveParticles (void)
 		// LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
 		if (p->die < cl.time)
 			freeparticles[j++] = p;
+		else
+		{
+			maxparticle = i;
+			activeparticles++;
+		}
 	}
 	// fill in gaps to compact the array
 	i = 0;
@@ -1150,15 +1190,18 @@ void R_CompleteLightPoint (vec3_t color, vec3_t p);
 void R_DrawParticles (void)
 {
 	particle_t		*p;
-	int				i, r,g,b,a;
+	int				i, r,g,b,a, dynlight;
 	float			scale, scale2, minparticledist;
 	byte			*color24;
 	vec3_t			up, right, uprightangles, forward2, up2, right2, tempcolor;
 
 	// LordHavoc: early out condition
-	if (!numparticles)
+	if ((!numparticles) || (!r_drawparticles.value))
 		return;
 
+	dynlight = r_dynamicparticles.value;
+	if (!r_dynamic.value)
+		dynlight = 0;
 	c_particles += numparticles;
 
 	VectorScale (vup, 1.5, up);
@@ -1186,7 +1229,7 @@ void R_DrawParticles (void)
 		g = color24[1];
 		b = color24[2];
 		a = p->alpha;
-		if (r_dynamicparticles.value)
+		if (dynlight && (p->dynlight || dynlight >= 2)) // LordHavoc: only light blood and smoke
 		{
 			R_CompleteLightPoint(tempcolor, p->org);
 			r = (r * (int) tempcolor[0]) >> 7;
diff --git a/render.h b/render.h
index c713eb40..669b7b37 100644
--- a/render.h
+++ b/render.h
@@ -156,11 +156,15 @@ extern void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength);
 extern void R_LavaSplash (vec3_t org);
 extern void R_TeleportSplash (vec3_t org);
 
+extern void R_NewExplosion(vec3_t org);
+
 extern void R_PushDlights (void);
 extern void R_DrawWorld (void);
 //extern void R_RenderDlights (void);
 extern void R_DrawParticles (void);
 extern void R_MoveParticles (void);
+extern void R_DrawExplosions (void);
+extern void R_MoveExplosions (void);
 
 extern void R_DynamicLightPoint(vec3_t color, vec3_t org, int *dlightbits);
 extern void R_DynamicLightPointNoMask(vec3_t color, vec3_t org);
diff --git a/sv_main.c b/sv_main.c
index 8bf174b6..d282bcc3 100644
--- a/sv_main.c
+++ b/sv_main.c
@@ -280,7 +280,7 @@ void SV_ConnectClient (int clientnum)
 	else
 	{
 	// call the progs to get default spawn parms for the new client
-		PR_ExecuteProgram (pr_global_struct->SetNewParms);
+		PR_ExecuteProgram (pr_global_struct->SetNewParms, "QC function SetNewParms is missing");
 		for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
 			client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
 	}
@@ -1171,7 +1171,7 @@ void SV_SaveSpawnparms (void)
 
 	// call the progs to get default spawn parms for the new client
 		pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
-		PR_ExecuteProgram (pr_global_struct->SetChangeParms);
+		PR_ExecuteProgram (pr_global_struct->SetChangeParms, "QC function SetChangeParms is missing");
 		for (j=0 ; j<NUM_SPAWN_PARMS ; j++)
 			host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
 	}
diff --git a/sv_phys.c b/sv_phys.c
index 48af07c4..73b013df 100644
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -148,7 +148,7 @@ qboolean SV_RunThink (edict_t *ent)
 	pr_global_struct->time = thinktime;
 	pr_global_struct->self = EDICT_TO_PROG(ent);
 	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
-	PR_ExecuteProgram (ent->v.think);
+	PR_ExecuteProgram (ent->v.think, "NULL think function");
 	return !ent->free;
 }
 
@@ -171,14 +171,14 @@ void SV_Impact (edict_t *e1, edict_t *e2)
 	{
 		pr_global_struct->self = EDICT_TO_PROG(e1);
 		pr_global_struct->other = EDICT_TO_PROG(e2);
-		PR_ExecuteProgram (e1->v.touch);
+		PR_ExecuteProgram (e1->v.touch, "");
 	}
 	
 	if (e2->v.touch && e2->v.solid != SOLID_NOT)
 	{
 		pr_global_struct->self = EDICT_TO_PROG(e2);
 		pr_global_struct->other = EDICT_TO_PROG(e1);
-		PR_ExecuteProgram (e2->v.touch);
+		PR_ExecuteProgram (e2->v.touch, "");
 	}
 
 	pr_global_struct->self = old_self;
@@ -560,7 +560,7 @@ void SV_PushMove (edict_t *pusher, float movetime)
 				{
 					pr_global_struct->self = EDICT_TO_PROG(pusher);
 					pr_global_struct->other = EDICT_TO_PROG(check);
-					PR_ExecuteProgram (pusher->v.blocked);
+					PR_ExecuteProgram (pusher->v.blocked, "");
 				}
 				
 				// move back any entities we already moved
@@ -719,7 +719,7 @@ void SV_PushRotate (edict_t *pusher, float movetime)
 			{
 				pr_global_struct->self = EDICT_TO_PROG(pusher);
 				pr_global_struct->other = EDICT_TO_PROG(check);
-				PR_ExecuteProgram (pusher->v.blocked);
+				PR_ExecuteProgram (pusher->v.blocked, "");
 			}
 			
 		// move back any entities we already moved
@@ -773,7 +773,7 @@ void SV_Physics_Pusher (edict_t *ent)
 		pr_global_struct->time = sv.time;
 		pr_global_struct->self = EDICT_TO_PROG(ent);
 		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
-		PR_ExecuteProgram (ent->v.think);
+		PR_ExecuteProgram (ent->v.think, "NULL think function");
 		if (ent->free)
 			return;
 	}
@@ -1082,7 +1082,7 @@ void SV_Physics_Client (edict_t	*ent, int num)
 //	
 	pr_global_struct->time = sv.time;
 	pr_global_struct->self = EDICT_TO_PROG(ent);
-	PR_ExecuteProgram (pr_global_struct->PlayerPreThink);
+	PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
 	
 //
 // do a move
@@ -1138,7 +1138,7 @@ void SV_Physics_Client (edict_t	*ent, int num)
 
 	pr_global_struct->time = sv.time;
 	pr_global_struct->self = EDICT_TO_PROG(ent);
-	PR_ExecuteProgram (pr_global_struct->PlayerPostThink);
+	PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
 }
 
 //============================================================================
@@ -1392,7 +1392,7 @@ void SV_Physics (void)
 	pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
 	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
 	pr_global_struct->time = sv.time;
-	PR_ExecuteProgram (pr_global_struct->StartFrame);
+	PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
 
 //SV_CheckAllEnts ();
 
@@ -1463,7 +1463,7 @@ void SV_Physics (void)
 		pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
 		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
 		pr_global_struct->time = sv.time;
-		PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions));
+		PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
 	}
 
 	sv.time += host_frametime;
diff --git a/sv_user.c b/sv_user.c
index 8ddff994..e76f78f9 100644
--- a/sv_user.c
+++ b/sv_user.c
@@ -650,7 +650,7 @@ void SV_RunClients (void)
 			{
 				pr_global_struct->time = sv.time;
 				pr_global_struct->self = EDICT_TO_PROG(sv_player);
-				PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions));
+				PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions), "");
 			}
 			else
 				SV_ClientThink ();
diff --git a/transform.c b/transform.c
index b8db4c35..e025c7f3 100644
--- a/transform.c
+++ b/transform.c
@@ -68,6 +68,18 @@ void softwaretransform_docopy (vec3_t in, vec3_t out)
 	out[2] = in[2];
 }
 
+void softwareuntransform (vec3_t in, vec3_t out)
+{
+	vec3_t v;
+	float s = 1.0f / softwaretransform_scale;
+	v[0] = in[0] - softwaretransform_offset[0];
+	v[1] = in[1] - softwaretransform_offset[1];
+	v[2] = in[2] - softwaretransform_offset[2];
+	out[0] = (v[0] * softwaretransform_x[0] + v[1] * softwaretransform_x[1] + v[2] * softwaretransform_x[2]) * s;
+	out[1] = (v[0] * softwaretransform_y[0] + v[1] * softwaretransform_y[1] + v[2] * softwaretransform_y[2]) * s;
+	out[2] = (v[0] * softwaretransform_z[0] + v[1] * softwaretransform_z[1] + v[2] * softwaretransform_z[2]) * s;
+}
+
 // to save time on transforms, choose the appropriate function
 void softwaretransform_classify()
 {
diff --git a/transform.h b/transform.h
index a68de7d7..b33b7c67 100644
--- a/transform.h
+++ b/transform.h
@@ -13,3 +13,4 @@ extern void softwaretransformforentity (entity_t *e);
 extern void softwaretransformidentity ();
 extern void softwaretransformset (vec3_t origin, vec3_t angles, vec_t scale);
 extern void (*softwaretransform) (vec3_t in, vec3_t out);
+extern void softwareuntransform (vec3_t in, vec3_t out);
diff --git a/vid_wgl.c b/vid_wgl.c
index d19b1d33..c30a890d 100644
--- a/vid_wgl.c
+++ b/vid_wgl.c
@@ -694,6 +694,9 @@ void AppActivate(BOOL fActive, BOOL minimize)
 				ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);
 				ShowWindow(mainwindow, SW_SHOWNORMAL);
 			}
+
+			// LordHavoc: from dabb, fix for alt-tab bug in NVidia drivers
+			MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false);
 		}
 		else if ((modestate == MS_WINDOWED) && _windowed_mouse.value && key_dest == key_game)
 		{
diff --git a/world.c b/world.c
index ec9942e4..a2cc7e48 100644
--- a/world.c
+++ b/world.c
@@ -325,7 +325,7 @@ loc0:
 		pr_global_struct->self = EDICT_TO_PROG(touch);
 		pr_global_struct->other = EDICT_TO_PROG(ent);
 		pr_global_struct->time = sv.time;
-		PR_ExecuteProgram (touch->v.touch);
+		PR_ExecuteProgram (touch->v.touch, "");
 
 		pr_global_struct->self = old_self;
 		pr_global_struct->other = old_other;
@@ -748,10 +748,6 @@ loc0:
 		return true;		// empty
 	}
 
-	// LordHavoc: this can be eliminated by validating in the loader...  but Mercury told me not to bother
-	if (num < hull->firstclipnode || num > hull->lastclipnode)
-		Sys_Error ("SV_RecursiveHullCheck: bad node number");
-
 // find the point distances
 	node = hull->clipnodes + num;
 	plane = hull->planes + node->planenum;
-- 
2.39.5