/*

	This file is part of Frogger2, (c) 1999 Interactive Studios Ltd.


	File		: sprite.c
	Programmer	: Jim Hubbard
	Date		: 25/02/00

----------------------------------------------------------------------------------------------- */

#define F3DEX_GBI_2

#include <isltex.h>
#include <islmem.h>
#include <islutil.h>
#include <stdio.h>

//#include "ultra64.h"
#include "sprite.h"
#ifdef PSX_VERSION
#include "textures.h"
#include "specfx.h"
#endif
#include "maths.h"
#include "newpsx.h"
#include "Shell.h"
#include "Main.h"


#ifdef PC_VERSION
#define MAX_SPRITES	512
#else
#define MAX_SPRITES	512
#endif

FOG fog = {120,120,160,FOG_OFF,995,5000};

SPRITELIST sprList;



/*	--------------------------------------------------------------------------------
	Function		: AllocateSprites
	Purpose			: Find a number of sprites and return a sublist
	Parameters		: number of sprites
	Returns			: pointer to first one
	Info			: 
*/
SPRITE *AllocateSprites( int number )
{
	SPRITE *s;
	// Return if allocation is impossible for any reason
	if( (number <= 0) || (sprList.stackPtr-number < 0) || (number >= MAX_SPRITES-sprList.count) ) return NULL;

	// Now we can go and allocate sprites with gay abandon
	while( number-- )
	{
		s = sprList.stack[sprList.stackPtr--];

		s->prev	= &sprList.head;
		s->next	= sprList.head.next;
		sprList.head.next->prev	= s;
		sprList.head.next = s;
		s->draw = 1;

		sprList.count++;
	}

	return sprList.stack[sprList.stackPtr+1];
}


/*	--------------------------------------------------------------------------------
	Function		: DeallocateSprites
	Purpose			: Remove sprites from list and flag as unused
	Parameters		: number of sprites
	Returns			: 
	Info			: 
*/
void DeallocateSprites( SPRITE *head, int number )
{
	SPRITE *s=head, *t;

	if( !s || !s->next || (number<=0) || (sprList.stackPtr+number >= MAX_SPRITES) ) return;

	while( number-- )
	{
		t = s->next;

		s->prev->next	= s->next;
		s->next->prev	= s->prev;
		s->next = NULL;
		s->draw = 0;

		sprList.count--;
		sprList.stack[++sprList.stackPtr] = s;

		s = t;
	}
}


/*	--------------------------------------------------------------------------------
	Function		: FreeSpriteList
	Purpose			: Deallocate all sprites and set list to empty
	Parameters		: 
	Returns			: 
	Info			: 
*/
void FreeSpriteList( )
{
	int i;

	if( sprList.array )
	{
		// Remove all sprites in array from sprite list so they don't get removed after deallocation
		for( i=0; i<MAX_SPRITES; i++ )
		{
			if( !sprList.array[i].next ) continue;

			sprList.array[i].prev->next	= sprList.array[i].next;
			sprList.array[i].next->prev	= sprList.array[i].prev;
			sprList.array[i].next = NULL;
			sprList.array[i].draw = 0;
		}

		FREE( sprList.array );
		sprList.array = NULL;
	}

	if( sprList.stack )
	{
		FREE( sprList.stack );
		sprList.stack = NULL;
	}

	sprList.count = 0;
	sprList.stackPtr = 0;
}


/*	--------------------------------------------------------------------------------
	Function		: InitSpriteList
	Purpose			: Set up static array and some bits of data.
	Parameters		: 
	Returns			: 
	Info			: 
*/
void InitSpriteList( )
{
	int i;

	sprList.head.next = sprList.head.prev = &sprList.head;

	// Allocate a big bunch of sprites
	sprList.array = (SPRITE *)MALLOC0( sizeof(SPRITE)*MAX_SPRITES );
	sprList.stack = (SPRITE **)MALLOC0( sizeof(SPRITE*)*MAX_SPRITES );

	// Initially, all sprites are available
	for( i=0; i<MAX_SPRITES; i++ )
		sprList.stack[i] = &sprList.array[i];

	sprList.count = 0;
	sprList.stackPtr = i-1;
}






#ifdef PSX_VERSION

//mm int Print3DSprite ( TextureType *spr, VERT *vect, short scale, unsigned long colour, short fade )
int Print3DSprite ( SPRITE *spr ,unsigned long colour )
{
	static POLY_FT4 *pp;

	LONG		width;
	LONG		height;
	LONG 		distancescale;
	LONG 		spritez;
	long		x0,y0,z0;
	long		x1,y1,z1;
	long		x2,y2,z2;
	long		x3,y3,z3;
	VERT 		tempVect;
	uchar		r,g,b;
	KMUINT32	rgba;

	tempVect.vx = -spr->pos.vx;
	tempVect.vy = -spr->pos.vy;
	tempVect.vz = spr->pos.vz;

// 	pp = (POLY_FT4 *)(currentDisplayPage->primPtr);
// 	setPolyFT4(pp);
// 	setRGB0(pp, (colour&255),((colour>>8)&255),((colour>>16)&255));

	width = (spr->texture->w);
	gte_SetLDDQB(0);			// clear offset control reg (C2_DQB)
	gte_ldv0(&tempVect)	;		//vert);
	gte_SetLDDQA(width);		// shove sprite width into control reg (C2_DQA)
	gte_rtps();					// do the rtps
	x0 = screenxy[2].vx;
	y0 = screenxy[2].vy;
	gte_stszotz(&spritez);		//depth);		// get order table z

	if(spritez < 20)
		return 0;

	gte_stopz(&distancescale);		// get scaled width of sprite

	width = (distancescale * (spr->scaleX * 25 )) >> 24;
	width /= 2;

	if(width < 2)
		return 0;	// better max-distance check

 	x1 = x3 = x0 + width;
 	x0 = x2 = x0 - width;
   	if(x1 < 0)
   		return 0;
   	if(x0 > 640)
   		return 0;

	height = (distancescale * (spr->scaleY * 25)) >> 24;
	height /= 2;
//	height /= 2;

	y2 = y3 = y0 + height;
	y1 = y0 = y0 - height;
   	if(y2 < 0)
   		return 0;
   	if(y0 > 480)
   		return 0;

//	rgba = RGBA(0,0,0,255);
	r = spr->r*2;
	if(r > 254)
		r = 254;
	g = spr->g*2;
	if(g > 254)
		g = 254;
	b = spr->b*2;
	if(b > 254)
		b = 254;
	rgba = RGBA(r,g,b,spr->a);

/*	if(spr->flags & SPRITE_TRANSLUCENT )//Only do additive if the 'sprite' fades or is translucent
	{
		// SL: modge the RGBs accordingly
	  	r = ((spr->a*(short)spr->r)/255);
	  	g = ((spr->a*(short)spr->g)/255);
	  	b = ((spr->a*(short)spr->b)/255);

//		// Make additive
//		pp->code |= 2;
//		pp->tpage |= 32;
	}
	
	rgba = RGBA(r,g,b,spr->a);
*/
	spritez = spritez << 2;

/*	if((gameState.mode != PAUSE_MODE)&&(spr->numFrames > 1))
	{
		spr->animTime += (gameSpeed>>12);
		if(spr->animTime > 1)
		{
			spr->animTime = 0;
	
			spr->currFrame++;
			if(spr->currFrame >= spr->numFrames)
				spr->currFrame = 0;
			
		}
	}
*/	
	vertices_GT4[0].fX = (float)x0;
	vertices_GT4[0].fY = (float)y0;
	vertices_GT4[0].u.fZ = 1.0 / ((float)spritez);
	vertices_GT4[0].fU = (float)1;
	vertices_GT4[0].fV = (float)0;
	vertices_GT4[0].uBaseRGB.dwPacked = rgba;

	vertices_GT4[1].fX = (float)x1;
	vertices_GT4[1].fY = (float)y1;
	vertices_GT4[1].u.fZ = 1.0 / ((float)spritez);
	vertices_GT4[1].fU = (float)0;
	vertices_GT4[1].fV = (float)0;
	vertices_GT4[1].uBaseRGB.dwPacked = rgba;

	vertices_GT4[2].fX = (float)x2;
	vertices_GT4[2].fY = (float)y2;
	vertices_GT4[2].u.fZ = 1.0 / ((float)spritez);
	vertices_GT4[2].fU = (float)1;
	vertices_GT4[2].fV = (float)1;
	vertices_GT4[2].uBaseRGB.dwPacked = rgba;

	vertices_GT4[3].fX = (float)x3;
	vertices_GT4[3].fY = (float)y3;
	vertices_GT4[3].u.fZ = 1.0 / ((float)spritez);
	vertices_GT4[3].fU = (float)0;
	vertices_GT4[3].fV = (float)1;
	vertices_GT4[3].uBaseRGB.dwPacked = rgba;

	if(spr->texture->colourKey)
	{
		kmChangeStripTextureSurface(&StripHead_Sprites,KM_IMAGE_PARAM1,spr->texture->surfacePtr);
		kmStartStrip(&vertexBufferDesc, &StripHead_Sprites);	
	}
	else
	{
		kmChangeStripTextureSurface(&StripHead_Sprites_Add,KM_IMAGE_PARAM1,spr->texture->surfacePtr);
		kmStartStrip(&vertexBufferDesc, &StripHead_Sprites_Add);	
	}		

	kmSetVertex(&vertexBufferDesc, &vertices_GT4[0], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[1], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[2], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));	
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[3], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));	
	kmEndStrip(&vertexBufferDesc);

	return 1;
}


int Print3D3DSprite ( SPECFX *fx, SVECTOR *vect, unsigned long colour ) 
{
	LONG 		spritez;
	SVECTOR 	tempVect[4];
	short		x0,y0,x1,y1,x2,y2,x3,y3;
	long		z0,z1,z2,z3;
	KMUINT32	rgba;
	TextureType *txPtr;

	tempVect[0].vx = -vect[0].vx;
	tempVect[0].vy = -vect[0].vy;
	tempVect[0].vz = vect[0].vz;

	tempVect[1].vx = -vect[1].vx;
	tempVect[1].vy = -vect[1].vy;
	tempVect[1].vz = vect[1].vz;

	tempVect[3].vx = -vect[2].vx;
	tempVect[3].vy = -vect[2].vy;
	tempVect[3].vz = vect[2].vz;

	tempVect[2].vx = -vect[3].vx;
	tempVect[2].vy = -vect[3].vy;
	tempVect[2].vz = vect[3].vz;

// 	pp = (POLY_FT4 *)(currentDisplayPage->primPtr);
//	setPolyFT4(pp);
// 	setRGB0(pp, (colour&255),((colour>>8)&255),((colour>>16)&255));
	
	gte_SetLDDQB(0);			// clear offset control reg (C2_DQB)

 	gte_ldv0(&tempVect[0]);		//vert);
 	gte_rtps();					// do the rtps
	x0 = screenxy[2].vx;
	y0 = screenxy[2].vy;
 	z0 = (((screenxy[2].vz/16) + fx->zDepthOff) >> 2);
 
 	gte_ldv0(&tempVect[1]);		//vert);
 	gte_rtps();					// do the rtps
	x1 = screenxy[2].vx;
	y1 = screenxy[2].vy;
 	z1 = (((screenxy[2].vz/16) + fx->zDepthOff) >> 2);
 
 	gte_ldv0(&tempVect[2]);		//vert);
 	gte_rtps();					// do the rtps
	x2 = screenxy[2].vx;
	y2 = screenxy[2].vy;
 	z2 = (((screenxy[2].vz/16) + fx->zDepthOff) >> 2);

	gte_ldv0(&tempVect[3]);		//vert);  								
	gte_rtps();					// do the rtps							
	x3 = screenxy[2].vx;
	y3 = screenxy[2].vy;
  	z3 = (((screenxy[2].vz/16) + fx->zDepthOff) >> 2);

	gte_stszotz(&spritez);		// get order table z

	spritez += fx->zDepthOff;		//mm allow for z depth to be altered

	spritez = spritez >> 2;

	// check to see the poly is on screen
	if((x0 >= 640)&&(x1 >= 640)&&(x2 >= 640)&&(x3 >= 640))
		return 0;
	if((x0 < 0)&&(x1 < 0)&&(x2 < 0)&&(x3 < 0))
		return 0;			
	if((y0 >= 480)&&(y1 >= 480)&&(y2 >= 480)&&(y3 >= 480))
		return 0;
	if((y0 < 0)&&(y1 < 0)&&(y2 < 0)&&(y3 < 0))
		return 0;
	
	if(spritez < 20)
		return 0;
		
   	if(x1 < 0)
   		return 0;
   	if(x0 > 640)
   		return 0;
 
   	if(y2 < 0)
   		return 0;
   	if(y0 > 480)
   		return 0;
   		
/*	if(fx->flags & SPRITE_TRANSLUCENT )//Only do additive if the 'sprite' fades or is translucent
	{
		// SL: modge the RGBs accordingly
	  	r = ((fx->a*(short)r)/255);
	  	g = ((fx->a*(short)g)/255);
	  	b = ((fx->a*(short)b)/255);

		// Make additive
//		pp->code |= 2;
//		pp->tpage |= 32;
	}
*/
/*	if(fx->flags & SPRITE_SUBTRACTIVE )//Subtractive for shadows
	{
	// SL: modge the RGBs accordingly
	  	pp->r0 = ((fx->a*(short)pp->r0)/255);
	  	pp->g0 = ((fx->a*(short)pp->g0)/255);
	  	pp->b0 = ((fx->a*(short)pp->b0)/255);

	// Make subtractive
		pp->code |= 2;
		pp->tpage |= 64;
	}
*/	

	rgba = RGBA(fx->r,fx->g,fx->b,fx->a);

	vertices_GT4[0].fX = (float)x0;
	vertices_GT4[0].fY = (float)y0;
	vertices_GT4[0].u.fZ = 1.0 / ((float)z0);
	vertices_GT4[0].fU = (float)1;
	vertices_GT4[0].fV = (float)0;
	vertices_GT4[0].uBaseRGB.dwPacked = rgba;

	vertices_GT4[1].fX = (float)x1;
	vertices_GT4[1].fY = (float)y1;
	vertices_GT4[1].u.fZ = 1.0 / ((float)z1);
	vertices_GT4[1].fU = (float)0;
	vertices_GT4[1].fV = (float)0;
	vertices_GT4[1].uBaseRGB.dwPacked = rgba;

	vertices_GT4[2].fX = (float)x2;
	vertices_GT4[2].fY = (float)y2;
	vertices_GT4[2].u.fZ = 1.0 / ((float)z2);
	vertices_GT4[2].fU = (float)1;
	vertices_GT4[2].fV = (float)1;
	vertices_GT4[2].uBaseRGB.dwPacked = rgba;

	vertices_GT4[3].fX = (float)x3;
	vertices_GT4[3].fY = (float)y3;
	vertices_GT4[3].u.fZ = 1.0 / ((float)z3);
	vertices_GT4[3].fU = (float)0;
	vertices_GT4[3].fV = (float)1;
	vertices_GT4[3].uBaseRGB.dwPacked = rgba;

	if(fx->tex->colourKey)
	{
		kmChangeStripTextureSurface(&StripHead_Sprites,KM_IMAGE_PARAM1,fx->tex->surfacePtr);
		kmStartStrip(&vertexBufferDesc, &StripHead_Sprites);	
	}
	else
	{
		kmChangeStripTextureSurface(&StripHead_Sprites_Add,KM_IMAGE_PARAM1,fx->tex->surfacePtr);
		kmStartStrip(&vertexBufferDesc, &StripHead_Sprites_Add);	
	}	
//	kmChangeStripTextureSurface(&StripHead_Sprites,KM_IMAGE_PARAM1,fx->tex->surfacePtr);
	
//	kmStartStrip(&vertexBufferDesc, &StripHead_Sprites);	
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[0], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[1], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[2], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));	
	kmSetVertex(&vertexBufferDesc, &vertices_GT4[3], KM_VERTEXTYPE_03, sizeof(KMVERTEX_03));	
	kmEndStrip(&vertexBufferDesc);

	return 1;
}





void PrintSprites ( void )
{
	SPRITE *cur;
	unsigned long colour;
	
	for( cur = sprList.head.next; cur != &sprList.head; cur = cur->next )
	{
		if ( !cur )
			break;

		if( !cur->draw )
			continue;

		//PUTS THE SPRITES RGB'S IN COLOUR, FIRST HALVING THEIR VALUES
		colour = cur->r>>1;
		colour += (cur->g>>1)<<8;
		colour += (cur->b>>1)<<16;

		Print3DSprite( cur, colour );			
 	}
}

#endif