#include "glover.h"

//rain variables

SPRITEX *pSnowFlake=NULL;
SPRITEX *pRain=NULL;

RAINTYPE	*rain;
LONG		rainAngle;
static UBYTE rainbuilt=0;	//to initialise rain co-ords
LONG 	oldRainAngle,rainHeight;
UBYTE		amountOfRain=AMOUNT_OF_RAIN,rainType;
UBYTE weather=0;
////////////////

int weatherType=WEATHER_NULL;

ULONG xFlakeSize, yFlakeSize;
/*********************************************************************************/
void weatherInitialise(int w)
{
	weatherType=w;
	weather=RAIN;
	amountOfRain=0;
	rainType=3;

	
	if(weatherType & (WEATHER_SNOW|WEATHER_RAIN))
	{
		if(weatherType & WEATHER_SNOW)
		{
			int i;

			pSnowFlake=textureFindInAllBanks("AI_SNOW");
			ASSERT(pSnowFlake);

			xFlakeSize=0;
			yFlakeSize=0;

			for(i=0; i<3; i++)
			{
				xFlakeSize=xFlakeSize<<8;
				xFlakeSize|=(pSnowFlake->w/(5-i));

				yFlakeSize=yFlakeSize<<8;
				yFlakeSize|=(pSnowFlake->h/(5-i));
			}
		}
		else
		{
			pRain=textureFindInAllBanks("GRSCALE");
			ASSERT(pRain);
		}

		(UBYTE*)rain=MALLOC(sizeof(RAINTYPE)*3*AMOUNT_OF_RAIN,"RAIN/SNOW");
		ASSERT(rain);
	}
}
/********************************************************************************/
void weatherCleanUp(void)
{
	if (pSnowFlake) FREENULL(pSnowFlake);
	if (pRain) FREENULL(pRain);
	FREENULL(rain);
}
/********************************************************************************/
void weatherDo(void)
{
	static int thunderTimer=-1;

	if(!weatherType)
		return;

	if(gameCtrl.gameActive)
	{
		// These ones just do sound effects
		if(weatherType & WEATHER_BIRDY)
		{
			if(!(RANDOM256() & 63))
			{
				VECTOR temp;
				int ang;
				ang = RANDOM256() << 4;
				temp.vx = (CamVars.camera.vpx<<12) + (rsin(ang)<<4);
				temp.vy = (CamVars.camera.vpy<<12);
				temp.vz = (CamVars.camera.vpz<<12) + (rcos(ang)<<4);

				if(world == HUB)
				{
					sfxPlay3D(levelFX,SFX_BIRD_TWEET_1+(RANDOM256() % 6),&temp);
				}
				else if (world == PIRATES)
				{
					sfxPlay3D(levelFX,SFX_BIRD_TWEET_1_PI+(RANDOM256() & 3),&temp);
				}
			}
		}

		if(weatherType & WEATHER_THUNDER)
		{
			if(randomInt(128)==0)
			{ // do a lightning flash
				effectsStartOverlay(3, 0xffffff);
				thunderTimer=RANDOM256()&31;
			}

			if((thunderTimer--)==0 && world == FORTRESS)
				sfxPlay(levelFX,SFX_THUNDER);
		}
		else
		{
			thunderTimer=-1;
		}
	}

	// make it snow ...
	if(weatherType & WEATHER_SNOW)	Rain(1); // snow really
		
	if(weatherType & WEATHER_RAIN) 	Rain(0);
}


/*******************************************************************************************/
void MoveRain(BYTE snow)
{

	ULONG 		i;
	LONG 		rAngle;
	RAINTYPE	*rainPtr=rain;
	UBYTE 		tmp;
	rAngle=rainAngle;

	tmp=3;//default

	if(framerate==1)tmp=4;
	if(framerate==2)tmp=3;
	if(framerate==3)tmp=2;

	for(i=0;i<amountOfRain;i++)
	{
		if(snow==NO)	//rain movement
		{
				rainPtr->x+=(rAngle*3)/tmp;
   				(rainPtr+amountOfRain)->x+=(rAngle*3)/tmp;
   				(rainPtr+(amountOfRain*2))->x+=(rAngle*3)/tmp;
			
				rainPtr->y+=48/tmp;
				(rainPtr+amountOfRain)->y+=36/tmp;
				(rainPtr+(amountOfRain*2))->y+=24/tmp;

		}
		else		//snow
		{
	   		rainPtr->x+=((rAngle*3)/2)/tmp;
 			rainPtr->x+=(((RANDOM256()&7)-4)*3)/tmp;
  			rainPtr->y+=18/tmp;
   			(rainPtr+amountOfRain)->x+=((rAngle*3)/2)/tmp+((((RANDOM256()&7)-4)*3)/tmp);
	 		(rainPtr+amountOfRain)->y+=12/tmp;
	   		(rainPtr+(amountOfRain*2))->x+=((rAngle*3)/2)/tmp+((((RANDOM256()&7)-4)*3)/tmp);
			(rainPtr+(amountOfRain*2))->y+=6/tmp;
		}
		//edge wrap
		if(rainPtr->x<0)					 rainPtr->x+=512;
		if(rainPtr->x>512)					 rainPtr->x-=512;
		if((rainPtr+amountOfRain)->x<0)		 (rainPtr+amountOfRain)->x+=512;
		if((rainPtr+amountOfRain)->x>512)	 (rainPtr+amountOfRain)->x-=512;
		if((rainPtr+(amountOfRain*2))->x<0)	 (rainPtr+(amountOfRain*2))->x+=512;
		if((rainPtr+(amountOfRain*2))->x>512)(rainPtr+(amountOfRain*2))->x-=512;
		   																			 //too big for random256
		if(rainPtr->y>256){rainPtr->y-=246-((RANDOM256() MOD 20));rainPtr->x=RANDOM256()*2;}
		if((rainPtr+amountOfRain)->y>(150+(rainHeight*2))){(rainPtr+amountOfRain)->y-=246-(RANDOM256() MOD 20);(rainPtr+amountOfRain)->x=RANDOM256()*2;	}
  		if((rainPtr+(amountOfRain*2))->y>(70+(rainHeight*4))){(rainPtr+(amountOfRain*2))->y-=246-(RANDOM256() MOD 20);(rainPtr+(amountOfRain*2))->x=RANDOM256()*2;	}
		rainPtr++;
	}
}
/********************************************************************************************************************/
void DrawRain(UBYTE snow)
{
ULONG i,dropImage,range;
GsOTA	 *otptr;
POLY_FT4 *poly;
LONG tmp_len; 
SPRITEX* spr;
ULONG colour;								
RAINTYPE	*rainPtr=rain;

LONG rHeight,rAngle;

	rAngle=rainAngle;
	rHeight=rainHeight;

	if (TOOMANYPOLYS(2*amountOfRain*3*MAXPACKETSIZE,"rain"))return;

	dropImage=128;
	if(snow==NO)dropImage=24;
	colour=(dropImage)|(dropImage<<8)|(dropImage<<16)|((GPU_COM_TF4|2)<<24);

	otptr=(GsOTA*)(PolyList->org+((100 &MAXDEPTH)>>PolyList->shift));
	spr=(SPRITEX*)pSnowFlake;	if(snow==NO)spr=pRain;

	poly = (POLY_FT4 *) GsOUT_PACKET_P;
	
	if(snow==YES)
	{
		rHeight/=3;
		rAngle=0;
	}													 

	for(i=amountOfRain*0;i<(amountOfRain*2);i++)
	{
		if(snow==NO) // rain
		{
			poly->x0=rainPtr->x-1+RAIN_X;
			poly->y0=rainPtr->y+RAIN_Y;
			poly->x1=rainPtr->x+1+RAIN_X;
			poly->y1=rainPtr->y+RAIN_Y;
		}

		range=0;
   		tmp_len=18-((rHeight+1)/3)-(5);

		if(i>=amountOfRain/2)
		{
			range=1;
   			tmp_len=18-((rHeight+1)/3)-(10);
		}

		if(i>=amountOfRain)
		{
			range=2;									
   			tmp_len=18-((rHeight+1)/3);
		}

		if(tmp_len<3)tmp_len=3;
		if(snow==YES)tmp_len=3;

		if(snow==NO)
		{	poly->x2=rainPtr->x-2+RAIN_X+(rAngle/(range+1));
			poly->x3=rainPtr->x+2+RAIN_X+(rAngle/(range+1));
			poly->y2=rainPtr->y+(tmp_len/(range+1))+RAIN_Y;
			poly->y3=rainPtr->y+(tmp_len/(range+1))+RAIN_Y;
		}
		else
		{
			//int s=range+2;
			int sx=(xFlakeSize>>(range<<3))&0xff;
			int sy=(yFlakeSize>>(range<<3))&0xff;

			poly->x0=rainPtr->x+RAIN_X-sx;
			poly->y0=rainPtr->y+RAIN_Y-sy;

			poly->x1=rainPtr->x+RAIN_X+sx;
			poly->y1=rainPtr->y+RAIN_Y-sy;

			poly->x2=rainPtr->x+RAIN_X-sx;
			poly->y2=rainPtr->y+RAIN_Y+sy;

			poly->x3=rainPtr->x+RAIN_X+sx;
			poly->y3=rainPtr->y+RAIN_Y+sy;
		}

		///////////PRINTS POLY/////////////
		*(ULONG*)&poly->r0=colour;
   	 	*(ULONG*)&poly->u0=*(ULONG*)&spr->u0;
   		*(ULONG*)&poly->u1=*(ULONG*)&spr->u1;
   		*(ULONG*)&poly->u2=*(ULONG*)&spr->u2;
   		*(ULONG*)&poly->u3=*(ULONG*)&spr->u3;

		poly->tpage=(spr)->tpage|((SEMITRANS_ADD-1)<<5);
	 	
	 	PUTPACKETINTABLE(poly,otptr,POLYTF4_LEN);
		poly++; 	
		/////////////////////////////////////
		rainPtr++;
	}
	GsOUT_PACKET_P=(PACKET*)poly;

}
/********************************************************************************************************************/
void Rain(UBYTE snow)
{
LONG i;
RAINTYPE	*rainPtr=rain;

//   	if(scrn_zoom<64)return;	//don't print weather on mini view
	//DB("raintype=%d\n",rainType);
  
	if(rainType==0)	   // rain/snow dying down
	{
		if(((RANDOM256() MOD 20)==1) && (amountOfRain>0) && gameCtrl.gameActive)
			amountOfRain--;
		if(amountOfRain==0)rainType=1;
	}
	if(rainType==1)
	{
		if (RANDOM256()==1)rainType=2;
	
	}
	if(rainType==2)	//building up to rain/snow
	{
		if(((RANDOM256() MOD 20)==1) && (amountOfRain<30) && gameCtrl.gameActive)
		{
			amountOfRain++;
		}
		if(amountOfRain==30)rainType=3;
	}
	if(rainType==3)
	{
		if((RANDOM256()&127)==1)rainType=0;
	}

	//DB("rain/snow ammount=%d\n",amountOfRain);

	oldRainAngle=rcos(CamVars.angle); //0; //     must work out camera angle!!!     rcos(mapview->angle);
	rainHeight=200; //CamVars.height; //200; // must work out camera height!!         mapview->cameraheight;
	rainHeight-=767;
	rainHeight/=64;

	if(rainbuilt==0)
	{
		for(i=0;i<amountOfRain;i++)
		{
			rainPtr->x=RANDOM256()*2;		
  			rainPtr->y=RANDOM256();		
			//second wave
		  	(rainPtr+amountOfRain)->x=RANDOM256()*2;	
  		  	(rainPtr+amountOfRain)->y=RANDOM256();	
  			//third wave
		  	(rainPtr+(amountOfRain*2))->x=RANDOM256()*2;
  		  	(rainPtr+(amountOfRain*2))->y=RANDOM256();
			rainPtr++;
		}
		rainbuilt=1;
	}
	rainAngle=oldRainAngle/=512;		//-8->8

	if(gameCtrl.gameActive)
		MoveRain(snow);

  	DrawRain(snow);

}
/********************************************************************************************************************/

struct
{
	int duration;
	int timer;
	int cycle;
	int sizeStr;
	int minStr;
	int div;
}QuakeInfo;

void weatherStartEarthquake(int duration, int maxStr, int minStr, int cycles, int flags)
{
//	DB("Starting earthquake (%d, %d, %d, %d, %d)\n", duration, maxStr, minStr, cycles, flags);

	QuakeInfo.timer=QuakeInfo.duration=duration;
	QuakeInfo.cycle=cycles;
	QuakeInfo.minStr=minStr;
	QuakeInfo.sizeStr=maxStr-minStr;
	QuakeInfo.div=(QuakeInfo.sizeStr*4096)/(QuakeInfo.duration/2);
}

void weatherDoEarthquake(void)
{
	int quakeSize, halfQuakeSize, smSize;

	if(QuakeInfo.cycle==0)
		return;

	QuakeInfo.timer--;

	if(QuakeInfo.timer==0)
	{
		QuakeInfo.timer=QuakeInfo.duration;
		QuakeInfo.cycle--;
	}

	// do camera wibbly stuff

	if(QuakeInfo.timer>(QuakeInfo.duration/2))
		smSize=QuakeInfo.duration-QuakeInfo.timer;
	else
		smSize=QuakeInfo.timer;

	quakeSize=(QuakeInfo.minStr*4096)+(smSize*QuakeInfo.div);
	halfQuakeSize=quakeSize/8192;

	CamVars.camera.vpx+=(halfQuakeSize-RANDOM256() MOD (quakeSize/4096));
	CamVars.camera.vpy+=(halfQuakeSize-RANDOM256() MOD (quakeSize/4096));
	CamVars.camera.vpz+=(halfQuakeSize-RANDOM256() MOD (quakeSize/4096));

	//DB("Quake size = %d\n", quakeSize/4096);
	//DB("Quake timer = %d, cycle = %d\n", QuakeInfo.timer, QuakeInfo.cycle);
}

/*******************************************************************************************/
WINDLIST windList;

void windInitialise(void)
{
	windList.head.next = windList.head.prev = &windList.head;
	windList.numEntries = 0;
}

// Checks integrity of wind linked list
void windVerify(void)
{
	WIND_DEFSTR *ptr;
	int c=0;

	for(ptr = windList.head.next;ptr != &windList.head;ptr = ptr->next)
	{
		ASSERT(ptr);
		c++;
	}

	ASSERT(c==windList.numEntries);
}

void windClearUpMessAfterwards(void)
{
	WIND_DEFSTR *ptr;

	windVerify();
	
	for(ptr = windList.head.next; ptr != &windList.head; ptr = ptr->next)
	{
		ptr->prev->next = ptr->next;
		ptr->next->prev = ptr->prev;
		windList.numEntries--;

		FREE(ptr);
	}

	ASSERT(windList.numEntries==0);
}

WIND_DEFSTR *windCreate(void)
{
	WIND_DEFSTR *ptr;
	WIND_DEFSTR *pWind;
	int nParticles=33;	// MUST be a multiple of 3 (NB. that's 3 NOT 4)

	size_t sizeParticles, sizeDepths;

	sizeParticles=	sizeof(VERT)*nParticles;
	sizeDepths=		sizeof(long)*nParticles; // also size of scrXY

	pWind=MALLOC(sizeof(WIND_DEFSTR)+sizeParticles+sizeDepths+sizeDepths, "Wind");
	pWind->nParticles=nParticles;

//	DB("Creating wind. # of particles = %d\n", pWind->nParticles);

	windVerify();

	ptr=windList.head.next;

	pWind->next = ptr;
	pWind->prev = ptr->prev;
	ptr->prev->next = pWind;
	ptr->prev = pWind;
	windList.numEntries++;

	pWind->pParticles=	(VERT *)(((size_t)pWind)+sizeof(WIND_DEFSTR));
	pWind->pDepths=		(long *)(((size_t)pWind->pParticles)+sizeParticles);
	pWind->pScrXY=		(long *)(((size_t)pWind->pDepths)+sizeDepths);

	return pWind;
}

int windUpdateObject(COLLDATA *pColl, WIND_DEFSTR *ptr)
{
	int ret=FALSE;
	VECTOR temp;

	temp.vx=pColl->pPos->vx/4096;
	temp.vy=pColl->pPos->vy/4096;
	temp.vz=pColl->pPos->vz/4096;

	if(isObjectInBox(&ptr->pos, &ptr->size, &temp))
	{
		// we need to treat the y differently from the x & z.
		// for x&z, add to position,
		// for y add to velocity

		ret=TRUE;


		if(ptr->dir.vy) // do y
		{
			pColl->pVel->vy+=(ptr->dir.vy*ptr->str)/4096;
		}

		if(ptr->dir.vx || ptr->dir.vz) // do x & z
		{
			COPYVECTOR(&temp, &ptr->dir);
			temp.vy=0;

			SCALEVECTOR(&temp, ptr->str); // multiply wind direction by wind strength
			ADDTOVECTOR(pColl->pPos, &temp);
		}
	}

	return ret;
}

void windDrawParticles(void)
{
	WIND_DEFSTR *ptr;

	TILE_1	*si;
	GsOTA	*otptr;

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

	si = (TILE_1 *) GsOUT_PACKET_P; 

	for(ptr = windList.head.next; ptr != &windList.head; ptr = ptr->next)
	{
		int i;

		if(!ptr->active)
			continue;

		if(!ptr->onScreen)
			continue;

		transformVertexListA(ptr->pParticles, ptr->nParticles, ptr->pScrXY, ptr->pDepths);

		for(i=0; i<ptr->nParticles; i++)
		{
			long depth=ptr->pDepths[i]/4;

			if((depth<0) || (depth>=MAXOTZ))
				continue;

			// code is 0x68, and LEN = 0x2000000  WHY AREN'T THESE DOCUMENTED ?
			*(ULONG	*)&si->r0= 0xffffff | ((0x68)<<24);
			*(ULONG	*)&si->x0= ptr->pScrXY[i];

			//otptr=(GsOTA*)(PolyList->org+(ptr->pDepths[i]));
			otptr=(GsOTA*)(PolyList->org+depth);

			PUTPACKETINTABLE(si,otptr, 0x2000000);		
			INCPOLYSDRAWN;

			si++;							//incr. packet building address by sizeof TILE1
		}
	}

	GsOUT_PACKET_P=(PACKET*)si;		 	//return new packet building address
}

/*#define	WINDCLIP(A, B, C)	{ \
if(ptr->pParticles[i].A>=(ptr->size.A+ptr->pos.A)) \
{ \
	ptr->pParticles[i].B=random(ptr->size.B)+ptr->pos.B; \
	ptr->pParticles[i].C=random(ptr->size.C)+ptr->pos.C; \
	ptr->pParticles[i].A=ptr->pos.A; \
} \
else \
{ \
	if(ptr->pParticles[i].A<ptr->pos.A) \
	{ \
		ptr->pParticles[i].B=random(ptr->size.B)+ptr->pos.B; \
		ptr->pParticles[i].C=random(ptr->size.C)+ptr->pos.C; \
		ptr->pParticles[i].A=(ptr->size.A+ptr->pos.A); \
	} \
} \
}
*/

//changed to RANDOM256  becuase its so much faster. Although possible to have larger
//values than 256. You cant notice (well I couldnt!)  Andrew.
#define	WINDCLIP(A, B, C)	{ \
if(ptr->pParticles[i].A>=(ptr->size.A+ptr->pos.A)) \
{ \
	ptr->pParticles[i].B=RANDOM256()MOD(ptr->size.B)+ptr->pos.B; \
	ptr->pParticles[i].C=RANDOM256()MOD(ptr->size.C)+ptr->pos.C; \
	ptr->pParticles[i].A=ptr->pos.A; \
} \
else \
{ \
	if(ptr->pParticles[i].A<ptr->pos.A) \
	{ \
		ptr->pParticles[i].B=RANDOM256()MOD(ptr->size.B)+ptr->pos.B; \
		ptr->pParticles[i].C=RANDOM256()MOD(ptr->size.C)+ptr->pos.C; \
		ptr->pParticles[i].A=(ptr->size.A+ptr->pos.A); \
	} \
} \
}

void windUpdate(void)	 ///There is no bounding box check on wind. So if it active, whereever it is
//on the level. it goes through processing every pixel.

{
	WIND_DEFSTR	*ptr;
	int		i,v;
	VERT	pBBox[9];
	int		totalchecks;
	XYSHORT	*xy;

	GloveCtrl.inWind=FALSE;
	BallCtrl.inWind=FALSE;

	if (windList.numEntries==0)return; //no wind around
	windDrawParticles();
//	DB("updating wind\n");
	for(ptr = windList.head.next; ptr != &windList.head; ptr = ptr->next)
	{
		if(!ptr->active)
			continue;

		ptr->onScreen=FALSE;

		for(v=0; v<8; v++)
		{	// cunning way to build a bounding box
			pBBox[v].vx=(v&1)?0:ptr->size.vx;
			pBBox[v].vy=(v&2)?0:ptr->size.vy;
			pBBox[v].vz=(v&4)?0:ptr->size.vz;

			pBBox[v].vx+=ptr->pos.vx;
			pBBox[v].vy+=ptr->pos.vy;
			pBBox[v].vz+=ptr->pos.vz;
		}

		transformVertexList(pBBox, 9, transVerts, transDepths);

		(ULONG*)xy=transVerts;
//		nClips=0;
		totalchecks=0xffff;
//		subDivFlag=0;
//		usefog = 0;

		for(v=0; v<8; v++)
		{
			int check=0;

			if(xy[v].x<-256)check|=OFFLEFT;
			if(xy[v].x>256 )check|=OFFRIGHT;
			if(xy[v].y<-128)check|=OFFUP;
			if(xy[v].y>128 )check|=OFFDOWN;
	  		if(transDepths[v]<=0)check|=OFFFRONT;

			if(transDepths[v]>lscapeFogFlatZ)
				check |= OFFBACK;
//	  		if(transDepths[v]>lscapeFogStartZ)
//				usefog = 1;

			totalchecks&=check;
		}

		if(totalchecks==0)
		{
			ptr->onScreen=TRUE;
		}
		else
		{
			continue;
		}

		if(	(GloveCtrl.action!=HAND_SLAM) &&
			(GloveCtrl.action!=HAND_CASTSPELL) &&
			(GloveCtrl.action!=HAND_PREWHACK) &&
			(GloveCtrl.action!=HAND_THROWAIM) &&
			(GloveCtrl.action!=HAND_AIRBOURNEWHACK) &&
			(GloveCtrl.action!=HAND_ROTOR) &&
			(GloveCtrl.action!=HAND_LOCATE))
		{
			if(windUpdateObject(&gloveColl, ptr))
				GloveCtrl.inWind=TRUE;
		}

		if((GloveCtrl.action!=HAND_CASTSPELL))// && (GloveCtrl.lastAction!=HAND_JOINED))
		{
			if(windUpdateObject(&ballColl, ptr))
				BallCtrl.inWind=TRUE;
		}

		// now update the wind particles
		for(i=0; i<ptr->nParticles; i++)
		{
			ptr->pParticles[i].vx+=(ptr->dir.vx*ptr->str)/0x200000;
			ptr->pParticles[i].vy-=(ptr->dir.vy*ptr->str)/0x200000;
			ptr->pParticles[i].vz+=(ptr->dir.vz*ptr->str)/0x200000;


			//printf(" wind size %d,%d,%d\n", ptr->size.vx,ptr->size.vy,ptr->size.vz);

			WINDCLIP(vx, vy,vz);
			WINDCLIP(vy, vx,vz);
			WINDCLIP(vz, vx,vy);

		}

		//DB("pParticles = %p, nParticles = %p, pScrXY = %p, pDepths = %p\n", ptr->pParticles, ptr->nParticles, ptr->pScrXY, ptr->pDepths);
	}
}

void windSetValues(int tag, int activeBool)
{
	WIND_DEFSTR *ptr;

	for(ptr = windList.head.next;ptr != &windList.head;ptr = ptr->next)
	{
		ASSERT(ptr);

		if(ptr->tag==tag)
		{
//			DB("Setting wind (tag %d) to %d\n", tag, activeBool);
			ptr->active=activeBool;

			if(activeBool)
			{
				int i;

				//ptr->nParticles=(ABS(ptr->size.vx*ptr->size.vy*ptr->size.vz))>>12;
				//DB("nParticles = %d\n", ptr->nParticles);

				for(i=0; i<ptr->nParticles; i++)
				{
					ptr->pParticles[i].vx=random(ptr->size.vx)+ptr->pos.vx;
					ptr->pParticles[i].vy=random(ptr->size.vy)+ptr->pos.vy;
					ptr->pParticles[i].vz=random(ptr->size.vz)+ptr->pos.vz;
				}
			}
		}
	}
}

/*******************************************************************************************/
LIGHTNINGLIST lightningList;

int drawFrame=0;

void lightningInitialise(void)
{
	lightningList.head.next = lightningList.head.prev = &lightningList.head;
	lightningList.numEntries = 0;
}

// Checks integrity of lightning linked list
void lightningVerify(void)
{
	LIGHTNING_DEFSTR *ptr;
	int c=0;

	for(ptr = lightningList.head.next;ptr != &lightningList.head;ptr = ptr->next)
	{
		ASSERT(ptr);
		c++;
	}
	ASSERT(c==lightningList.numEntries);
}

void lightningClearUpMessAfterwards(void)
{
	LIGHTNING_DEFSTR *ptr;

	lightningVerify();
	
	for(ptr = lightningList.head.next; ptr != &lightningList.head; ptr = ptr->next)
	{
		ptr->prev->next = ptr->next;
		ptr->next->prev = ptr->prev;
		lightningList.numEntries--;

		FREE(ptr);
	}
	ASSERT(lightningList.numEntries==0);
}

LIGHTNING_DEFSTR *lightningCreate(void)
{
	LIGHTNING_DEFSTR *ptr;
	LIGHTNING_DEFSTR *pWind=MALLOC(sizeof(LIGHTNING_DEFSTR), "Wind");

	lightningVerify();

	ptr=lightningList.head.next;

	pWind->next = ptr;
	pWind->prev = ptr->prev;
	ptr->prev->next = pWind;
	ptr->prev = pWind;
	lightningList.numEntries++;

	return pWind;
}

int lightningIsModelInRange(VECTOR *pPos, int radius)
{
	LIGHTNING_DEFSTR *ptr;
//	DYNCOLLBOX *pPlat1, *pPlat2;

	SVECTOR *v1, *v2;
	VECTOR a,b;
	int d,e,f;
	int dp1,dp2;
	VECTOR p1, p2;
	VECTOR pos;
	int calc;

	// PLEASE NOTE: If this ASSERT fails, it means that lightningDrawAll() has not been called this frame,
	// before this function. It needs to be.
	ASSERT(drawFrame==frame);

	pos.vx=pPos->vx/4096;
	pos.vy=pPos->vy/4096;
	pos.vz=pPos->vz/4096;

	for(ptr = lightningList.head.next; ptr != &lightningList.head; ptr = ptr->next)
	{
		ASSERT(ptr);

		if(!ptr->active)
			continue;

		v1=ptr->calcPos;
		v2=ptr->calcPos+1;

		// work out whether lightning has hurt the ball or glove ...

		a.vx=v2->vx-v1->vx;
		a.vy=v2->vy-v1->vy;
		a.vz=v2->vz-v1->vz;

		SUBVECTOR(&p1, v1, &pos);
		SUBVECTOR(&p2, v2, &pos);

		dp1=DotProduct(&a, &p1);
		dp2=DotProduct(&a, &p2);

		if(dp1 < 0 && dp2 > 0)
		{
			b.vx=(pos.vx)-v1->vx;
			b.vy=(pos.vy)-v1->vy;
			b.vz=(pos.vz)-v1->vz;

			MakeUnit(&a);
			
			e=DotProduct(&a, &b);
			f=Magnitude(&b);
			//d=fast_sqrt((f*f)-(e*e))>>4;
			calc=(f*f)-(e*e);

//			ASSERT(calc >= 0);
			if(calc < 0)
			{
// this happens due to maths error when e & f are within 1 or 2 of each other
// (ie, you're virtually *on* the lightning, and should die)
				DB("weather overflow %d^2 - %d^2 = %d\n",f,e,calc);
				return TRUE;
			}
			else
			{
				FASTSQRT(d,calc);
				d=d>>4;
				//DB("v1=%d,%d,%d v2=%d,%d,%d\n", v1.vx, v1.vy, v1.vz, v2.vx, v2.vy, v2.vz);
				//DB("You are within %d thingies of lightning\n", d);

				if(d<=(radius*2))
				{
					//return(FALSE);
					//DB("You are up shit street\n");
					return(TRUE);
				}
			}
		}
	}

	return(FALSE);
}

void lightningDrawAll(void) // This MUST be called BEFORE lightningIsModelInRange()
{
	LIGHTNING_DEFSTR *ptr;
	DYNCOLLBOX *pPlat[2];
	SVECTOR v[2];
	
	drawFrame=frame;

	//DB("lightningDrawAll ...\n");
	for(ptr = lightningList.head.next; ptr != &lightningList.head; ptr = ptr->next)
	{
		int i;

		ASSERT(ptr);

		ptr->active=TRUE;

		for(i=0; i<2; i++)
		{
			loadlndFindPlatform(ptr->pTag[i], NULL, pPlat+i);
			ASSERT(pPlat[i]);

			if(!pPlat[i]->active)
				ptr->active=FALSE;

			v[i].vx=pPlat[i]->cumPos.vx/4096;
			v[i].vy=pPlat[i]->cumPos.vy/4096;
			v[i].vz=pPlat[i]->cumPos.vz/4096;
			
			*(ULONG *)&(ptr->calcPos[i].vx)=*(ULONG *)(v+i);
			ptr->calcPos[i].vz=v[i].vz;
		}

		if(!ptr->active)
		{
			continue;
		}

		if(!(activeframe & 7) && world == FORTRESS)
		{
			if(!(activeframe & 8))
				sfxPlay3D(levelFX,SFX_FRANKIE_ATTACK_LOOPED,&pPlat[0]->cumPos);
			else
				sfxPlay3D(levelFX,SFX_FRANKIE_ATTACK_LOOPED,&pPlat[1]->cumPos);
		}
//ULONG sfxPlay3D(int vab,int sampleNum, VECTOR *position);


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

		effectsDrawLightningStrand(v,v+1, *(ULONG *)&(ptr->colour));
	}
}

