/****************************************************************************
*
* SNOW.C
*
*    VERSION : 00.00
*    CREATED : 2/07/98
*   MODIFIED : 2/07/98
*	   TABS : 5
*
* DESCRIPTION
*
*****************************************************************************/


/*---------------------------------------------------------------------------
 * Import Headers
\*--------------------------------------------------------------------------*/

#ifdef PSX_VERSION
#include "..\spec_psx\typedefs.h"
#include "..\spec_psx\gpu.h"
#include <libgte.h>
#include <libgpu.h>
#include <gtemac.h>
#include <inline_c.h>
#else
#include "../specific/stypes.h"
#endif

#ifdef PSX_VERSION
#include "..\spec_psx\maths.h"
#endif

#include "lara.h"
#include "snow.h"
#include "anim.h"
#include "objects.h"

/*---------------------------------------------------------------------------
 * Constants
\*--------------------------------------------------------------------------*/

#define X 0
#define Y 1
#define Z	2
#define XYZ 3

#define CENTREX 256
#define CENTREY 120

/*---------------------------------------------------------------------------
 * Externals
\*--------------------------------------------------------------------------*/


#ifdef PSX_VERSION
	extern VECTOR3D CamPos, CamTgt;
#endif

extern WORD rcossin_tbl[];
extern	long	SmokeWindX, SmokeWindZ;
extern	short IsRoomOutsideNo;

/*---------------------------------------------------------------------------
 * Globals
\*--------------------------------------------------------------------------*/

extern	char *OutsideRoomTable;
extern	short OutsideRoomOffsets[27*27];	// -1 = no rooms

/*---------------------------------------------------------------------------
 * Locals
\*--------------------------------------------------------------------------*/

static SNOWFLAKE	snowflakes[MAX_SNOWFLAKES];
static long	NextSnowflake=0;

/*---------------------------------------------------------------------------
 * Macros
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Local Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
* DoSnow
*
* SYNOPSIS
*	void DoSnow()
*
* PURPOSE
*	Displays a load of lovely snowflakes
*
* INPUTS
*	None
*
* RETURNS
*	Nothing
*/

#ifdef PSX_VERSION

void DoSnow()
{
//	long	result[XYZ];
//	long	screencoords[XYZ];
	long	x, y, z, radius, lp, angle, u1, u2, v1, v2, size, randval, onwater;
	SNOWFLAKE	*snowptr;
	PSXSPRITESTRUCT *pSpriteInfo;
	POLY_FT3 	*ft3;

	short		*scrxy;	// X,Y
	long			*scrz;	// Z
	short		*TempMesh;

/* -------- determine distance (this is wank - do it properly after E3) */

//	if (OutsideRoomOffsets[((CamPos.x>>12)*27)+(CamPos.z>>12)] != -1)
//		distance = 3072;
//	else
//		distance = 4096;

	scrxy = (short *) 0x1f800000;	// Set pointer to scratch pad.
	scrz = (long *) 0x1f800100;	// Set pointer to scratch pad.
	TempMesh = (short *) 0x1f800200;	// Set pointer to scratch pad.

// Update snowflakes and trigger any if need be (i.e. Xpos = 0).

	snowptr = &snowflakes[0];

	for ( lp=0 ; lp<MAX_SNOWFLAKES ; lp++ )
	{
		if (snowptr->on == 0)	// Trigger a new one.
		{
			y = -1024 - (GetRandomDraw()&2047);

			radius = (GetRandomDraw()&4095);
			angle = GetRandomDraw()&8190;

			x = (((rcossin_tbl[angle])*radius)>>12);
			z = (((rcossin_tbl[angle+1])*radius)>>12);

			snowptr->x = x;
			snowptr->y = y;
			snowptr->z = z;

			if (!IsRoomOutside(snowptr->x+lara_item->pos.x_pos, snowptr->y+lara_item->pos.y_pos, snowptr->z+lara_item->pos.z_pos))
			{
				snowptr->on = 0;
				snowptr++;
				continue;
			}
			snowptr->on = 1;
			snowptr->xv = (GetRandomDraw()&7) - 4;
			snowptr->yv = ((GetRandomDraw()&15) + 8)<<3;
			snowptr->zv = (GetRandomDraw()&7) - 4;
			snowptr->life = 32+(56-(snowptr->yv*2));
		}

		onwater = 0;
		if (IsRoomOutside(snowptr->x+lara_item->pos.x_pos, snowptr->y+lara_item->pos.y_pos, snowptr->z+lara_item->pos.z_pos))
		{
			if (room[IsRoomOutsideNo].flags & UNDERWATER)	// Underwater room.
			{
//				snowptr->y = room[IsRoomOutsideNo].maxceiling+8;
				onwater = 1;
				if (snowptr->life > 16)
					snowptr->life = 16;
				if (snowptr->yv > 8)
					snowptr->yv -= 8;

			}
		}

		if (!snowptr->life ||
			abs(CamPos.x - (snowptr->x+lara_item->pos.x_pos)) > 6000 ||
			abs(CamPos.z - (snowptr->z+lara_item->pos.z_pos)) > 6000 )
		{
			if (!snowptr->life)
			{
				snowptr->on = 0;	// Turn it off.
				snowptr++;
				continue;
			}
			else if (snowptr->life > 16)
				snowptr->life = 15;
		}

		if (!onwater)
		{
			snowptr->x+=snowptr->xv+(SmokeWindX<<1);
			snowptr->y+=(snowptr->yv>>2);
			snowptr->z+=snowptr->zv+(SmokeWindZ<<1);
		}

		randval = GetRandomControl();
		if ((randval&3) != 3)
		{
			snowptr->xv += (randval&3)-1;
			if (snowptr->xv < -4)
				snowptr->xv = -4;
			else if (snowptr->xv > 4)
				snowptr->xv = 4;
		}

		randval = (randval>>2)&3;
		if (randval != 3)
		{
			snowptr->zv += randval-1;
			if (snowptr->zv < -4)
				snowptr->zv = -4;
			else if (snowptr->zv > 4)
				snowptr->zv = 4;
		}

		snowptr->life--;
		if ((snowptr->yv&7) < 7)
			snowptr->yv++;
		snowptr++;
	}

	mPushMatrix();

	SetRotMatrix((MATRIX*) Matrix);
	SetTransMatrix((MATRIX*) Matrix);
	mTranslateXYZ(lara_item->pos.x_pos,lara_item->pos.y_pos,lara_item->pos.z_pos);

	pSpriteInfo=psxspriteinfo+objects[EXPLOSION1].mesh_index+17;
	u1 = pSpriteInfo->u1;
	u2 = pSpriteInfo->u2;
	v1 = pSpriteInfo->v1;
	v2 = pSpriteInfo->v2;

	ft3=(POLY_FT3*)db.polyptr;

	snowptr = &snowflakes[0];

	for ( lp=0 ; lp<MAX_SNOWFLAKES ; lp++ )
	{
		if (snowptr->on == 0)	// Not triggered?
		{
			snowptr++;
			continue;
		}

		TempMesh[X] = snowptr->x;
		TempMesh[Y] = snowptr->y;
		TempMesh[Z] = snowptr->z;

	  	gte_ldv0(TempMesh);
		gte_rtps();
		gte_stsxy(scrxy);
		gte_stsz(scrz);

		if ((*scrz) > 16384 || (*scrz) < 32 ||
			scrxy[X] < -32 ||
			scrxy[X] > 512+32 ||
			scrxy[Y] < -64 ||
			scrxy[Y] > 256+64)
		{
			if ((*scrz) < -1024)
			{
				if (snowptr->life > 16)
			 		snowptr->life = 15;
			}
			snowptr++;
			continue;
		}

		size = (((snowptr->yv>>3))*phd_persp) / ((*scrz)>>3);
		if (size < 4)
			size = 4;
		setlen(ft3, 7);
		setcode(ft3, 0x26);	// Transparent ft3.
		setXY3(ft3,scrxy[X],scrxy[Y]-size,scrxy[X],scrxy[Y],scrxy[X]-size,scrxy[Y]);
		setUV3(ft3,u2,v1+1,u2,v2,u1+1,v2);

		if ((snowptr->yv&7) < 7)	// Fade in.
		{
			long	fadeval = (snowptr->yv&7)<<3;
			setRGB0(ft3,fadeval,fadeval,fadeval);
		}
		else if (snowptr->life > 18)	// Normal.
			setRGB0(ft3,72,72,72);
		else
			setRGB0(ft3,snowptr->life<<2,snowptr->life<<2,snowptr->life<<2);	// Fade out.
		ft3->clut = pSpriteInfo->clut;
		ft3->tpage = (pSpriteInfo->tpage & (~96)) | (1<<5);	// Set Colour addition.
		MyaddPrim( db.ot,(*scrz)>>3, ft3 );
		ft3++;

		snowptr++;
	}

	db.polyptr = (uchar *) ft3;

	mPopMatrix();
}

#endif


/****************************************************************************
* BuildOutsideTable
*
* SYNOPSIS
*	void BuildOutsideTable()
*
* PURPOSE
*	Creates a table of outside and underwater rooms
*
* INPUTS
*	None
*
* RETURNS
*	Nothing
*/

#define OUTSIDE_Z 32	// z depth of outside table

void BuildOutsideTable()
{
	unsigned char *s;
	int x, y, i;

	OutsideRoomTable = (unsigned char *)game_malloc(27*27*OUTSIDE_Z, 0);
	S_MemSet(OutsideRoomTable, 0xff, 27*27*OUTSIDE_Z);

/* -------- determine outside rooms within a grid pos */

	for ( y=0 ; y<108 ; y+=4 )
	{
		for ( x=0 ; x<108 ; x+=4 )
		{
			for ( i=0 ; i<number_rooms ; i++ )
			{
				unsigned char *d;
				int rx, ry, j;
				ROOM_INFO *r = &room[i];

				rx = r->z >> 10;
				ry = r->x >> 10;

				if (((!(r->flags & NOT_INSIDE)) && (!(r->flags & UNDERWATER)))
				|| ((rx + r->x_size) < x)
				||  (rx > x)
				|| ((ry + r->y_size) < y)
				||  (ry > y))
					continue;

#if defined(GAMEDEBUG) || defined(EZORAY)
				if (i == 255)
				{
					printf("ERROR : Room 255 fuckeroony - go tell Chris\n");
					exit(0);
				}
#endif
				d = &OutsideRoomTable[((x>>2)*OUTSIDE_Z) + ((y>>2)*(27*OUTSIDE_Z))];

				for ( j=0 ; j<OUTSIDE_Z ; j++ )
				{
					if (d[j] == 0xff)
					{
						d[j] = i;
						break;
					}
				}

#if defined(GAMEDEBUG) || defined(EZORAY)
				if (j == OUTSIDE_Z)
				{
					printf("ERROR : Buffer shittage - go tell Chris\n");
					exit(0);
				}
#endif
			}
		}
	}

/* -------- create outside room table */

	s = OutsideRoomTable;

	for ( y=0 ; y<27 ; y++ )
	{
		for ( x=0 ; x<27 ; x++ )
		{
			int z=0;
			unsigned char *d, *p;

			i = (y*27)+x;
			d = &OutsideRoomTable[(x*OUTSIDE_Z) + (y*(27*OUTSIDE_Z))];

			while (d[z] != 0xff)
				z++;

			if (z == 0)
				OutsideRoomOffsets[i] = 0xffff;
			else if (z == 1)
				OutsideRoomOffsets[i] = *d | 0x8000;
			else
			{

			/* -------- pack string */

				p = OutsideRoomTable;

				while (p < s)
				{
					if (memcmp(p, d, z) == 0)
					{
						OutsideRoomOffsets[i] = (long)p-(long)OutsideRoomTable;
						break;
					}
					else
					{
						int z2=0;

						while (p[z2] != 0xff)
							z2++;

						p+=z2+1;
					}
				}

			/* -------- add a new string */

				if (p >= s)
				{
					OutsideRoomOffsets[i] = (long)s-(long)OutsideRoomTable;

					for ( ; z ; z-- )
						*s++ = *d++;

					*s++ = 0xff;
				}
			}
		}
	}

	game_free((((27*27*OUTSIDE_Z)-((long)s-(long)OutsideRoomTable))+3)&~3, 0);

//#if defined(GAMEDEBUG) || defined(EZORAY)
	printf("Ouside room table = %d bytes\n", (long)s-(long)OutsideRoomTable);
//#endif
}
