// Shadows & lights
// Based, nay copied, from Slamb's exlight code in Titan...
/*
#include "shell.h"

#include "timer.h"
#include "exentity.h"
#include "exlight.h"
#include "explore.h"
#include "aidraw.h"
#include "maingame.h"

#include "maths.h"
#include "lscape.h"
*/


#define LIGHT_ZERO_CAMERA
#define MAP_SCALE_DEPTH_DOWN 3

#include "global.h"
#include "debris.h"
#include "lights.h"
#include "main.h"
#include "overlays.h"
#include "collide.h"
#include "map_draw.h"
#include "maths.h"
#include "bff_load.h"
#include "enemies.h"	// start hourglass
#include "charactr.h"	// hide-char

#ifdef LIGHT_ZERO_CAMERA
extern GsRVIEW2	mycamera;
#endif

#define PRINTF DB
#define TIMER_START(a)
#define TIMER_STOP(a)
#ifndef DREAMCAST
	#define calc_angle(a,b) utilCalcAngle((a),(b))
#endif
#define SQR(a) ((a) * (a))
#define fast_sqrt(a) utilSqrt(a)
int EXLITE_Sqrt(int num);

EXLIGHT_DATA_TYPE	*EXLIGHT_Data = NULL;

#define MAX_LIGHT_LIGHTS	3
#define MAX_SHADOW_LIGHTS	5

#define OVERFLOW_CHECKS

#ifdef DREAMCAST
extern int lines2D;
#endif

//struct _EXLIGHT_TYPE *actor_lightSource[MAX_LIGHT_LIGHTS];
struct _EXLIGHT_TYPE *actor_shadowSource[MAX_SHADOW_LIGHTS];

EXLIGHT_TYPE *triggering_light;

void EXLIGHT_Initialise(void)
{
	PRINTF("EXLIGHT_Initialise begun...\n");

	// SL: malloc the system data
	EXLIGHT_Data = MALLOC( sizeof(EXLIGHT_DATA_TYPE) );
	if(!EXLIGHT_Data)
		PRINTF("EEEEEEEK! MALLOC FOR EXLIGHT_Data FAILED!!!\n");

	// SL: blank it off.
	FMEMZERO(EXLIGHT_Data, sizeof(EXLIGHT_DATA_TYPE));

	// SL: setup the drej orbiting lights...
//	EXLIGHT_InitDrejOrbitingLights();

	EXLIGHT_Data->shadow_width = 80;
	// SL: write in default values for the ambient light...
	EXLIGHT_Data->ambient.r = EXLIGHT_Data->ambient.g = EXLIGHT_Data->ambient.b = 1024;

	// SL: get a pointer to the lightsource texture
//	EXLIGHT_Data->lightsrc = textureFindInBank(EXPLORE_Data->genericTextures, "LIGHTSRC");


	psiSetAmbient(512,512,512);	// norm is 1024, max is 4096
	triggering_light = NULL;

	// SL: all done!
	PRINTF("EXLIGHT_Initialise complete...\n");
}

void EXLIGHT_PostBFF()
{
	EXLIGHT_Data->lightsrc = textureFindCRCInAllBanks(utilStr2CRC("LIGHTSRC"));
	EXLIGHT_Data->shadow   = textureFindCRCInAllBanks(utilStr2CRC("SHADOW"));
	if(!EXLIGHT_Data->lightsrc)
		EXLIGHT_Data->lightsrc = EXLIGHT_Data->shadow;
}

void EXLIGHT_Shutdown(void)
{
int i;
	EXLIGHT_TYPE	*current, *next;

	PRINTF("EXLIGHT_Shutdown begun...\n");

// FW - Most lights will be in the BFF, so freeing is irrelevant
// Freeing "addlight" lights is now up to the caller
/*
	// SL: de-allocate all the lights we may or may not have added...
	for(current = EXLIGHT_Data->root.next; current != NULL; current = next)
	{
		// SL: store the next pointer
		next = current->next;
		// SL: free the current item.
		FREE(current);
	}
*/
	
	// SL: throw away the level struct...
	FREE(EXLIGHT_Data);
	EXLIGHT_Data = NULL;

	// JS : In some cases the pointers actor_shadowSource array are not valid because the memory has
	// been free'd at the end of the map play.  So we'll just make sure the array holds nothing but
	// NULL pointers.  Not too sure hif this is a good idea becuase of non-bff lights, but it'll stop
	// crashes happening for now on the PC.	
	for(i=0; i < MAX_SHADOW_LIGHTS; i++)
	{
		if (actor_shadowSource[i]==NULL)
			break;
		actor_shadowSource[i]=NULL;
	} // end-for



	PRINTF("EXLIGHT_Shutdown completed.\n");
}

void EXLIGHT_MapSwitch(EXLIGHT_TYPE	*current,int which)
{
	int i;
	WORLD_MESH_INSTANCE **instance;

	instance = (void *)(current->meshswaps);

	for(i = current->n_meshswaps; i > 0; i--)
	{
		if(*instance)
			mapdrawEnable(*instance, 1-which);
		instance++;
		if(*instance)
			mapdrawEnable(*instance, which);
		instance++;
	}
}



// Mobile searchlight, dude...
// Move the target point. Leave the source point alone
void EXLIGHT_MoveAround(EXLIGHT_TYPE *current)
{
	LIGHT_MOBILESTR *mob = (void *)current->meshswaps;	// Consequence - you can't have meshswapping mobile lights
	VECTOR *pTo;
	VECTOR *pos;

	if(current->flags & LIGHTFLAG_CONE)
		pos = (VECTOR *)&current->boxsize;
	else
		pos = (VECTOR *)&current->pos;

	if(mob->n_points < 2)
		return;


	if(mob->waitTimer==0)
	{
		VECTOR *pTo;
//Figure which point we're moving to
		pTo = (VECTOR *)&(mob->points[(mob->point+1)%mob->n_points].vx);

// Accelerate
		if(mob->accel)
		{
			mob->speed += mob->accel;
			if(mob->speed > mob->maxspeed)
				mob->speed = mob->maxspeed;
		}
		else
		{
			mob->speed = mob->maxspeed;
		}


		if(VectorMoveTo(pos, pTo, mob->speed))
		{
// Things to do when we get there
			mob->point++;

			if(mob->point == mob->n_points)
				mob->point = 0;


			if(mob->waitTimer != 0)
			{
				mob->speed=0;
			}


			mob->waitTimer = mob->points[mob->point].wait;


		}


	}
	if(mob->waitTimer>0)
		mob->waitTimer--;

}

void EXLIGHT_UpdateLights(void)
{
	EXLIGHT_TYPE	*current;
	int				r1, g1, b1, r2, g2, b2, scale;

//	PRINTF("Started light update...\n");

	// SL: run through all the lights in the system...
	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		if(current->flags & LIGHTFLAG_MOBILE)
		{
			EXLIGHT_MoveAround(current);
		}

		switch(current->type)
		{
		case EXLIGHT_ANIM_NONE:
			{
				// SL: just make double dog sure we have the right colour...
				current->col = current->basecol1;
			}
			break;


// Fred's removed the gameinfo bit...
		case EXLIGHT_ANIM_SWITCH_TO:
			{
				if(current->flags & LIGHTFLAG_MESHSWAP)
				{
					EXLIGHT_MapSwitch(current,0);
				}
				// SL: if it's not yet time to update this little feller, then move along, jimlad.
				if( (loop_ticker2+current->offset) % current->speed )
					break;

				// SL: write in the new colour to use, and toggle state...
				current->col = current->basecol2;
				current->type = EXLIGHT_ANIM_SWITCH_FROM;
			}
			break;
		case EXLIGHT_ANIM_SWITCH_FROM:
			{
				if(current->flags & LIGHTFLAG_MESHSWAP)
				{
					EXLIGHT_MapSwitch(current,1);
				}
				// SL: if it's not yet time to update this little feller, then move along, jimlad.
				if( (loop_ticker2+current->offset) % current->speed )
					break;

				// SL: write in the new colour to use, and toggle state...
				current->col = current->basecol1;
				current->type = EXLIGHT_ANIM_SWITCH_TO;
			}
			break;
		case EXLIGHT_ANIM_RANDOM:
			{
				if(!current->speed)
					current->speed = 1;
				// SL: if it's not yet time to update this little feller, then move along, jimlad.
				if( (loop_ticker2+current->offset) % current->speed )
					break;

				// SL: randomly pick one of the two colours to be...
				if( abs(utilRandomInt(10)) > 5 )
				{
//					PRINTF("Random: Chose 1\n");
					if(current->flags & LIGHTFLAG_MESHSWAP)
					{
						EXLIGHT_MapSwitch(current,0);
					}
					current->col = current->basecol1;
				}
				else
				{
//					PRINTF("Random: Chose 2\n");
					if(current->flags & LIGHTFLAG_MESHSWAP)
					{
						EXLIGHT_MapSwitch(current,1);
					}
					current->col = current->basecol2;
				}
			}
			break;
		case EXLIGHT_ANIM_PULSE_TO:
			{
				if(!current->speed)
					current->speed = 1;

				if(current->flags & LIGHTFLAG_MESHSWAP)
				{
					EXLIGHT_MapSwitch(current,0);
				}

				// SL: if it's not yet time to update this little feller, then move along, jimlad.
				if( ((loop_ticker2+current->offset) % current->speed) == 0 )
				{
					// SL: write in the full colour and toggle the type...
					current->col = current->basecol2;
					current->type = EXLIGHT_ANIM_PULSE_FROM;
				}
				else
				{
					// SL: how far through this blend are we?
					scale = (loop_ticker2 + current->offset) % current->speed;

					// SL: scale the colours in the right proportions...
					r1 = ((int)current->basecol1.r * (current->speed - scale)) / current->speed;
					g1 = ((int)current->basecol1.g * (current->speed - scale)) / current->speed;
					b1 = ((int)current->basecol1.b * (current->speed - scale)) / current->speed;

					r2 = ((int)current->basecol2.r * scale) / current->speed;
					g2 = ((int)current->basecol2.g * scale) / current->speed;
					b2 = ((int)current->basecol2.b * scale) / current->speed;

					// SL: mix em together...
					current->col.r = r1 + r2;
					current->col.g = g1 + g2;
					current->col.b = b1 + b2;
				}
			}
			break;
		case EXLIGHT_ANIM_PULSE_FROM:
			{
				if(current->flags & LIGHTFLAG_MESHSWAP)
				{
					EXLIGHT_MapSwitch(current,1);
				}
				// SL: if it's not yet time to update this little feller, then move along, jimlad.
				if( ((loop_ticker2+current->offset) % current->speed) == 0 )
				{
					// SL: write in the full colour and toggle the type...
					current->col = current->basecol1;
					current->type = EXLIGHT_ANIM_PULSE_TO;
				}
				else
				{
					// SL: how far through this blend are we?
					scale = (loop_ticker2 + current->offset) % current->speed;

					// SL: scale the colours in the right proportions...
					r1 = ((int)current->basecol2.r * (current->speed - scale)) / current->speed;
					g1 = ((int)current->basecol2.g * (current->speed - scale)) / current->speed;
					b1 = ((int)current->basecol2.b * (current->speed - scale)) / current->speed;

					r2 = ((int)current->basecol1.r * scale) / current->speed;
					g2 = ((int)current->basecol1.g * scale) / current->speed;
					b2 = ((int)current->basecol1.b * scale) / current->speed;

					// SL: mix em together...
					current->col.r = r1 + r2;
					current->col.g = g1 + g2;
					current->col.b = b1 + b2;
				}
			}
			break;

		default:
			{
				PRINTF("Eeeek!!! Unhandled EXLIGHT_ANIM type (%d)!!\n", current->type);
			}
			break;
		}
	}

//	PRINTF("Finished light update...\n");
}

void EXLIGHT_Update(void)
{
	// SL: update the drej lights...
//	EXLIGHT_UpdateDrejOrbitingLights();

	// SL: draw the positions of the light sources (doesn't work yet...)
//	EXLIGHT_DebugDrawLights();

	EXLIGHT_UpdateLights();

}

// CPW: Get nearest light to position passed
EXLIGHT_TYPE *EXLIGHT_GetNearest(VECTOR *pPos)
{
	EXLIGHT_TYPE *current;

	EXLIGHT_TYPE *result = NULL;
	int check=0x7fffffff;

	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		int dist=EXLITE_Sqrt((current->pos.x-pPos->vx)*(current->pos.x-pPos->vx)+
							(current->pos.y-pPos->vy)*(current->pos.y-pPos->vy)+
							(current->pos.z-pPos->vz)*(current->pos.z-pPos->vz))>>16;

		// CPW: DB("dist from light = %d ", dist); 

		if(dist<check)
		{
			// CPW: we've found a closer light
			// CPW: DB("Closer\n");
			check=dist;
			result=current;
		}
	}
	
	return result;
}


//EXLIGHT_TYPE *torchlight = NULL;	// test purposes only !!! tbd - do properly !
#define SPOT_POINTS 16

// Shove this on the dcache, and get a little bit of speed
typedef struct
{
	VERT points[SPOT_POINTS+4];
	long tfv[SPOT_POINTS+4];
	long tfd[SPOT_POINTS+4];
}spotliteDrawScratch;



void spotlitePointCalc(VECTOR *pos, VECTOR *targ, int spotsize, VERT *out)
{
	VECTOR d;
	VECTOR edgedelta;
	int len;
	int rad;
	static VECTOR north = {0,0,4096};
	static VECTOR east = {4096,0,0};
	VECTOR fore;
	VECTOR p1;
	VECTOR p2;

	VECTOR pt;
	int i;


	d.vx = targ->vx - pos->vx;
	d.vy = targ->vy - pos->vy;
	d.vz = targ->vz - pos->vz;
	len = Magnitude(&d);
	rad = len * 4 / spotsize;

	fore = d;
	MakeUnit(&fore);
	if(fore.vx == 0 && fore.vz == 0)
	{
		p1 = north;
	}
	else if(fore.vx > fore.vz)
	{
		CrossProduct(&p1,&fore,&north);
	}
	else
	{
		CrossProduct(&p1,&fore,&east);
	}
	CrossProduct(&p2,&p1,&fore);

// okay, we now have a torchlight-aligned cross
	p1.vx = p1.vx * rad/4096;
	p1.vy = p1.vy * rad/4096;
	p1.vz = p1.vz * rad/4096;
	p2.vx = p2.vx * rad/4096;
	p2.vy = p2.vy * rad/4096;
	p2.vz = p2.vz * rad/4096;
/*
	{
		VECTOR t1,t2;
		t1.vx = targ->vx + p1.vx;
		t1.vy = targ->vy + p1.vy;
		t1.vz = targ->vz + p1.vz;
		t2.vx = targ->vx - p1.vx;
		t2.vy = targ->vy - p1.vy;
		t2.vz = targ->vz - p1.vz;
		overlayShowDebugLine(
				&t1,
				&t2,
				0x80ff80,
				DEBUGPOLY_SUBDIV);	//+DEBUGPOLY_USEDEPTH);

		t1.vx = targ->vx + p2.vx;
		t1.vy = targ->vy + p2.vy;
		t1.vz = targ->vz + p2.vz;
		t2.vx = targ->vx - p2.vx;
		t2.vy = targ->vy - p2.vy;
		t2.vz = targ->vz - p2.vz;
		overlayShowDebugLine(
				&t1,
				&t2,
				0x80ff80,
				DEBUGPOLY_SUBDIV);	//+DEBUGPOLY_USEDEPTH);
		overlayShowDebugLine(
				pos,
				targ,
				0x808080,
				DEBUGPOLY_SUBDIV);	//+DEBUGPOLY_USEDEPTH);

	}
*/

//	DB(" p1,p2 = %d %d %d, %d %d %d\n",p1.vx,p1.vy,p1.vz,p2.vx,p2.vy,p2.vz);

	for(i = 0; i < SPOT_POINTS; i++)
	{
		int c,s;
		c = rcos(i * 4096 / SPOT_POINTS);
		s = rsin(i * 4096 / SPOT_POINTS);
		pt.vx = targ->vx + ((s * p1.vx + c * p2.vx)>>12);
		pt.vy = targ->vy + ((s * p1.vy + c * p2.vy)>>12);
		pt.vz = targ->vz + ((s * p1.vz + c * p2.vz)>>12);


// reet, now we need the isect point with the ground...
		pt.vx = (int)pos->vx + (pt.vx - pos->vx) * d.vy / (pt.vy - pos->vy);
		pt.vz = (int)pos->vz + (pt.vz - pos->vz) * d.vy / (pt.vy - pos->vy);
		pt.vy = targ->vy;

/*
		overlayShowDebugLine(
				targ,
				&pt,
				0x80ff80,
				DEBUGPOLY_SUBDIV);	//+DEBUGPOLY_USEDEPTH);
*/

		out[i].vx = pt.vx-mapCamData.gameCam.vpx;
		out[i].vy = pt.vy-mapCamData.gameCam.vpy;
		out[i].vz = pt.vz-mapCamData.gameCam.vpz;
	}
	out[i].vx = targ->vx-mapCamData.gameCam.vpx;
	out[i].vy = targ->vy-mapCamData.gameCam.vpy;
	out[i].vz = targ->vz-mapCamData.gameCam.vpz;
}


//void spotliteDraw(VECTOR *pos, int rx, int rz, unsigned int col, int ya)
void spotliteDraw(VECTOR *pos, VECTOR *targ, int spotsize, unsigned int col)
{
#ifndef DREAMCAST
	int i;
	int ang;
	PACKET * packet;

	unsigned long *ot = currentDisplayPage->ot;
#ifndef _WINDOWS
	register long clipflag asm("$20");
	register long depth asm("$20");
#else
	register long clipflag;
	register long depth;
#endif
	int t1,t2;

	spotliteDrawScratch *scratch = (void *)DCACHE;
	long *tfv = &scratch->tfv[0];
	long *tfd = &scratch->tfd[0];
	VERT *points = &scratch->points[0];

	VECTOR cen;

	clipflag = depth = 0;

	ASSERT (sizeof(spotliteDrawScratch) < 1024);




/*
	points[SPOT_POINTS].vx = pos->vx-mapCamData.gameCam.vpx;
	points[SPOT_POINTS].vy = pos->vy-mapCamData.gameCam.vpy;
	points[SPOT_POINTS].vz = pos->vz-mapCamData.gameCam.vpz;
	ang = 0;
	for(i = 0; i < SPOT_POINTS; i++)
	{
		VERT temp;
		int x,z;


		x =  ((rx * rsin(ang)) >> 12);
		z =  ((rz * rcos(ang)) >> 12);

		points[i].vx = ((x * rcos(ya) + z * rsin(ya)) >> 12) + points[SPOT_POINTS].vx;
		points[i].vy = points[SPOT_POINTS].vy;
		points[i].vz = ((z * rcos(ya) - x * rsin(ya)) >> 12) + points[SPOT_POINTS].vz;
		ang += 4096/SPOT_POINTS;
	}
*/
	spotlitePointCalc(pos, targ, spotsize, points);


	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);


// Now run that little lot through transform...
//	transformVertexListA_ChickenRun(&points[0], SPOT_POINTS+1, transformedVertices, transformedDepths);
	transformVertexListA_ChickenRun(&points[0], SPOT_POINTS+1, tfv, tfd);

	packet = (PACKET *)currentDisplayPage->primPtr;

	for(i = 0; i < SPOT_POINTS; i++)
	{
		if(i==SPOT_POINTS-1)
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[0], tfv[i]);
		}
		else
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[i+1], tfv[i]);
		}
		gte_nclip();
		gte_stopz_cpu(clipflag);

		if (clipflag<0)
		{
			if(i==SPOT_POINTS-1)
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[0],tfd[i]);
			}
			else
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[i+1],tfd[i]);
			}
   			gte_avsz3();
			gte_stotz_cpu(depth);
			depth = depth >> MAP_SCALE_DEPTH_DOWN;
// push depth away, so that shadows come on top of it
// (This requires the lscape to be at +40 odd!)
			depth += 20;

			if(depth < 0) depth = 0;
			if(depth > 1023) depth = 1023;

#define si ((POLY_G3*)packet)

			addPrimLen(ot+depth, (si),6,t2);

			t2 = ((col & 0xFEFEFE)>>1) | ((GPU_COM_G3+2) << 24);
			t1 = ((col & 0xFCFCFC)>>2) | ((GPU_COM_G3+2) << 24);

			*(u_long *)  (&si->r0) = t2;
			gte_stsxy3_g3(si);

			*(u_long *)  (&si->r1) = t1;
			*(u_long *)  (&si->r2) = t1;

			si++;

#undef si

// now stick in an additive control
#define si ((DR_TPAGE*)packet)
			SetDrawTPage(si,1,1,(1<<5));	// (1 = +ve) this is a macro, anyway
			addPrimLen(ot+depth, (si),1,t2);
			si++;

#undef si
		}
	}
	currentDisplayPage->primPtr = (char *)packet;
#endif
}

#ifdef DREAMCAST
int spotbodge = -5;
void spotliteDrawDC(VECTOR *pos, VECTOR *targ, int spotsize, unsigned int col)
{
	int i;
	int ang;
	PACKET * packet;
	
	unsigned long *ot;// = currentDisplayPage->ot;
	long clipflag;
	long depth;

	int t1,t2;


	spotliteDrawScratch *scratch = (void *)DCACHE;
	long *tfv = &scratch->tfv[0];
	long *tfd = &scratch->tfd[0];
	VERT *points = &scratch->points[0];

	VECTOR cen;

	clipflag = depth = 0;

	ASSERT (sizeof(spotliteDrawScratch) < 1024);

	spotlitePointCalc(pos, targ, spotsize, points);
	
	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

// Now run that little lot through transform...
	transformVertexListA(&points[0], SPOT_POINTS+1, tfv, tfd);

	packet = (PACKET *)currentDisplayPage->primPtr;

	for(i = 0; i < SPOT_POINTS; i++)
	{
		if(i==SPOT_POINTS-1)
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[0], tfv[i]);
		}
		else
		{
			gte_ldsxy3(tfv[SPOT_POINTS],tfv[i+1], tfv[i]);
		}
		gte_nclip();
		gte_stopz(&clipflag);

//		if (clipflag<0)
		{
			if(i==SPOT_POINTS-1)
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[0],tfd[i]);
			}
			else
			{
				gte_ldsz3(tfd[SPOT_POINTS],tfd[i+1],tfd[i]);
			}

#define si ((POLY_G3*)packet)

			BEGINPRIM(si,POLY_G3);

			gte_stsxy3_g3(si);

			t2 = ((col & 0xFEFEFE)>>1) ;
			t1 = ((col & 0xFCFCFC)>>2) ;

			*(u_long *)  (&si->r0) = t2;
			*(u_long *)  (&si->r1) = t1;
			*(u_long *)  (&si->r2) = t1;

			si->tag=SEMITRANS_ADD;
			si->code=GPU_COM_G3;


			ENDPRIMA(si,tfd[SPOT_POINTS]+spotbodge,tfd[i]+spotbodge,tfd[(i+1)&15]+spotbodge,0,POLY_G3);
#undef si
		}
	}
}

int shadDepthOffset = -5;
//still in dreamcast define
void EXLIGHT_shadowEllipseDC(VECTOR *worldPosTrans, VECTOR *parentPosTrans, int width, unsigned char darkness)
{
	VERT		worldPos, parentPos;
	VERT		delta, point[4];
	int			dist, i;
	DVECTOR		worldXY, parentXY;
	POLY_FT4	*si; 
	long		depth,d[4];
	TextureType *sprite;

	VERT side;
	LINE_F2 *line;

	//mikey
	VERT		point2d[4];//shorts

	// SL: copy in the positions...
	worldPos.vx = worldPosTrans->vx;
	worldPos.vy = worldPosTrans->vy;
	worldPos.vz = worldPosTrans->vz ;

	parentPos.vx = parentPosTrans->vx;
	parentPos.vy = parentPosTrans->vy;
	parentPos.vz = parentPosTrans->vz;

	//between the two points
	delta.vx = parentPos.vx - worldPos.vx;
	delta.vz = parentPos.vz - worldPos.vz;

	if(delta.vx==0 && delta.vz==0)return;

	//fill in the space between the joints of out shadow man
	parentPos.vy = worldPos.vy;

	sprite = EXLIGHT_Data->shadow;
					 
	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);
	
//this bit draws the stick man//
/*
	BEGINPRIM(line, LINE_F2);
	
	gte_ldv0(&parentPos);		   
	gte_rtps();					   
	gte_stsxy((long*)&line->x0); 

	gte_ldv0(&worldPos);		   
	gte_rtps();					   
	gte_stsxy((long*)&line->x1); 

	setLineF2(line);
	setRGB0(line, 240,240,240);
	line->code = 64;
	lines2D = NO;
	ENDPRIM(line, 0, LINE_F2);
	lines2D = YES;
*/
////////////////////////////////


	side.vx = (parentPos.vz - worldPos.vz)/2;
	side.vz =-(parentPos.vx - worldPos.vx)/2;

  	point[1].vx = worldPos.vx+side.vx+side.vz;
	point[1].vz = worldPos.vz+side.vz-side.vx;

	point[0].vx = worldPos.vx-side.vx+side.vz;
	point[0].vz = worldPos.vz-side.vz-side.vx;

	point[3].vx = parentPos.vx+side.vx-side.vz;
	point[3].vz = parentPos.vz+side.vz+side.vx;

	point[2].vx = parentPos.vx-side.vx-side.vz;
	point[2].vz = parentPos.vz-side.vz+side.vx;
	
	point[0].vy = point[1].vy = point[2].vy = point[3].vy = worldPos.vy;

   	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);
  
	gte_ldv0(&point[0]);		   
	gte_rtps();					   
	gte_stsxy((long*)&point2d[0]); 
	gte_stsz(&d[0]);			   

	gte_ldv0(&point[1]);		   
	gte_rtps();					   
	gte_stsxy((long*)&point2d[1]); 
	gte_stsz(&d[1]);			   

	gte_ldv0(&point[2]);		   
	gte_rtps();					   
	gte_stsxy((long*)&point2d[2]); 
	gte_stsz(&d[2]);			   

	gte_ldv0(&point[3]);		   
	gte_rtps();					   
	gte_stsxy((long*)&point2d[3]); 
	gte_stsz(&d[3]);			   

	//overflow check
	for(i=0;i<4;i++)
	{
		if(abs(point2d[i].vx) >= 1023)return;
		if(abs(point2d[i].vy) >= 1023)return;
	}

   //	gte_avsz4();				   
   //psx uses this one	gte_stotz(&depth);		   
//	gte_stsz(&depth);			   

			 
	BEGINPRIM(si, POLY_FT4);
	setPolyFT4(si);

	// write in the RGBs...
	si->r0 = darkness*2;
	si->g0 = darkness*2;
	si->b0 = darkness*2;

	si->tpage = sprite->tpage;
	si->tag=SEMITRANS_SUB;

	setXY4(si, point2d[1].vx, point2d[1].vy,
			   point2d[0].vx, point2d[0].vy,
			   point2d[3].vx, point2d[3].vy,
			   point2d[2].vx, point2d[2].vy);

	// SL: setup the UVs...
	si->u0 = sprite->u0;
	si->v0 = sprite->v0;
	si->u1 = sprite->u0;
	si->v1 = sprite->v2;
	si->u2 = sprite->u1;
	si->v2 = sprite->v0;
	si->u3 = sprite->u1;
	si->v3 = sprite->v2;
							  
	si->code = 0x2c; 

	ENDPRIMA(si, d[1]+shadDepthOffset,d[0]+shadDepthOffset,d[3]+shadDepthOffset,d[2]+shadDepthOffset, POLY_FT4);
}
#endif

void EXLIGHT_DebugDrawLights(void)
{
	EXLIGHT_TYPE		*current;
	TPOINT				point;
#ifdef LIGHT_ZERO_CAMERA
	long t[3];
#endif


	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		if(current->flags & LIGHTFLAG_CONE)
		{
/*
			int ya;
			int xa;

			int dx,dy,dz;
			int d;
			int m;
			int r1,r2;

			dx = current->boxsize.x - current->pos.x;
			dy = current->boxsize.y - current->pos.y;
			dz = current->boxsize.z - current->pos.z;
			m = utilSqrt(dx * dx + dz * dz) >> 16;
			d = utilSqrt(dx * dx + dy * dy + dz * dz) >> 16;

			ya = utilCalcAngle(dx, dz) & 4095;
			xa = utilCalcAngle(m, current->boxsize.y - current->pos.y) & 4095;

//			r1 = d / 6;	// a bit arbitary, but enough to look okay shadow-wise
			r1 = d * 16 / (current->spot_size * 6);	// a bit arbitary, but enough to look okay shadow-wise
			if(dy < 100)
			{
				DB("Spotlights need to light to be above the path\n");
				CRASH;
			}
			r2 = r1 * (dy + (m>>1)) / dy;


			spotliteDraw((VECTOR *)&current->boxsize, r1,r2,  *(unsigned int *)&current->col, ya);
*/
#ifdef _WINDOWS
			PC3DH_SpotliteDraw((VECTOR *)&current->pos,(VECTOR *)&current->boxsize, current->spot_size, *(unsigned int *)&current->col);
#else
#ifdef DREAMCAST
			spotliteDrawDC((VECTOR *)&current->pos,(VECTOR *)&current->boxsize, current->spot_size, *(unsigned int *)&current->col);
#else
			spotliteDraw((VECTOR *)&current->pos,(VECTOR *)&current->boxsize, current->spot_size, *(unsigned int *)&current->col);
#endif
#endif
		}
	}



	if(overlay_show_collision != DEBUG_OVERLAY_ENEMY_INFO)
		return;


#ifdef LIGHT_ZERO_CAMERA
	t[0] = GsWSMATRIX.t[0];
	t[1] = GsWSMATRIX.t[1];
	t[2] = GsWSMATRIX.t[2];
	GsWSMATRIX.t[0] = 0;
	GsWSMATRIX.t[1] = 0;
	GsWSMATRIX.t[2] = 0;
	gte_SetTransMatrix(&GsWSMATRIX);

#endif
	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		VERT temp;
		temp.vx = current->pos.x;
		temp.vy = current->pos.y;
		temp.vz = current->pos.z;
#ifdef LIGHT_ZERO_CAMERA
		temp.vx -= mycamera.vpx;
		temp.vy -= mycamera.vpy;
		temp.vz -= mycamera.vpz;
#endif
//		aiDrawAlignedSprite3D(EXLIGHT_Data->lightsrc, &current->pos, 32, 32, AIDRAW_TYPE_PSX_SEMITRANS_ADD, current->col.r, current->col.g, current->col.b );
		Print3DSprite(EXLIGHT_Data->lightsrc, &temp, 1024, P3DS_ADD | (current->col.r) | (current->col.g << 8) | (current->col.b <<16) );
	}
#ifdef LIGHT_ZERO_CAMERA
	GsWSMATRIX.t[0] = t[0];
	GsWSMATRIX.t[1] = t[1];
	GsWSMATRIX.t[2] = t[2];
#endif
}

EXLIGHT_TYPE *EXLIGHT_AddLight(void)
{
	EXLIGHT_TYPE		*addme;

	// SL: malloc it...
	addme = MALLOC( sizeof(EXLIGHT_TYPE) );

	if(!addme)
	{
		PRINTF("Could not alloc new light - no RAM. !!!!!!!!!!!!!!!!!!!\n");
		return NULL;
	}

	// SL: blank it...
	memset(addme, 0, sizeof(EXLIGHT_TYPE));

	// SL: link it into the list...
	addme->next = EXLIGHT_Data->root.next;
	EXLIGHT_Data->root.next = addme;

	addme->flags = LIGHTFLAG_SHADOW	| LIGHTFLAG_SHADE;
	addme->shadow_darkness = 64;
	addme->spot_size = 16;

	// SL: return the new pointer...
	return addme;
}

// note - doesn't FREE it.
void EXLIGHT_RemoveLight(EXLIGHT_TYPE *light)
{
	EXLIGHT_TYPE	*test;
	for(test = &EXLIGHT_Data->root;test; test = test->next)
	{
		if(test->next == light)
		{
			test->next = light->next;
				return;
		}
	}
}


void EXLIGHT_SetupDefaultActorLights(void)
{
	TIMER_START(TIMER_LIGHTING);
	psiSetLight(0, 128, 128,  128, 0, 100, -50);
	psiSetLight(1, 0, 0, 0, 1, 1, 1);
	psiSetLight(2, 0, 0, 0, 1, 1, 1);
	TIMER_STOP(TIMER_LIGHTING);

}

/*
void MATHS_Rotate(short angx, short angy, short angz, long movex, long movey, long movez, VECTOR *result)
{																
	SVECTOR  MATHS_ang;											
	VECTOR   MATHS_move;										
	MATRIX   MATHS_rotMat;										
																
	MATHS_ang.vx = (angx);										
	MATHS_ang.vy = (angy);										
	MATHS_ang.vz = (angz);										
																
	MATHS_move.vx = (movex);								
	MATHS_move.vy = (movey);									
	MATHS_move.vz = (movez);									
																
	MATHS_rotMat = GsIDMATRIX;									
	RotMatrixYXZ_gte(&MATHS_ang, &MATHS_rotMat);				
	ApplyMatrixLV(&MATHS_rotMat, &MATHS_move, (result));		
}
*/

// Copy of the sqrt routine, because this way there's no cache miss
extern unsigned long 	sqrtable[];				// square root lookup table
int EXLITE_Sqrt(int num)
{
	long	j;
	j = 0;
	while(num > 1024)
	{
		num >>= 2;
		j++;
	}
	return sqrtable[num] << j;
}



// ===== Playstation version of EXLIGHT_shadowEllipse.  I've had to re-write this for the PC because
// ===== we're using an average z-depth so the ellipses won't z-sort properly with the hardware 
// ===== z-buffer
#ifndef _WINDOWS


void EXLIGHT_shadowEllipse(VECTOR *worldPosTrans, VECTOR *parentPosTrans, int width, unsigned char darkness)
{
#ifndef DREAMCAST
	VERT		worldPos, parentPos;
	VERT		delta, point[4];
//	LINE_F2		*line;
//	int			rot, result, dist, i;
//	VECTOR		tempVect;
	int			dist, i;
	DVECTOR		worldXY, parentXY;
	POLY_FT4	*si; 
	long		depth;
	TextureType *sprite;

	VECTOR side;


	// SL: check we can do ANY of this...
//	if (TOOMANYPOLYS(5))
//		return;

	// SL: copy in the positions...
	worldPos.vx = worldPosTrans->vx;
	worldPos.vy = worldPosTrans->vy;
	worldPos.vz = worldPosTrans->vz;

	parentPos.vx = parentPosTrans->vx;
	parentPos.vy = parentPosTrans->vy;
	parentPos.vz = parentPosTrans->vz;

	// SL: get the deltas
	delta.vx = parentPos.vx - worldPos.vx;
	delta.vz = parentPos.vz - worldPos.vz;






	dist = EXLITE_Sqrt(delta.vx * delta.vx + delta.vz * delta.vz);
	dist = dist>>16;
	if(dist == 0)
	{
//		return;
// Do a circle if the points are on top of each other
		side.vx = width;
		side.vz = 0;
		delta.vx = -side.vz;
		delta.vz =  side.vx;
	}
	else
	{
		side.vx = delta.vz * width/dist;
		side.vz = -delta.vx * width/dist;
//	delta.vx = delta.vx>>1;
//	delta.vz = delta.vz>>1;

		delta.vx = -side.vz; //>> 1;
		delta.vz =  side.vx; //>> 1;
	}




// Add an extra half-delta as well as the side vectors, so the ellipses overlap nicely

	point[0].vx = parentPos.vx - side.vx + delta.vx;
	point[0].vz = parentPos.vz - side.vz + delta.vz;

	point[1].vx = parentPos.vx + side.vx + delta.vx;
	point[1].vz = parentPos.vz + side.vz + delta.vz;

	point[2].vx = worldPos.vx - side.vx - delta.vx;
	point[2].vz = worldPos.vz - side.vz - delta.vz;

	point[3].vx = worldPos.vx + side.vx - delta.vx;
	point[3].vz = worldPos.vz + side.vz - delta.vz;

	point[0].vy = point[1].vy = point[2].vy = point[3].vy = worldPos.vy;


//	sprite = EXPLORE_Data->shadowSprPtr;
	sprite = EXLIGHT_Data->shadow;
	// SL: make sure the matrices are reset...
	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

	// SL: degin the poly...
	BEGINPRIM(si, POLY_FT4);
	setPolyFT4(si);

	// write in the RGBs...
	si->r0 = darkness;
	si->g0 = darkness;
	si->b0 = darkness;

	// SL: setup the code and all that...
	si->clut = sprite->clut;
	si->tpage = sprite->tpage;
	si->code |= 2;
//	si->tpage |= ((AIDRAW_TYPE_PSX_SEMITRANS_SUB-1)<<5);
	si->tpage |= ((SEMITRANS_SUB-1)<<5);

 	gte_ldv3c(&point[0]);		//load vector's 3 from continuous memory addresses
	gte_rtpt();					//RotTransPers3
	gte_stsxy3_ft4(si);			//store in POLY_F4 structure location
	gte_ldv0(&point[3]);			//load final vector into v0
	gte_rtps();					//RotTransPers
	gte_stsxy(&(si->x3));		//store final vector into x3 location
	gte_avsz4();				//Average the z's (and does /4)
	gte_stotz(&depth);			//Store OTZ (ordering table z)

	// SL: setup the UVs...
	si->u0 = sprite->u0;
	si->v0 = sprite->v0;
	si->u1 = sprite->u0;
	si->v1 = sprite->v2;
	si->u2 = sprite->u1;
	si->v2 = sprite->v0;
	si->u3 = sprite->u1;
	si->v3 = sprite->v2;

//	depth = ((depth-10) >> LSCAPE_Data.depthshift)&MAXDEPTH;

	depth = ((depth-10) >> 3)&1023;

	// ADD TO DRAW LIST
	ENDPRIM(si, depth, POLY_FT4);
//	ENDPRIM(si, 1, POLY_FT4);

#endif
}

#else


extern int runningHard;
extern int PCH3D_DrawShadowEllipse(SHORTXY *screenXY, int *zDepths, TextureType *sprite, char darkness);

void EXLIGHT_shadowEllipse(VECTOR *worldPosTrans, VECTOR *parentPosTrans, int width, unsigned char darkness)
{
#ifndef DREAMCAST

	VERT		worldPos, parentPos;
	VERT		delta, point[4];
//	LINE_F2		*line;
//	int			rot, result, dist, i;
//	VECTOR		tempVect;
	int			dist, i;
	DVECTOR		worldXY, parentXY;
	POLY_FT4	*si; 
	long		depth;
	TextureType *sprite;

	VECTOR side;
	int zDepths[4];
	SHORTXY scrXY[4];


	// SL: check we can do ANY of this...
//	if (TOOMANYPOLYS(5))
//		return;

	// SL: copy in the positions...
	worldPos.vx = worldPosTrans->vx;
	worldPos.vy = worldPosTrans->vy;
	worldPos.vz = worldPosTrans->vz;

	parentPos.vx = parentPosTrans->vx;
	parentPos.vy = parentPosTrans->vy;
	parentPos.vz = parentPosTrans->vz;

	// SL: get the deltas
	delta.vx = parentPos.vx - worldPos.vx;
	delta.vz = parentPos.vz - worldPos.vz;



	dist = EXLITE_Sqrt(delta.vx * delta.vx + delta.vz * delta.vz);
	dist = dist>>16;
#ifndef _WINDOWS
	if(dist == 0)
		return;
#else
	if(dist < 20)
		return;
#endif

	side.vx = delta.vz * width/dist;
	side.vz = -delta.vx * width/dist;


//	delta.vx = delta.vx>>1;
//	delta.vz = delta.vz>>1;

	delta.vx = -side.vz; //>> 1;
	delta.vz =  side.vx; //>> 1;


// Add an extra half-delta as well as the side vectors, so the ellipses overlap nicely

	point[0].vx = parentPos.vx - side.vx + delta.vx;
	point[0].vz = parentPos.vz - side.vz + delta.vz;

	point[1].vx = parentPos.vx + side.vx + delta.vx;
	point[1].vz = parentPos.vz + side.vz + delta.vz;

	point[2].vx = worldPos.vx - side.vx - delta.vx;
	point[2].vz = worldPos.vz - side.vz - delta.vz;

	point[3].vx = worldPos.vx + side.vx - delta.vx;
	point[3].vz = worldPos.vz + side.vz - delta.vz;


	// Setup the y positions of the points. If we're running in hardware, 
	// and a little extra so that we don't get weird z-buffer innacuracies
	// happening.
	if (runningHard)
		point[0].vy = point[1].vy = point[2].vy = point[3].vy = worldPos.vy-30;
	else
		point[0].vy = point[1].vy = point[2].vy = point[3].vy = worldPos.vy;

	

//	sprite = EXPLORE_Data->shadowSprPtr;
	sprite = EXLIGHT_Data->shadow;
	// SL: make sure the matrices are reset...
	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

	// SL: degin the poly...
	BEGINPRIM(si, POLY_FT4);
	setPolyFT4(si);

	// write in the RGBs...
	si->r0 = darkness;
	si->g0 = darkness;
	si->b0 = darkness;

	// SL: setup the code and all that...
	si->clut = sprite->clut;
	si->tpage = sprite->tpage;
	si->code |= 2;
//	si->tpage |= ((AIDRAW_TYPE_PSX_SEMITRANS_SUB-1)<<5);
	si->tpage |= ((SEMITRANS_SUB-1)<<5);





	if (!runningHard)
	{
 		gte_ldv3c(&point[0]);		//load vector's 3 from continuous memory addresses
		gte_rtpt();					//RotTransPers3
		gte_stsxy3_ft4(si);			//store in POLY_F4 structure location
		gte_ldv0(&point[3]);			//load final vector into v0
		gte_rtps();					//RotTransPers
		gte_stsxy(&(si->x3));		//store final vector into x3 location
		gte_avsz4();				//Average the z's (and does /4)
		gte_stotz(&depth);			//Store OTZ (ordering table z)
		
		// SL: setup the UVs...
		si->u0 = sprite->u0;
		si->v0 = sprite->v0;
		si->u1 = sprite->u0;
		si->v1 = sprite->v2;
		si->u2 = sprite->u1;
		si->v2 = sprite->v0;
		si->u3 = sprite->u1;
		si->v3 = sprite->v2;

		depth = ((depth-10) >> 3)&1023;

		// ADD TO DRAW LIST
		ENDPRIM(si, depth, POLY_FT4);
		ENDPRIM(si, 1, POLY_FT4);
	} else {

		gte_ldv3c(&point[0]);		// load vector's 3 from continuous memory addresses
		gte_rtpt();					// RotTransPers3
		gte_stsxy3c(scrXY);			// Get the first three sets of screen XY's 
		gte_stsz3c(zDepths);		// Get the z-depth of the first 3 verts	

		gte_ldv0(&point[3]);		// load final vector into v0
		gte_rtps();					// RotTransPers
		gte_stsxy(&scrXY[3]);		// store final screen XY
		gte_stsz(&zDepths[3]);		// Store the final z-depth

		PCH3D_DrawShadowEllipse(scrXY, zDepths, sprite, darkness);

	} // end-if-else

#endif
}

#endif




//#define EXLIGHT_SHADOW_RGB 32
//#define EXLIGHT_SHADOW_RGB 128

/*
// Print a light at each bonepoint
void LightsShowHir(ACTOR *actor, PSIOBJECT *world)
{
	if(!world)
		return;

	while(world)
	{
		VERT temp;
		static VECTOR org = {0,0,0};
		VECTOR dest;


		gte_SetRotMatrix(&world->matrixscale);
		gte_SetTransMatrix(&world->matrixscale);

		gte_ldlvl(&org);	// long t[3]
		gte_rtirtr();
		gte_stlvl(&dest);

		temp.vx = dest.vx; //+ actor->position.vx;
		temp.vy = dest.vy; //+ actor->position.vy;
		temp.vz = dest.vz; //+ actor->position.vz;

		gte_SetRotMatrix(&GsWSMATRIX);
		gte_SetTransMatrix(&GsWSMATRIX);

		Print3DSprite(EXLIGHT_Data->lightsrc, &temp, 512, P3DS_ADD | 0x808000);

		if(world->child)
		{

			LightsShowHir(actor,world->child);
		}
		world = world->next;

	}
}
*/

int EXLIGHT_conecheck(VECTOR *p1, VECTOR *p2, VECTOR *point, int *along)
{
	VECTOR dirn;
	VECTOR dp;
	int mag;
	int dot;
	dirn.vx = p2->vx - p1->vx;
	dirn.vy = p2->vy - p1->vy;
	dirn.vz = p2->vz - p1->vz;

	dp.vx = point->vx - p1->vx;
	dp.vy = point->vy - p1->vy;
	dp.vz = point->vz - p1->vz;

	mag = MakeUnit(&dirn);
	if(mag == 0)
	{
		if(along)
			*along = 32768;	// nice and big
		return(Magnitude(&dp));
	}
	dot = DotProduct(&dp,&dirn);

	if(along)
		*along = dot;
	if(dot < 0)
	{
		return -1;
	}
	else
	{
		int s = 12;
		while(dot > 0x7ffff)
		{
			dot = dot >> 2;
			s -= 2;
		}
		dp.vx = p1->vx + ((dot * dirn.vx) >> s);
		dp.vy = p1->vy + ((dot * dirn.vy) >> s);
		dp.vz = p1->vz + ((dot * dirn.vz) >> s);

		dp.vx = point->vx - dp.vx;
		dp.vy = point->vy - dp.vy;
		dp.vz = point->vz - dp.vz;
		return (Magnitude(&dp));
	}
}

void fredShadow(struct _EXLIGHT_TYPE *light,ACTOR *actor,PSIOBJECT *parent, int floor_y)	//darkness)
{
	VERT temp;
	static VECTOR org = {0,0,0};
	VECTOR parentpos;
	VECTOR childpos;
	VECTOR dv1;
	VECTOR dv2;
	PSIOBJECT *child;
	int darkness;

	int floorht = -light->pos.y + floor_y;	//(37<<3) - light->pos.y;
	gte_SetRotMatrix(&parent->matrixscale);
	gte_SetTransMatrix(&parent->matrixscale);

	gte_ldlvl(&org);	// long t[3]
	gte_rtirtr();
	gte_stlvl(&parentpos);

	dv1.vx = parentpos.vx - light->pos.x;
	dv1.vy = parentpos.vy - light->pos.y;
	dv1.vz = parentpos.vz - light->pos.z;


	if(dv1.vy <= 0)
		return;

// reet, now we need the isect point with the ground...
	dv1.vx = (int)light->pos.x + dv1.vx * floorht / dv1.vy;
	dv1.vz = (int)light->pos.z + dv1.vz * floorht / dv1.vy;
	dv1.vy = floor_y;	//FLOOR_Y;

	{
		VECTOR diffVec;
		int distance;

		diffVec.vx = light->pos.x - dv1.vx;
		diffVec.vy = light->pos.y - dv1.vy;
		diffVec.vz = light->pos.z - dv1.vz;

#ifdef OVERFLOW_CHECKS
		distance = Magnitude(&diffVec);
		if(distance <= 0 || distance > light->range)
			return;
		{
			int range;
			int mag;
				
			range = light->range;
			mag = ABS(range) | ABS(distance);
			while(mag > 32768)
			{
				mag = mag>>2;
				distance = distance>>2;
				range = range>>2;
			}
			darkness = ((int)(light->shadow_darkness) * (range - distance)) / range;
		}
#else
		distance = fast_sqrt(SQR(diffVec.vx) + SQR(diffVec.vy) + SQR(diffVec.vz));
		distance >>= 16;
		if(distance <= 0 || distance > light->range)
			return;
		darkness = ((int)(light->shadow_darkness) * (light->range - distance)) / light->range;

#endif
	}


	if(light->flags & (LIGHTFLAG_CONE))	// Mr Tweedy's torch of doom
	{
		int perp,along;
		perp = EXLIGHT_conecheck((VECTOR *)&light->pos.x, (VECTOR *)&light->boxsize, &parentpos, &along);
		if(perp == -1 || along == -1)
			return;

//		DB("val1 = %d, val2 = %d\n",(along * 4 / (int)light->spot_size),along / 4);

		if(perp > (along * 4 / (int)light->spot_size))	// along / 4)
			return;
		perp = perp * 4096 / (along * 4 / (int)light->spot_size);

/*
		if(perp > along / 4)
			return;
		perp = perp * 4096 / (along/4);
*/
		if(perp > 3000)
		{
//			printf("scale %d %d\n",(4096 - perp),(4096 - 3000));
			darkness = darkness * (4096 - perp) / (4096 - 3000);
		}
	}



#ifdef LIGHT_ZERO_CAMERA
	dv1.vx -= mycamera.vpx;
	dv1.vy -= mycamera.vpy;
	dv1.vz -= mycamera.vpz;
#endif

	for(child = parent->child; child; child = child->next)
	{
		gte_SetRotMatrix(&child->matrixscale);
		gte_SetTransMatrix(&child->matrixscale);

		gte_ldlvl(&org);	// long t[3]
		gte_rtirtr();
		gte_stlvl(&childpos);

		dv2.vx = childpos.vx-light->pos.x;
		dv2.vy = childpos.vy-light->pos.y;
		dv2.vz = childpos.vz-light->pos.z;

		if(dv2.vy > 0)
		{
			dv2.vx = (int)light->pos.x + dv2.vx * floorht / dv2.vy;
			dv2.vz = (int)light->pos.z + dv2.vz * floorht / dv2.vy;
			dv2.vy = floor_y;	//FLOOR_Y;

#ifdef LIGHT_ZERO_CAMERA
	dv2.vx -= mycamera.vpx;
	dv2.vy -= mycamera.vpy;
	dv2.vz -= mycamera.vpz;
#endif
			gte_SetRotMatrix(&GsWSMATRIX);
			gte_SetTransMatrix(&GsWSMATRIX);
//			EXLIGHT_shadowEllipse(&dv2, &dv1, 30, darkness);
#ifdef DREAMCAST
			EXLIGHT_shadowEllipseDC(&dv2, &dv1, EXLIGHT_Data->shadow_width, darkness);
#else
			EXLIGHT_shadowEllipse(&dv2, &dv1, EXLIGHT_Data->shadow_width, darkness);
#endif
		}
		if(child->child)
		{
			fredShadow(light,actor,child,floor_y);	//darkness);
		}
	}
}


// Fred was here
// A (hopefully) temporary modification to move the GS camera to zero while
// the actor's being displayed.
// (In an ideal world, the GS camera'll be at zero all the time)




void EXLIGHT_DrawShadow(ACTOR *actor)
{
	PSIOBJECT	*world;
	VECTOR		diffVec;	//, rotVec, actorPos;
	long		distance, darkness, i;
	long		floor_y;
#ifdef LIGHT_ZERO_CAMERA
	long t[3];
#endif


	// SL: no lights? sod it, then...
	if(!actor_shadowSource[0])
		return;

	// SL: this stuff only wants doing once...
   	world = actor->psiData.object;

	// SL: if there's no hierarchy, this ain't gonna work...
	if(!world->child)
		return;




// Move the camera to zero for the duration...
#ifdef LIGHT_ZERO_CAMERA
	t[0] = GsWSMATRIX.t[0];
	t[1] = GsWSMATRIX.t[1];
	t[2] = GsWSMATRIX.t[2];
	GsWSMATRIX.t[0] = 0;
	GsWSMATRIX.t[1] = 0;
	GsWSMATRIX.t[2] = 0;
	gte_SetTransMatrix(&GsWSMATRIX);

#endif



	// SL: reset the animation, coz if it's been drawn, its been fooked...
//	actorSetAnimation(actor, 0, actor->animation[0].frame);

	actorSetAnimation(actor, actor->animation.animData[0].actualFrame);

	// SL: translation relative to the motion bone, etc...

	if(actor->psiData.flags & ACTOR_MOTIONBONE)
	{
		world->matrix.t[0] = actor->position.vx;
		world->matrix.t[1] = actor->position.vy;
		world->matrix.t[2] = actor->position.vz;
	}
	else
	{
// the "-mycamera" is the "camera-at-zero" mod
		if((world->rotate.vx) || (world->rotate.vy) || (world->rotate.vz))
		{
			MATRIX		rotmat1;
			RotMatrixYXZ_gte(&world->rotate,&rotmat1);

			gte_SetRotMatrix(&rotmat1);
//			gte_SetTransMatrix	// not doing an rtirtr, so we don't need so set the tx to zero
			gte_ldlvl(&world->matrix.t);
			gte_rtir();
			gte_stlvl(&world->matrix.t);
		}



		world->matrix.t[0] += actor->position.vx;
		world->matrix.t[1] += actor->position.vy;	// + (37<<3);
		world->matrix.t[2] += actor->position.vz;
	}

	// SL: setup the scale global, before we do the local matrix thing...
	PSIrootScale = &world->scale;

	// SL: transform the bones in model space...
	psiCalcLocalMatrix(world);

	floor_y = getHeightAt(actor->position.vx<<9,actor->position.vy<<9,actor->position.vz<<9);
	floor_y = floor_y >> 9;
#ifdef _WINDOWS
	floor_y = floor_y + actor->position.vy;
#else
	floor_y += actor->position.vy;
#endif

	for(i = 0; i < MAX_SHADOW_LIGHTS; i++)
	{
		if(!actor_shadowSource[i])
		{
			break;
		}
		diffVec.vx = actor_shadowSource[i]->pos.x - actor->position.vx;
		diffVec.vy = actor_shadowSource[i]->pos.y - (actor->position.vy); // + (37<<3));
		diffVec.vz = actor_shadowSource[i]->pos.z - actor->position.vz;

#ifdef OVERFLOW_CHECKS
		distance = Magnitude(&diffVec);
		if(!distance)
			break;
#else
		distance = fast_sqrt(SQR(diffVec.vx) + SQR(diffVec.vy) + SQR(diffVec.vz));
		if(distance <= 65535)
		{
//			PRINTF("shadow distance overflow... aborting...\n");
			break;
		}
		distance >>= 16;
#endif
		if(distance < actor_shadowSource[i]->range)
		{
//			darkness = (EXLIGHT_SHADOW_RGB * (actor_lightSource[i]->range - distance)) / distance;

// the dog doesn't work if we do this check. Tsk!
/*
			if(actor->psiData.flags & ACTOR_MOTIONBONE)
			{
				PSIOBJECT *child;
				for(child = world->child; child; child = child->next)
				{
					if(child->child)
					{
						fredShadow(actor_lightSource[i],actor,child,darkness);
					}
				}
			}
			else */
			{
				fredShadow(actor_shadowSource[i],actor,world,floor_y);	//darkness);
			}
		}
	}

// Restore everything to work the old way
#ifdef LIGHT_ZERO_CAMERA
	GsWSMATRIX.t[0] = t[0];
	GsWSMATRIX.t[1] = t[1];
	GsWSMATRIX.t[2] = t[2];

#endif
}


// Stick a simple ellipse shadow under the actor. Front distance, Back distance, Width. 200,-200,200 gives an oval 0 0 200 gives round.
void EXLIGHT_SimpleShadow(ACTOR *actor, int f, int b, int w)
{
	int floor_y;
	int t[3];
	VECTOR av,bv;
	int s,c;

// Move the camera to zero for the duration...
#ifdef LIGHT_ZERO_CAMERA
	t[0] = GsWSMATRIX.t[0];
	t[1] = GsWSMATRIX.t[1];
	t[2] = GsWSMATRIX.t[2];
	GsWSMATRIX.t[0] = 0;
	GsWSMATRIX.t[1] = 0;
	GsWSMATRIX.t[2] = 0;
	gte_SetTransMatrix(&GsWSMATRIX);

#endif


	floor_y = getHeightAt(actor->position.vx<<9,actor->position.vy<<9,actor->position.vz<<9);
	floor_y = floor_y >> 9;
#ifdef _WINDOWS
	floor_y = floor_y + actor->position.vy;
#else
	floor_y += actor->position.vy;
#endif
	av = actor->position;
	bv = actor->position;

	av.vx -= mycamera.vpx;
	av.vz -= mycamera.vpz;
	bv.vx -= mycamera.vpx;
	bv.vz -= mycamera.vpz;
	s = rsin(actor->psiData.object->rotate.vy);
	c = rcos(actor->psiData.object->rotate.vy);

	av.vx -= (f * s) >> 12;
	av.vz -= (f * c) >> 12;

	bv.vx -= (b * s) >> 12;
	bv.vz -= (b * c) >> 12;

	av.vy = bv.vy = floor_y - mycamera.vpy;

// tbd - it'd be nice if the shadow darkness reflected the incident light
	EXLIGHT_shadowEllipse(&av,&bv,w,32);
// Restore everything to work the old way
#ifdef LIGHT_ZERO_CAMERA
	GsWSMATRIX.t[0] = t[0];
	GsWSMATRIX.t[1] = t[1];
	GsWSMATRIX.t[2] = t[2];

#endif
}



void EXLIGHT_SetupActorLights(ACTOR *actor)
{
	EXLIGHT_TYPE	*current, *nearest = NULL;
	EXLIGHT_TYPE	*lightSource[3];
	int				lightnum,shadnum;
	int				dx, dy, dz, xzdistsqrd, xzdist, dist, i, range;
	int				scaledr[3], scaledg[3], scaledb[3], neardist = -1;
	TVECTOR			lightvec[3];
	ULONG			amb_r, amb_g, amb_b;
	int in_alert_zone;

	if(EXLIGHT_Data->alertcheck)
		triggering_light = NULL;

	if(EXLIGHT_Data->root.next == NULL)
	{
		EXLIGHT_SetupDefaultActorLights();
		return;
	}

	TIMER_START(TIMER_LIGHTING);

	lightnum = 0;
	shadnum = 0;

	amb_r = EXLIGHT_Data->ambient.r;
	amb_g = EXLIGHT_Data->ambient.g;
	amb_b = EXLIGHT_Data->ambient.b;
	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		if(!(current->flags & LIGHTFLAG_DISABLED))
		{
			in_alert_zone = 0;
			if(current->flags & LIGHTFLAG_BOX)
			{
	// adjust ambient
	//void psiSetAmbient(int r, int g, int b)


				dx = actor->position.vx - current->pos.x;
				dy = actor->position.vy - current->pos.y;
				dz = actor->position.vz - current->pos.z;

				if(
					   dx > -current->range
					&& dy > -current->range
					&& dz > -current->range
					&& dx < current->boxsize.x+current->range
					&& dy < current->boxsize.y+current->range
					&& dz < current->boxsize.z+current->range
					)
				{

					
					if(dx >= 0 && dy >= 0 && dz >= 0
						&& dx <= current->boxsize.x
						&& dy <= current->boxsize.y
						&& dz <= current->boxsize.z
						)
					{
						amb_r = current->col.r<<4;
						amb_g = current->col.g<<4;
						amb_b = current->col.b<<4;
						if(in_alert_zone < 1)
							in_alert_zone = 1;
					}
					else
					{
						int r,g,b;

						if(dx >= current->boxsize.x)
							dx = dx - current->boxsize.x;
						else if (dx < 0)
							dx = -dx;
						else
							dx = 0;

						if(dy >= current->boxsize.y)
							dy = dy - current->boxsize.y;
						else if (dy < 0)
							dy = -dy;
						else
							dy = 0;

						if(dz >= current->boxsize.z)
							dz = dz - current->boxsize.z;
						else if (dz < 0)
							dz = -dz;
						else
							dz = 0;

						if(dx < dz)
							dx = dz;
						if(dx < dy)
							dx = dy;

						dx = dx * 4096/current->range;
						dy = 4096 - dx;

						if(dy > 3000 && in_alert_zone < 1)
							in_alert_zone = 1;

						amb_r = ((EXLIGHT_Data->ambient.r * dx) + ((current->col.r<<4) * dy)) / 4096;
						amb_g = ((EXLIGHT_Data->ambient.g * dx) + ((current->col.g<<4) * dy)) / 4096;
						amb_b = ((EXLIGHT_Data->ambient.b * dx) + ((current->col.b<<4) * dy)) / 4096;
					}
				}
			}
			else if(current->flags & (LIGHTFLAG_SHADOW+LIGHTFLAG_SHADE))
			{
			// SL: if we've run out of hardware lights, then quit looking for more...
//				if(lightnum >= MAX_LIGHT_LIGHTS)
//					break;

				// SL: quick box test to see if the actor's within range of this light...
				dx = actor->position.vx - current->pos.x;
				dy = actor->position.vy - current->pos.y;
				dz = actor->position.vz - current->pos.z;

				range = current->range;

				// SL: check and continue if out of range...
				if( (abs(dx) > range) || (abs(dy) > range) || (abs(dz) > range) )
					continue;

#ifdef OVERFLOW_CHECKS
				xzdist = Magnitude2D(dx,dz);
				if( xzdist > range )
					continue;
				dist = Magnitude2D(xzdist,dy);
				if( dist > range )
					continue;
#else
				// SL: check if the actor is within the range of this particular light...
				xzdistsqrd = SQR(dx) + SQR(dz);
				xzdist = fast_sqrt( xzdistsqrd ) >> 16;
				// SL: out of range?
				if( xzdist > range )
					continue;
				// SL: get total 3d distance from light
				dist = fast_sqrt( xzdistsqrd + SQR(dy) ) >> 16;
				// SL: out of range?
				if( dist > range )
					continue;
#endif

//
				dist = range - dist;

				if(current->flags & (LIGHTFLAG_CONE))
				{
					int perp,along;
					perp = EXLIGHT_conecheck((VECTOR *)&current->pos.x, (VECTOR *)&current->boxsize, &actor->position, &along);
					if(perp == -1 || along == -1)
						continue;

					if(perp > (along * 4 / current->spot_size))	//along / 4)
						continue;
					perp = perp * 4096 / (along * 4 / current->spot_size);	//(along/4);

					if(perp > 3000)
					{
//						printf("scale %d %d\n",(4096 - perp),(4096 - 3000));
						dist = dist * (4096 - perp) / (4096 - 3000);
					}
				}

				if(dist > range / 3)
				{
//					if((current->flags & LIGHTFLAG_ALERT) && EXLIGHT_Data->alertcheck)
//					{
//						utilPrintf("dist %d,range %d\n",dist,range);
//					}
					if(current->flags & (LIGHTFLAG_CONE))
					{
						VECTOR v1;
						VECTOR v2;
						COLLBOX hitBox;
						int rv;
// Collision test...
						v1 = actor->position;
						v2.vx = current->pos.x;
						v2.vy = current->pos.y;
						v2.vz = current->pos.z;

						v1.vy -= 400;	// Ginger's head

						v1.vx = v1.vx <<9;
						v1.vy = v1.vy <<9;
						v1.vz = v1.vz <<9;

						v2.vx = v2.vx <<9;
						v2.vy = v2.vy <<9;
						v2.vz = v2.vz <<9;

						rv = collideBoxIsectCheck(&v1,&v2);

						if(!rv)
						{
							in_alert_zone = 2;
						}
					}
					else
					{
						if(in_alert_zone < 1)
						{
							in_alert_zone = 1;
						}
					}
				}

				if(current->flags & (LIGHTFLAG_SHADE))
				{
					if(lightnum < MAX_LIGHT_LIGHTS)
					{
						scaledr[lightnum] = ((int)(current->col.r) * (dist) ) / range;
						scaledg[lightnum] = ((int)(current->col.g) * (dist) ) / range;
						scaledb[lightnum] = ((int)(current->col.b) * (dist) ) / range;

						// SL: write in the vector from the light to the actor...
						lightvec[lightnum].x = dx;
						lightvec[lightnum].y = dy;
						lightvec[lightnum].z = dz;

//						lightSource[lightnum] = current;

						// SL: increment the current light
						lightnum++;
					}
				}

				if(current->flags & (LIGHTFLAG_SHADOW))
				{
					if(shadnum < MAX_SHADOW_LIGHTS)
					{
						actor_shadowSource[shadnum] = current;
						shadnum++;
					}
				}

				if(EXLIGHT_Data->alertcheck && (current->flags & LIGHTFLAG_ALERT) && in_alert_zone)
				{

/*					DB("In-a-light Hourglass Trigger\n");
					if(current->flags & (LIGHTFLAG_CONE))
					{
						DB(" (Cone)\n");
					}
*/


//					nmeDogStartHourglass(in_alert_zone);
					if(current->flags & (LIGHTFLAG_CONE))
					{
						hourglass2Inform(HOURGLASS_IN_SEARCHLIGHT,&actor->position);
					}
					else
					{
						hourglass2Inform(HOURGLASS_IN_LIGHT,&actor->position);
						triggering_light = current;

						// "current->triggered" is monitored by enemies.
						// If an enemy abandoned its chase when a light was in triggered state,
						// then the enemy isn't interested in future alarm calls until you've left that light.
						// (Of particular relevance in a1_housext_n, by the stairs & the coalshed.)
					}

				}
			}
		}
	}




	// SL: hokay, setup dem hardware lights...
	for(i = 0; i < MAX_LIGHT_LIGHTS; i++)
	{
		// SL: if this light wasn't setup...
		if( i >= lightnum )
		{
			psiSetLight(i, 0, 0, 0, 1, 1, 1);
//			actor_lightSource[i] = NULL;
		}
		else
		{
			psiSetLight(i, scaledr[i], scaledg[i], scaledb[i], lightvec[i].x, lightvec[i].y, lightvec[i].z);
//			psiSetLight(i, 255, 255, 255, lightvec[i].x, -lightvec[i].y, lightvec[i].z);
//			actor_lightSource[i] = lightSource[i];
		}
	}

	for(i = shadnum; i < MAX_SHADOW_LIGHTS; i++)
	{
		actor_shadowSource[i] = NULL;
	}



// normal is 1024, max is 4096
	psiSetAmbient(amb_r,amb_g,amb_b);

	// SL: store in this actor what the nearest light to him is...
//	actor->nearestLight = nearest;

	TIMER_STOP(TIMER_LIGHTING);
	// SL: ACE!!
}


void EXLIGHT_SetupActorLightsAlert(ACTOR *actor)
{
// Made so the welly & gnome can run around in the lights without fear
	EXLIGHT_Data->alertcheck = 1;

	if(characterVars.pHideChar)
	{
		if(characterVars.pHideChar->pEnemy->runFlags & RUNFLAGS_VISIBLE)
		{
			EXLIGHT_Data->alertcheck = 0;
		}
	}

	EXLIGHT_SetupActorLights(actor);
	EXLIGHT_Data->alertcheck = 0;
}



#ifdef DREAMCAST
int spotlightYBodge = 175;
#endif
void EXLIGHT_MergeLights(EXLIGHT_TYPE *list)
{
	EXLIGHT_TYPE *end, *oldEnd = NULL;
// "light->meshswaps" has already been pointer-to-integer-ed in puzzles.c)

	for(end = list; end; end = end->next)
	{
		oldEnd=end;
		if(end->flags & LIGHTFLAG_MESHSWAP)
		{
			FMA_WORLD2 *world;
			int i;

			world = (void *)BFF_FindObject(BFF_FMA_WORLD2_ID,utilStr2CRC("LNDMAP"));
			for(i = 0; i < end->n_meshswaps * 2; i++)
			{
				end->meshswaps[i] = (int)mapdrawFindInstance(world, end->meshswaps[i]);
			}
		}
		if(end->flags & LIGHTFLAG_MOBILE)
		{
			LIGHT_MOBILESTR *mob = (void *)end->meshswaps;
			end->boxsize.x = mob->points[0].vx;
			end->boxsize.y = mob->points[0].vy;
			end->boxsize.z = mob->points[0].vz;
#ifdef DREAMCAST
			{
				int j;
				for(j = 0;j < mob->n_points;j++)
				{
					mob->points[j].vy += spotlightYBodge;
				}
			}
#endif
		}
		if(!end->shadow_darkness)
			end->shadow_darkness = 64;
		if(!end->spot_size)
			end->spot_size = 16;

	}
	oldEnd->next = EXLIGHT_Data->root.next;
	EXLIGHT_Data->root.next = list;
}

EXLIGHT_TYPE *EXLIGHT_FindLight(int tag)
{
	EXLIGHT_TYPE	*current;
	for(current = EXLIGHT_Data->root.next; current != NULL; current = current->next)
	{
		if(current->tag == tag)
			return current;
	}
	return NULL;
}

void EXLIGHT_EnableLight(EXLIGHT_TYPE *light, int enable)
{
	if(enable)
	{
		light->flags &= ~LIGHTFLAG_DISABLED;
	}
	else
	{
		light->flags |= LIGHTFLAG_DISABLED;
	}
}
int EXLIGHT_IsLightEnabled(EXLIGHT_TYPE *light)
{
	return(!(light->flags & LIGHTFLAG_DISABLED));
}

void EXLIGHT_AlertLight(EXLIGHT_TYPE *light, int enable)
{
	if(enable)
	{
		light->flags &= ~LIGHTFLAG_ALERT;
	}
	else
	{
		light->flags |= LIGHTFLAG_ALERT;
	}
}


void EXLIGHT_SetAmbient(unsigned int bgr)
{
	EXLIGHT_Data->ambient.r = (bgr << 4) & 0xff0;
	EXLIGHT_Data->ambient.g = (bgr >> 4) & 0xff0;
	EXLIGHT_Data->ambient.b = (bgr >> 12) & 0xff0;
	utilPrintf("ambient set to %d %d %d\n",
		EXLIGHT_Data->ambient.r,
		EXLIGHT_Data->ambient.g,
		EXLIGHT_Data->ambient.b);
}

