// Stadium.c

#include "ult_64.h"
#include "main.h"
#include "file.h"
#include "define.h"
#include "ml.h"

#include "misc.h"
#include "view.h"
#include "crowd.h"
#include "scorebrd.h"
#include "stadium.h"


#define TOP_ROW 2
#define MID_ROW 1
#define BOT_ROW 0


#define FRAME_RELAX1			0
#define FRAME_RELAX2			1
#define FRAME_CLAP1				2
#define FRAME_CLAP2				3
#define FRAME_CLAP3				4
#define FRAME_FREETHROW1	5
#define FRAME_FREETHROW2	6
#define FRAME_FREETHROW3	7
#define FRAME_COUNT				8

#define MTL_RAIL			0
#define MTL_STAIRS		1
#define MTL_CEMENT		2
#define MTL_UPPER			3
#define MTL_LUXURY		4
#define MTL_FLAT1			5
#define MTL_FLAT2			6
#define MTL_FRONT1		7
#define MTL_FRONT2		8
#define MTL_FRONT3		9
#define MTL_FRONT4		10
#define MTL_PHOTOS1		11
#define MTL_PHOTOS2		12
#define MTL_BENCHAWAY	13
#define MTL_BENCHHOME	14
#define MTL_TABLE			15
#define MTL_ROOFLAMP	16
#define MTL_COUNT			17


typedef struct SOverlay
{
	s16
		x,
		y,
		w,
		h;
	u8
		*Alpha;
	RGBA16
		*Color;
} SOverlay;

typedef struct SUpCrowd
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version;

	SOverlay
		Seats,
		Wall,
		People[4];
} SUpCrowd;

typedef struct SLuxCrowd
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version;

	SOverlay
		Seats,
		Shade,
		Wall,
		People[4];
} SLuxCrowd;

typedef struct SFlatCrowd
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version;

	RGBA16
		Palette[256],
		SeatPal[16];
	SOverlay
		SeatSide,
		SeatSideUp,
		SeatMid,
		SeatMidUp,
		Shade,
		People[3][10][8];
} SFlatCrowd;

typedef struct SFrontCrowd
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version;

	RGBA16
		Palette[256],
		BenchPals[2][256];
	SOverlay
		Chair,
		People[15][FRAME_COUNT],
		Photos[6][FRAME_COUNT],
		BenchTexs[2][5];
} SFrontCrowd;

typedef struct SFlatSeat
{
	SOverlay
		*Up,
		*Down;
} SFlatSeat;

typedef struct SSeat
{
	s8
		Row,
		Column;
} SSeat;


static u8
	*AdsObj,
	*AdsMtls,
	*EntryObj,
	*MainObj,
	*RoofObj,
	*RailsObj,
	*Table;

static SMaterial
	*Materials;

static Gfx
	*AdsList,
	*EntryList,
	*RoofList;

static RGBA16
	LightSeatPal[16],
	DarkSeatPal[16];

static u8
	*BenchTexs[2][5],
	*FlatTexs[2][FRAME_COUNT],
	*FrontCrowd[6][FRAME_COUNT];

static s32
	Wrap = FALSE,
	AntiAlias = FALSE;

static u8
	Used[3][10];

static SSeat
	Seating[2][3][5];

static s8
	FrontSeating[6][5],
	PhotoSeating[2][4];

static s8
	Xpos[5] = { 0, 12, 25, 38, 51 },
	FrontXs[5] = { 1, 14, 27, 40, 52 },
	PhotoXs[4] = { 0, 16, 32, 47 };

static char
	*Allow[2][3] =
	{
		"AAA.AAAAAA",			// Left
		"A.A..A...A",
		"AA...A....",

		"A..AAAAA.A",			// Right
		"A...A....A",
		"AAA.A....A",
	};

static SFlatSeat
	FlatSeats[5];

static RGBA32
	*Work;

static vector
	RoofMin,
	RoofMax;


/********************************************************************/

typedef struct SLight
{
	vector16
		Pos;
	Mtx
		Matrix[2];
} SLight;

typedef struct SLightFile
{
	u8
		Texture[48*42];


	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version,
		NumLights,
		pad;
	SLight
		Lights[1];
} SLightFile;


static SLightFile
	*LightData;


static void Lights_Init()
{
	LightData = AllocAndLoadFile( "Lights.DAT", MEM_ANY );
}


static void Lights_Deinit()
{
	MemFree( LightData );
}


static void ScaleMatXY( matrix *m, s32 scale )
{
	m->xX = (m->xX * scale) >> 8;
	m->xY = (m->xY * scale) >> 8;
	m->xZ = (m->xZ * scale) >> 8;

	m->yX = (m->yX * scale) >> 8;
	m->yY = (m->yY * scale) >> 8;
	m->yZ = (m->yZ * scale) >> 8;
}


static Gfx *Lights_Draw( Gfx *gp, s32 db )
{
	static Vtx
		verts[4] =
		{
			-(24*64), +(21*64), 0, 0,         0,         0, 255, 255, 255, 128,
			+(24*64), +(21*64), 0, 0, (48*64)-1,         0, 255, 255, 255, 128,
			+(24*64), -(21*64), 0, 0, (48*64)-1, (42*64)-1, 255, 255, 255, 128,
			-(24*64), -(21*64), 0, 0,         0, (42*64)-1, 255, 255, 255, 128,
		};
	SLight
		*light;
	matrix
		work;
	s32
		i,
		d,
		x,
		y,
		z,
		count;

	gDPPipeSync( gp++ );
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPSetCombineMode( gp++, G_CC_MODULATEIA, G_CC_MODULATEIA );
	gDPSetRenderMode( gp++, G_RM_XLU_SURF, G_RM_XLU_SURF2 );
	gDPSetTextureLUT( gp++, G_TT_NONE );

	gDPLoadTextureBlock( gp++,
											 LightData->Texture,
											 G_IM_FMT_IA,
											 G_IM_SIZ_8b,
											 48, 42,
											 0,
											 G_TX_CLAMP,
											 G_TX_CLAMP,
											 6, 6,
											 G_TX_NOLOD,
											 G_TX_NOLOD );

	count = LightData->NumLights;
	light = &LightData->Lights[0];

	for (i=0; i<count; i++)
	{
		if (View_Inside( &light->Pos, 2040 ))
		{
			x = light->Pos.x - View->Camera.x;
			y = light->Pos.y - View->Camera.y;
			z = light->Pos.z - View->Camera.z;

			d = (x*x) + (y*y) + (z*z);
			d = (s32)sqrtf( d );
			d = (1 << (14+14)) / d;

			x = (x * d) >> 14;
			y = (y * d) >> 14;
			z = (z * d) >> 14;

			d  = (x * View->Front.x) >> 14;
			d += (y * View->Front.y) >> 14;
			d += (z * View->Front.z) >> 14;

			d >>= (14-8);

			if (d > 16)
			{
				work = View->Cam2World;
				ScaleMatXY( &work, d );
				MatToN64( &work, &light->Matrix[db] );
				SetTransN64( &light->Matrix[db],
										 light->Pos.x,
										 light->Pos.y,
										 light->Pos.z );

				gSPMatrix( gp++, &light->Matrix[db], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
				gSPVertex( gp++, verts, 4, 0 );
				gSP2Triangles( gp++, 0, 2, 1, 0, 0, 3, 2, 0 );
				gDPPipeSync( gp++ );
			}
		}

		light++;
	}

	gDPSetTextureLUT( gp++, G_TT_RGBA16 );

	return( gp );
}


/********************************************************************/

static u32 GetTeamNum( u32 team )
{
	team = pcl__gMatch->asb__mTeam[team];

	if (team > TEAM_SSN)
	{
		if (team >= TEAM_LFP && team <= TEAM_NOA)
			team -= 2;
		else
			team = 0;			// All-stars.
	}

	return( team );
}


static void FindColors( u8 *tex, RGBA16 *pal )
{
	u32
		i,
		r,
		g,
		b,
		col1,
		col2,
		diff;

	for (i=0; i<(64*32); i++)
	{
		r = Work[i].r >> 3;
		g = Work[i].g >> 3;
		b = Work[i].b >> 3;

		col1 = Pal_FindColor( r, g, b, pal, 16 );
		diff = ColorDiff;

		col2 = Table[(r<<10)+(g<<5)+b];
		Pal_FindColor( r, g, b, &pal[col2], 1 );

		if (diff < ColorDiff)
			tex[i] = col1;
		else
			tex[i] = col2;
	}
}


static void SetWrap( const s32 wrap )
{
	Wrap = wrap;
}


static void PutBitmap( SOverlay *overlay, const RGBA16 *pal, const s32 xpos, const s32 ypos )
{
	s32
		i,
		j,
		x,
		y,
		off = 0;

	if (!overlay || !pal)
		return;

	for (j=0; j<overlay->h; j++)
	{
		for (i=0; i<overlay->w; i++)
		{
			x = xpos + i;
			y = ypos + j;

			if (Wrap)
				y &= 31;

			if (x >= 0 && x < 64 &&
					y >= 0 && y < 32)
			{
				Work[(y<<6)+x].r = pal[overlay->Alpha[off]].r << 3;
				Work[(y<<6)+x].g = pal[overlay->Alpha[off]].g << 3;
				Work[(y<<6)+x].b = pal[overlay->Alpha[off]].b << 3;
			}

			off++;
		}
	}
}


static void Flat_PutSeat( SOverlay *overlay, const s32 xpos, const s32 ypos )
{
	RGBA16
		*pal;
	s32
		i,
		j,
		x,
		y,
		off = 0;

	if (!overlay)
		return;

	for (j=0; j<overlay->h; j++)
	{
		for (i=0; i<overlay->w; i++)
		{
			x = xpos + i;
			y = ypos + j + 2;

			if (Wrap)
				y &= 31;

			if (x >= 0 && x < 64 &&
					y >= 0 && y < 32)
			{
				Work[(y<<6)+x].r = LightSeatPal[overlay->Alpha[off]].r << 3;
				Work[(y<<6)+x].g = LightSeatPal[overlay->Alpha[off]].g << 3;
				Work[(y<<6)+x].b = LightSeatPal[overlay->Alpha[off]].b << 3;
			}

			off++;
		}
	}
}


static void Overlay( SOverlay *overlay, const s32 xpos, const s32 ypos )
{
	RGBA32
		*rgb;
	RGBA16
		*color;
	u8
		*alpha;
	u32
		r,
		g,
		b,
		a;
	s32
		i,
		j,
		x,
		y,
		w,
		off = 0;

	if (!overlay)
		return;

	if (!overlay->Color)			// Shade only!
	{
		for (j=0; j<overlay->h; j++)
		{
			for (i=0; i<overlay->w; i++)
			{
				x = (s32)overlay->x + xpos + i;
				y = (s32)overlay->y + ypos + j;// - 8;

				if (Wrap)
					y &= 31;

				if (x >= 0 && x < 64 &&
						y >= 0 && y < 32)
				{
					rgb = &Work[(y<<6)+x];
					a = (255 - overlay->Alpha[off]);

					r = rgb->r;
					g = rgb->g;
					b = rgb->b;

					r *= a;
					g *= a;
					b *= a;

					rgb->r = (r >> 8);
					rgb->g = (g >> 8);
					rgb->b = (b >> 8);
				}

				off++;
			}
		}
	}
	else
	{
		y = overlay->y + ypos;

		for (j=0; j<overlay->h; j++)
		{
			x = overlay->x + xpos;

			if (Wrap)
				y &= 31;

			if (y >= 32)
				return;

			if (y >= 0)
			{
				alpha = &overlay->Alpha[off];
				color = &overlay->Color[off];

				w = overlay->w;

				if (x < 0)
				{
					alpha -= x;
					color -= x;
					w += x;
					x = 0;
				}

				if ((x+w) > 64)
					w -= (x+w) - 64;

				rgb = &Work[(y<<6)+x];

				for (i=0; i<w; i++)
				{
					a = *alpha++;

					r = color->r;
					g = color->g;
					b = color->b;
					color++;

					if (a)
					{
						r <<= (3+8);
						g <<= (3+8);
						b <<= (3+8);

						r += (u32)rgb->r * a;
						g += (u32)rgb->g * a;
						b += (u32)rgb->b * a;

						rgb->r = (r >> 8);
						rgb->g = (g >> 8);
						rgb->b = (b >> 8);
					}
					else
					{
						rgb->r = (r << 3);
						rgb->g = (g << 3);
						rgb->b = (b << 3);
					}

					rgb++;
				}
			}

			off += overlay->w;
			y++;
		}
	}
}


static void PutFront( SOverlay *overlay, s32 xpos, s32 ypos, u8 *tex )
{
	u8
		*data;
	s32
		x,
		y;

	data = overlay->Alpha;

	for (y=0; y<overlay->h; y++)
	{
		for (x=0; x<overlay->w; x++)
		{
			if (*data)
				tex[((overlay->y + ypos + y) * 64) + (overlay->x + xpos + x)] = *data;

			data++;
		}
	}
}


static s32 SeatUsed( SSeat *seat )
{
#if 0

	s32
		x,
		y;

	for (y=0; y<3; y++)
	{
		for (x=0; x<5; x++)
		{
			if (Seating[y][x].Row == seat->Row &&
					Seating[y][x].Column == seat->Column)
				return( TRUE );
		}
	}

	return( FALSE );

#else

	return( Used[seat->Row][seat->Column] );

#endif
}


static s32 CheckSeat( SSeat *seat, const s32 row, const s32 col )
{
	// Already used?

	if (SeatUsed( seat ))
		return( FALSE );


	// Only allow row 3 people on row 3...

	if (row == 2 && seat->Row != 2)
		return( FALSE );


	// Check left side restrictions...

	if (col == 0)
	{
		if (Allow[0][2 - seat->Row][seat->Column] != 'A')
			return( FALSE );
	}


	// Check right side restrictions...

	if (col == 4)
	{
		if (Allow[1][2 - seat->Row][seat->Column] != 'A')
			return( FALSE );
	}


	return( TRUE );
}


static void BumpColumn( SSeat *seat )
{
	seat->Column++;
	if (seat->Column >= 10)
		seat->Column = 0;
}


static void BumpRow( SSeat *seat )
{
	seat->Row++;
	if (seat->Row >= 3)
	{
		seat->Row = 0;

		seat->Column++;
		if (seat->Column >= 10)
			seat->Column = 0;
	}
}


static void SetupSeating( const s32 percent )			// Assumes RAND_MAX is 0x7fff!
{
	SSeat
		seat;
	struct
	{
		s32
			total,
			rows[3];
	} counts[2];
	s32
		i,
		t,
		x,
		y,
		cnt,
		temp;

	for (y=0; y<3; y++)
	{
		for (x=0; x<10; x++)
			Used[y][x] = FALSE;
	}

	for (t=0; t<2; t++)
	{
		for (y=0; y<3; y++)
		{
			for (x=0; x<5; x++)
			{
				Seating[t][y][x].Row = -1;			// Empty.
				Seating[t][y][x].Column = -1;
			}
		}
	}


	for (t=0; t<2; t++)
	{
		cnt = (15 * percent) / 100;

		if (percent > 0)
		{
			cnt += (rand() >> 14) & 1;

			if (cnt < 0)
				cnt = 1;
			if (cnt > 15)
				cnt = 15;
		}

		counts[t].total = cnt;
	}

	cnt += (rand() >> 14) & 1;
	cnt /= 3;
	if (cnt > 5)
		cnt = 5;

	counts[0].rows[TOP_ROW] = cnt;
	counts[1].rows[TOP_ROW] = cnt;

	for (t=0; t<2; t++)
	{
		cnt = counts[t].total;
		cnt -= counts[t].rows[TOP_ROW];
		cnt += (rand() >> 14) & 1;
		cnt >>= 1;
		if (cnt > 5)
			cnt = 5;
		counts[t].rows[BOT_ROW] = cnt;

		cnt = counts[t].total;
		cnt -= counts[t].rows[TOP_ROW];
		cnt -= counts[t].rows[BOT_ROW];
		counts[t].rows[MID_ROW] = cnt;
	}


	// Do row 3 first as its more restricted...

	cnt = counts[0].rows[TOP_ROW];
	if (cnt)
	{
		static const u8
			seating[10] = { 4, 4, 10, 17, 21, 14, 27, 27, 31, 31 };

		y = (cnt - 1) << 1;
		y += (rand() >> 14) & 1;

		for (x=0; x<5; x++)
		{
			if (seating[y] & (1 << x))
				Seating[0][TOP_ROW][x].Row = 0;
		}
	}

	for (x=0; x<5; x++)
		Seating[1][TOP_ROW][x].Row = Seating[0][TOP_ROW][x].Row;


	for (t=0; t<2; t++)
	{
		for (y=MID_ROW; y>=BOT_ROW; y--)
		{
			u8
				order[5],
				weights[5];

			for (x=0; x<5; x++)
				weights[x] = 100;

			for (x=0; x<5; x++)
			{
				i = (y + 1);
				while (i < 3)
				{
					if (Seating[t][i][x].Row == 0)
						weights[x] -= 30;

					if (Seating[t][i][(x + 1) % 5].Row == 0)
						weights[x] -= 10;

					if (Seating[t][i][(x + 4) % 5].Row == 0)
						weights[x] -= 10;

					i++;
				}
			}


			for (x=0; x<5; x++)
				order[x] = x;

			for (x=0; x<4; x++)
			{
				for (i=(x+1); i<5; i++)
				{
					if (weights[order[i]] > weights[order[x]])
					{
						temp = order[x];
						order[x] = order[i];
						order[i] = temp;
					}
				}
			}

			cnt = 0;
			for (x=0; x<5; x++)
			{
				if (weights[x] == weights[order[0]])
					cnt++;
			}
			if (cnt < counts[t].rows[y])
				cnt = counts[t].rows[y];

			for (i=0; i<counts[t].rows[y]; i++)
			{
				x = ((s32)rand() * cnt) >> 15;

				while (order[x] == 9)
					x = (x + 1) % cnt;

				Seating[t][y][order[x]].Row = 0;
				order[x] = 9;
			}
		}
	}


	// Do row 3 first as its more restricted...

	seat.Row = TOP_ROW;

	// Do left & right columns first...

	for (t=0; t<2; t++)
	{
		for (x=0; x<5; x+=4)
		{
			if (Seating[t][TOP_ROW][x].Row == 0)
			{
				seat.Column = rand() % 10;

				while (!CheckSeat( &seat, TOP_ROW, x ))
					BumpColumn( &seat );

				Seating[t][TOP_ROW][x] = seat;
				Used[seat.Row][seat.Column] = TRUE;
			}
		}
	}

	// Do mid section...

	for (t=0; t<2; t++)
	{
		for (x=1; x<4; x++)
		{
			if (Seating[t][TOP_ROW][x].Row == 0)
			{
				seat.Column = rand() % 10;

				while (!CheckSeat( &seat, TOP_ROW, x ))
					BumpColumn( &seat );

				Seating[t][TOP_ROW][x] = seat;
				Used[seat.Row][seat.Column] = TRUE;
			}
		}
	}


	// Do next 2 rows...

	// Do left & right columns first...

	for (t=0; t<2; t++)
	{
		for (y=BOT_ROW; y<TOP_ROW; y++)
		{
			for (x=0; x<5; x+=4)
			{
				if (Seating[t][y][x].Row == 0)
				{
					seat.Row = rand() % 3;
					seat.Column = rand() % 10;

					while (!CheckSeat( &seat, y, x ))
						BumpRow( &seat );

					Seating[t][y][x] = seat;
					Used[seat.Row][seat.Column] = TRUE;
				}
			}
		}
	}

	// Do mid sections...

	for (t=0; t<2; t++)
	{
		for (y=BOT_ROW; y<TOP_ROW; y++)
		{
			for (x=1; x<4; x++)
			{
				if (Seating[t][y][x].Row == 0)
				{
					seat.Row = rand() % 3;
					seat.Column = rand() % 10;

					while (!CheckSeat( &seat, y, x ))
						BumpRow( &seat );

					Seating[t][y][x] = seat;
					Used[seat.Row][seat.Column] = TRUE;
				}
			}
		}
	}
}


static void Flat_Build( SFlatCrowd *crowd )
{
	u8
		*tex;
	SSeat
		*seat;
	s32
		i,
		t,
		frame;

	Work = MemMalloc( 64*32*4, MEM_TMP );

	for (frame=0; frame<FRAME_COUNT; frame++)
	{
		for (t=0; t<2; t++)
		{
			tex = FlatTexs[t][frame];

#ifndef DEBUG

			Set64s( Work, 0, 64*32/2 );

			SetWrap( TRUE );
			for (i=0; i<5; i++)
			{
				if (Seating[t][TOP_ROW][i].Row >= 0)
					Flat_PutSeat( FlatSeats[i].Down, Xpos[i], 10+11 );
				else
					Flat_PutSeat( FlatSeats[i].Up, Xpos[i], 10+11 );
			}
			for (i=0; i<5; i++)
			{
				seat = &Seating[1-t][TOP_ROW][i];

				if (seat->Row >= 0)
					Overlay( &crowd->People[seat->Row][seat->Column][frame], Xpos[i], 32-8 );
			}
			Overlay( &crowd->Shade, 0, 10+11-8 );

			SetWrap( FALSE );
			for (i=0; i<5; i++)
			{
				if (Seating[t][TOP_ROW][i].Row >= 0)
					Flat_PutSeat( FlatSeats[i].Down, Xpos[i], 0 );
				else
					Flat_PutSeat( FlatSeats[i].Up, Xpos[i], 0 );
			}
			for (i=0; i<5; i++)
			{
				seat = &Seating[t][TOP_ROW][i];

				if (seat->Row >= 0)
					Overlay( &crowd->People[seat->Row][seat->Column][frame], Xpos[i], 0-8 );
			}
			Overlay( &crowd->Shade, 0, 0-8 );

			for (i=0; i<5; i++)
			{
				if (Seating[t][MID_ROW][i].Row >= 0)
					Flat_PutSeat( FlatSeats[i].Down, Xpos[i], 10 );
				else
					Flat_PutSeat( FlatSeats[i].Up, Xpos[i], 10 );
			}
			for (i=0; i<5; i++)
			{
				seat = &Seating[t][MID_ROW][i];

				if (seat->Row >= 0)
					Overlay( &crowd->People[seat->Row][seat->Column][frame], Xpos[i], 10-8 );
			}
			Overlay( &crowd->Shade, 0, 10-8 );

			for (i=0; i<5; i++)
			{
				if (Seating[t][BOT_ROW][i].Row >= 0)
					Flat_PutSeat( FlatSeats[i].Down, Xpos[i], 10+11 );
				else
					Flat_PutSeat( FlatSeats[i].Up, Xpos[i], 10+11 );
			}
			for (i=0; i<5; i++)
			{
				seat = &Seating[t][BOT_ROW][i];

				if (seat->Row >= 0)
					Overlay( &crowd->People[seat->Row][seat->Column][frame], Xpos[i], 10+11-8 );
			}
			Overlay( &crowd->Shade, 0, 10+11-8 );
			for (i=0; i<5; i++)
			{
				seat = &Seating[1-t][TOP_ROW][i];

				if (seat->Row >= 0)
					Overlay( &crowd->People[seat->Row][seat->Column][frame], Xpos[i], 32-8 );
			}

			FindColors( tex, crowd->Palette );

#else

			memset( tex, 1, 64*32 );

#endif

			tex += (64*32);
		}
	}

	MemFree( Work );
}


static s32 CheckFront( s32 tex, s32 seat, s32 pos )
{
	static const char
		*bad[6] =
		{
			"1r 2l 6l 4l 5l 3l 3r 4r 5r 2r 6r ",
			"2r 4l 1l 3l 6r 3r 4r 5r 1r ",
			"3r 4l 2l 1l 1r 2r 4r ",
			"4r 2l 3l 5l 6l 1l 3r 1r 2r 5r 6r ",
			"5l 4l 6l 1l 4r 1r 6r 2r ",
			"6l 1l 5l 4l 5r 4r 2r 1r ",
		};
	s32
		l,
		m,
		r,
		t,
		i = 0;

	// First, check this texture...

	for (i=0; i<5; i++)
	{
		if (FrontSeating[tex][i] == seat)
			return( FALSE );
	}


	m = pos;

	l = pos - 1;
	if (l < 0)
		l = 4;

	r = pos + 1;
	if (r > 4)
		r = 0;

	i = 0;
	while (bad[tex][i])
	{
		t = bad[tex][i] - '1';

		if (FrontSeating[t][l] == seat) return( FALSE );
		if (FrontSeating[t][m] == seat) return( FALSE );
		if (FrontSeating[t][r] == seat) return( FALSE );

		i += 3;
	}

	return( TRUE );
}


static void SetupFront()
{
	s32
		t,
		pos,
		seat,
		count,
		found,
		odd = 0,
		time_out;

	memset( FrontSeating, -1, 6*5 );

	if (!CrowdAttendance)
		return;


	for (t=0; t<6; t++)
	{
		count = (CrowdAttendance * 6) / 100;
		count += odd * (rand() >> 14);
		if (odd)
			odd = -odd;
		if (count < 0)
			count = 0;
		if (count > 5)
			count = 5;

		while (count)
		{
			pos = (rand() * 5) >> 15;
			while (FrontSeating[t][pos] >= 0)
			{
				pos += 3;
				if (pos >= 5)
					pos -= 5;
			}

			time_out = 15;
			found = FALSE;
			seat = (rand() * 15) >> 15;

			while (time_out && !found)
			{
				if (CheckFront( t, seat, pos ))
					found = TRUE;
				else
				{
					seat += 4;
					if (seat >= 15)
						seat -= 15;

					time_out--;
				}
			}

			if (!found)
				seat = (rand() * 15) >> 15;

			FrontSeating[t][pos] = seat;

			count--;
		}
	}
}


/********************************************************************/

static SFrontCrowd *Front_Load( void *workspace )
{
	FH
		file;
	SFrontCrowd
		*crowd;
	SOverlay
		*overlay;
	s32
		i,
		size,
		team;

	crowd = (SFrontCrowd *)workspace;


	file = FileOpen( "FRONT.CRD", "rb" );
	FileSeek( file, 0, SEEK_END );
	size = FileTell( file );
	FileSeek( file, 0, SEEK_SET );
	FileRead( workspace, size, 1, file );
	FileClose( file );


	overlay = &crowd->Chair;
	for (i=0; i<(1+(15*FRAME_COUNT)+(6*FRAME_COUNT)+(2*5)); i++)
	{
		overlay->Alpha = (u8 *)((u32)overlay->Alpha + (u32)workspace);

		overlay++;
	}


	memcpy( &crowd->Palette[3], LightSeatPal, 16*2 );


	// Set color index 0 to be transparent black...

	*(u16 *)&crowd->Palette[0] = 0;
	*(u16 *)&crowd->BenchPals[0][0] = 0;
	*(u16 *)&crowd->BenchPals[1][0] = 0;


	memcpy( Materials[MTL_FRONT1].Palette, crowd->Palette, 256*2 );

	memcpy( Materials[MTL_BENCHHOME].Palette, crowd->BenchPals[0], 256*2 );
	memcpy( Materials[MTL_BENCHAWAY].Palette, crowd->BenchPals[1], 256*2 );


	file = FileOpen( "Bench.PAL", "rb" );

	team = GetTeamNum( HOME_TEAM );
	FileSeek( file, team * (2*40*2), SEEK_SET );
	FileRead( &Materials[MTL_BENCHHOME].Palette[3], 40*2, 1, file );

	team = GetTeamNum( AWAY_TEAM );
	FileSeek( file, (team * (2*40*2)) + (40*2), SEEK_SET );
	FileRead( &Materials[MTL_BENCHAWAY].Palette[3], 40*2, 1, file );

	FileClose( file );


	return( crowd );
}


/********************************************************************/

static SFlatCrowd *Flat_Load( void *workspace )
{
	FH
		file;
	SFlatCrowd
		*crowd;
	SOverlay
		*overlay;
	s32
		i,
		size;

	crowd = (SFlatCrowd *)workspace;


	file = FileOpen( "FLAT.CRD", "rb" );
	FileSeek( file, 0, SEEK_END );
	size = FileTell( file );
	FileSeek( file, 0, SEEK_SET );
	FileRead( workspace, size, 1, file );
	FileClose( file );


	overlay = &crowd->SeatSide;
	for (i=0; i<5+(3*10*8); i++)
	{
		if (overlay->Alpha)
			overlay->Alpha = (u8 *)((u32)overlay->Alpha + (u32)workspace);

		if (overlay->Color)
			overlay->Color = (RGBA16 *)((u32)overlay->Color + (u32)workspace);

		overlay++;
	}


	FlatSeats[0].Up		= &crowd->SeatSideUp;
	FlatSeats[0].Down	= &crowd->SeatSide;
	for (i=1; i<4; i++)
	{
		FlatSeats[i].Up		= &crowd->SeatMidUp;
		FlatSeats[i].Down	= &crowd->SeatMid;
	}
	FlatSeats[4].Up		= &crowd->SeatSideUp;
	FlatSeats[4].Down	= &crowd->SeatSide;


#if 0
	Shade = &crowd->Shade;

	for (j=0; j<3; j++)
	{
		for (i=0; i<10; i++)
		{
			for (t=0; t<FRAME_COUNT; t++)
			{
				People[j][i][t] = overlay;
				overlay++;
			}
		}
	}
#endif


	memcpy( crowd->Palette, DarkSeatPal, 16*2 );
	Pal_SetAlpha( crowd->Palette, 255, 256 );
	memcpy( Materials[MTL_UPPER].Palette, crowd->Palette, 256*2 );


	return( crowd );
}


/********************************************************************/

static SLuxCrowd *Lux_Load( void *workspace )
{
	SLuxCrowd
		*crowd;
	FH
		file;
	SOverlay
		*overlay;
	s32
		i,
		size;

	crowd = (SLuxCrowd *)workspace;


	file = FileOpen( "Luxury.CRD", "rb" );
	FileSeek( file, 0, SEEK_END );
	size = FileTell( file );
	FileSeek( file, 0, SEEK_SET );
	FileRead( workspace, size, 1, file );
	FileClose( file );


	overlay = &crowd->Seats;
	for (i=0; i<(3+4); i++)
	{
		if (overlay->Alpha)
			overlay->Alpha = (u8 *)((u32)overlay->Alpha + (u32)workspace);

		if (overlay->Color)
			overlay->Color = (RGBA16 *)((u32)overlay->Color + (u32)workspace);

		overlay++;
	}


	return( crowd );
}


static void Lux_Build( SLuxCrowd *crowd )
{
	SMaterial
		*mtl = (SMaterial *)Materials;
	s32
		attend;

	Work = MemMalloc( 64*32*4, MEM_TMP );

	attend = (CrowdAttendance / 25) - 1;

	PutBitmap( &crowd->Seats, DarkSeatPal, 0, 0 );

	if (attend >= 0)
		Overlay( &crowd->People[attend], 0, 0 );

	Overlay( &crowd->Wall, 0, 0 );
	Overlay( &crowd->Shade, 0, 0 );

	FindColors( mtl[MTL_LUXURY].Texture, mtl[MTL_UPPER].Palette );

	MemFree( Work );
}


/********************************************************************/

static SUpCrowd *Up_Load( void *workspace )
{
	SUpCrowd
		*crowd;
	FH
		file;
	SOverlay
		*overlay;
	s32
		i,
		size;

	crowd = (SUpCrowd *)workspace;


	file = FileOpen( "Upper.CRD", "rb" );
	FileSeek( file, 0, SEEK_END );
	size = FileTell( file );
	FileSeek( file, 0, SEEK_SET );
	FileRead( workspace, size, 1, file );
	FileClose( file );


	overlay = &crowd->Seats;
	for (i=0; i<(2+4); i++)
	{
		if (overlay->Alpha)
			overlay->Alpha = (u8 *)((u32)overlay->Alpha + (u32)workspace);

		if (overlay->Color)
			overlay->Color = (RGBA16 *)((u32)overlay->Color + (u32)workspace);

		overlay++;
	}


	return( crowd );
}


static void Up_Build( SUpCrowd *crowd )
{
	SMaterial
		*mtl = (SMaterial *)Materials;
	s32
		attend;

	Work = MemMalloc( 64*32*4, MEM_TMP );

	attend = (CrowdAttendance / 25) - 1;

	PutBitmap( &crowd->Seats, DarkSeatPal, 0, 0 );

	if (attend >= 0)
		Overlay( &crowd->People[attend], 0, 0 );

	Overlay( &crowd->Wall, 0, 0 );

	FindColors( mtl[MTL_UPPER].Texture, mtl[MTL_UPPER].Palette );

	MemFree( Work );
}


/********************************************************************/

static Gfx *DrawStadium( Gfx *gp, u8 *object )
{
	s32
		d,
		wx,
		wy,
		count,
		command;
	u8
		*ptr,
		*tptr;

gDPSetBlendColor( gp++, 0, 0, 0, 166 );
gSPSetGeometryMode(gp++,G_SHADE | G_SHADING_SMOOTH);
gDPSetTextureLUT( gp++, G_TT_RGBA16 );
	gSPSetGeometryMode(gp++,G_CULL_BACK);

	if (AntiAlias)
	{
		gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
		gDPSetAlphaCompare( gp++, G_AC_NONE );
	}
	else
	{
		gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
		gDPSetAlphaCompare( gp++, G_AC_THRESHOLD );
	}

	ptr = &object[*(s32 *)&object[5*4]];
	while (*(s32 *)&ptr[0] > 0)
	{
		command = *(s32 *)&ptr[0];
		count = *(s32 *)&ptr[4];
		ptr += 8;

		switch (command)
		{
			case 1:
				gDPPipeSync( gp++ );

				if (count < 0)
				{
					gSPTexture( gp++, 0x8000, 0x8000, 0, 0, G_OFF );
					gDPSetCombineMode( gp++, G_CC_SHADE, G_CC_SHADE );
				}
				else
				{
					tptr = &object[6*4];
					tptr += count * 16;

					gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
					gDPSetCombineMode( gp++, G_CC_MODULATERGBA, G_CC_MODULATERGBA );

					switch (*(s16 *)&tptr[0])
					{
						case 8:			wx = 3;		break;
						case 16:		wx = 4;		break;
						case 32:		wx = 5;		break;
						case 64:		wx = 6;		break;
						case 128:		wx = 7;		break;
					}

					switch (*(s16 *)&tptr[2])
					{
						case 8:			wy = 3;		break;
						case 16:		wy = 4;		break;
						case 32:		wy = 5;		break;
						case 64:		wy = 6;		break;
						case 128:		wy = 7;		break;
					}

					if (*(s16 *)&tptr[4] == 4)
					{
						gDPLoadTextureBlock_4b( gp++,
																		object + *(s32 *)&tptr[8],
																		G_IM_FMT_CI,
																		*(s16 *)&tptr[0],
																		*(s16 *)&tptr[2],
																		0,
																		G_TX_WRAP,
																		G_TX_WRAP,
																		wx, wy,
																		G_TX_NOLOD, G_TX_NOLOD );
						gDPLoadTLUT_pal16( gp++, 0, object + *(s32 *)&tptr[12] );
					}
					else
					{
						gDPLoadTextureBlock( gp++,
																 object + *(s32 *)&tptr[8],
																 G_IM_FMT_CI,
																 G_IM_SIZ_8b,
																 *(s16 *)&tptr[0],
																 *(s16 *)&tptr[2],
																 0,
																 G_TX_WRAP,
																 G_TX_WRAP,
																 wx, wy,
																 G_TX_NOLOD, G_TX_NOLOD );
						gDPLoadTLUT_pal256( gp++, object + *(s32 *)&tptr[12] );
					}
				}
				break;

			case 2:
				gSPVertex( gp++, ptr, count, 0 );
				ptr += (count * 16);
				break;

			case 3:
#if 0
				while (count > 0)
				{
					gSP1Triangle( gp++, *(s16 *)&ptr[0], *(s16 *)&ptr[2], *(s16 *)&ptr[4], 0 );
					ptr += 8;
					count--;
				}
#else
				while (count >= 2)
				{
					gSP2Triangles( gp++,
												 *(s16 *)&ptr[0], *(s16 *)&ptr[2], *(s16 *)&ptr[4], 0,
												 *(s16 *)&ptr[8], *(s16 *)&ptr[10], *(s16 *)&ptr[12], 0 );
					ptr += 2*8;
					count -= 2;
				}
				if (count > 0)
				{
					gSP1Triangle( gp++, *(s16 *)&ptr[0], *(s16 *)&ptr[2], *(s16 *)&ptr[4], 0 );
					ptr += 8;
				}
#endif
				break;
		}
	}

gDPPipeSync( gp++ );
gDPSetAlphaCompare( gp++, G_AC_NONE );

	return( gp );
}


u32 Stadium_GetNum( u32 team )
{
	if (team > TEAM_SSN)
	{
		if (team != TEAM_3PT && team >= TEAM_LFP)
			team = TEAM_LAL;
		else
			team = 0;			// All-Stars.
	}

	return( team );
}


void Stadium_Init( void *workspace )
{
	SMaterial
		*mtl;
	vector16
		min,
		max;
	Gfx
		*gp;
	RGBA16
		*pals;
	u8
		*mem;
	s32
		i,
		j,
		size,
		team,
		offset;

	Table = AllocAndLoadFile( "CrowdPal.TAB", MEM_TMP );


	team = Stadium_GetNum( pcl__gMatch->asb__mTeam[0] );
	offset = (team * 2 * 16);

	pals = AllocAndLoadFile( "Seats.PAL", MEM_TMP );

	memcpy( LightSeatPal, &pals[offset], 16*2 );
	memcpy( DarkSeatPal, &pals[offset+16], 16*2 );

	MemFree( pals );


	{
		s32
			count;

		count = (CrowdAttendance / 2) + 50;
		memset( PhotoSeating, -1, 2*4 );

		for (j=0; j<2; j++)
		{
			for (i=0; i<4; i++)
			{
				PhotoSeating[j][i] = (rand() * 6) >> 15;
			}
		}
	}


	SetupFront();


	AdsObj = AllocAndLoadFile( "ads.obj", MEM_ANY );
	AdsMtls = AllocAndLoadFile( "ads.mtl", MEM_ANY );
	FixUpMatList( AdsMtls, 7 );


	EntryObj = AllocAndLoadFile( "Entrance.OBJ", MEM_ANY );
	MainObj = AllocAndLoadFile( "stadium.obj", MEM_ANY );
	RoofObj = AllocAndLoadFile( "Roof.OBJ", MEM_ANY );
	RailsObj = AllocAndLoadFile( "rails.obj", MEM_ANY );
	Materials = (SMaterial *)AllocAndLoadFile( "stadium.mtl", MEM_ANY );
	FixUpMatList( Materials, 17 );


	// Table color...

	pals = AllocAndLoadFile( "PostCols.PAL", MEM_TMP );

	Materials[MTL_TABLE].Palette[1] = pals[team*2];

	MemFree( pals );

	Pal_SetAlpha( Materials[MTL_TABLE].Palette, 255, 16 );


	gDPSetCombineMode( &TexModeCombine, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gDPSetCombineMode( &ColModeCombine, G_CC_SHADE, G_CC_SHADE );
	gp = DrawObject( workspace, EntryObj, Materials );
	gSPEndDisplayList( gp++ );
	size = (s32)gp - (s32)workspace;
	EntryList = (Gfx *)MemMalloc( size, MEM_ANY );
	gp = DrawObject( EntryList, EntryObj, Materials );
	gSPEndDisplayList( gp++ );


	{
		vector16
			min,
			max;

		Obj_CalcExtents( RoofObj, &min, &max );

		RoofMin.x = (s32)min.x << 1;
		RoofMin.y = (s32)min.y << 1;
		RoofMin.z = (s32)min.z << 1;

		RoofMax.x = (s32)max.x << 1;
		RoofMax.y = (s32)max.y << 1;
		RoofMax.z = (s32)max.z << 1;
	}

	gDPSetCombineMode( &TexModeCombine, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gDPSetCombineMode( &ColModeCombine, G_CC_SHADE, G_CC_SHADE );
	gp = DrawObject( workspace, RoofObj, Materials );
	gSPEndDisplayList( gp++ );
	size = (s32)gp - (s32)workspace;
	RoofList = (Gfx *)MemMalloc( size, MEM_ANY );
	gp = DrawObject( RoofList, RoofObj, Materials );
	gSPEndDisplayList( gp++ );


	gDPSetCombineMode( &TexModeCombine, G_CC_MODULATERGB, G_CC_MODULATERGB );
	gp = DrawObject( workspace, AdsObj, AdsMtls );
	gSPEndDisplayList( gp++ );
	size = (s32)gp - (s32)workspace;
	AdsList = (Gfx *)MemMalloc( size, MEM_ANY );
	gp = DrawObject( AdsList, AdsObj, AdsMtls );
	gSPEndDisplayList( gp++ );


	{
		SFlatCrowd
			*crowd;

		mem = MemMalloc( 2*8*64*32, MEM_ANY );
		for (j=0; j<2; j++)
		{
			for (i=0; i<FRAME_COUNT; i++)
			{
				FlatTexs[j][i] = mem;
				mem += (64*32);
			}
		}

		crowd = Flat_Load( workspace );

		SetupSeating( CrowdAttendance );
		Flat_Build( crowd );

		Materials[MTL_FLAT1].Texture = FlatTexs[0][0];
		Materials[MTL_FLAT2].Texture = FlatTexs[1][0];

		Pal_SetAlpha( Materials[MTL_UPPER].Palette, 255, 256 );
	}


	// Front crowd...

	{
		SMaterial
			*mtl = (SMaterial *)Materials;
		FH
			file;
		SFrontCrowd
			*crowd;
		SOverlay
			*overlay;
		s32
			frame;
		s32
			t;

		mem = MemMalloc( 10*64*32, MEM_ANY );
		for (j=0; j<2; j++)
		{
			for (i=0; i<5; i++)
			{
				BenchTexs[j][i] = mem;
				mem += 64*32;
			}
		}

		crowd = Front_Load( workspace );

		memcpy( BenchTexs[0][0], crowd->BenchTexs[0][0].Alpha, 10*64*32 );
		mtl[MTL_BENCHHOME].Texture = BenchTexs[0][0];
		mtl[MTL_BENCHAWAY].Texture = BenchTexs[1][0];


		mem = MemMalloc( 6*FRAME_COUNT*64*32, MEM_ANY );
memset( mem, 0, 6*FRAME_COUNT*64*32 );
		for (j=0; j<6; j++)
		{
			for (i=0; i<FRAME_COUNT; i++)
			{
				FrontCrowd[j][i] = mem;
				mem += 64*32;
			}
		}


		for (i=0; i<FRAME_COUNT; i++)
		{
			for (j=0; j<6; j++)
			{
				for (t=0; t<5; t++)
					PutFront( &crowd->Chair, FrontXs[t], 9, FrontCrowd[j][i] );

				for (t=0; t<5; t++)
				{
					if (FrontSeating[j][t] >= 0)
						PutFront( &crowd->People[FrontSeating[j][t]][i], FrontXs[t], 0, FrontCrowd[j][i] );
				}
			}
		}


		if (!PracticeMode)
		{
			for (i=0; i<FRAME_COUNT; i++)
			{
				for (j=0; j<4; j++)
				{
					t = PhotoSeating[0][j];
					PutFront( &crowd->Photos[t][i], PhotoXs[j], 0, FrontCrowd[4][i] );

					t = PhotoSeating[1][j];
					PutFront( &crowd->Photos[t][i], PhotoXs[j], 0, FrontCrowd[5][i] );
				}
			}
		}
	}


	// Luxury...

	Lux_Build( Lux_Load( workspace ) );


	// Upper...

	Up_Build( Up_Load( workspace ) );


	Pal_SetAlpha( Materials[MTL_STAIRS].Palette, 255, 256 );
	Pal_SetAlpha( Materials[MTL_CEMENT].Palette, 255, 256 );


	ScoreBrd_Init( workspace );
	Lights_Init();


	MemFree( Table );
}


void Stadium_Deinit()
{
	Lights_Deinit();
	ScoreBrd_Deinit();


	MemFree( FrontCrowd[0][0] );
	MemFree( BenchTexs[0][0] );
	MemFree( FlatTexs[0][0] );

	MemFree( AdsList );
	MemFree( RoofList );
	MemFree( EntryList );

	MemFree( Materials );
	MemFree( RailsObj );
	MemFree( RoofObj );
	MemFree( MainObj );
	MemFree( EntryObj );

	MemFree( AdsMtls );
	MemFree( AdsObj );
}


void Stadium_Update()
{
	frame = CrowdAnims[CROWD_HOME].Frame;
	if (frame > FRAME_CLAP3) frame -= 3;
	Materials[MTL_BENCHHOME].Texture = BenchTexs[0][frame];

	frame = CrowdAnims[CROWD_AWAY].Frame;
	if (frame > FRAME_CLAP3) frame -= 3;
	Materials[MTL_BENCHAWAY].Texture = BenchTexs[1][frame];


	frame = CrowdAnims[CROWD_MAIN].Frame;

	Materials[MTL_FRONT1].Texture = FrontCrowd[0][frame];
	Materials[MTL_FRONT2].Texture = FrontCrowd[1][frame];
	Materials[MTL_FRONT3].Texture = FrontCrowd[2][frame];
	Materials[MTL_FRONT4].Texture = FrontCrowd[3][frame];

	Materials[MTL_PHOTOS1].Texture = FrontCrowd[4][frame];
	Materials[MTL_PHOTOS2].Texture = FrontCrowd[5][frame];

	Materials[MTL_FLAT1].Texture = FlatTexs[0][frame];
	Materials[MTL_FLAT2].Texture = FlatTexs[1][frame];
}


Gfx *Stadium_DrawEntry( Gfx *gp )
{
	gDPPipeSync( gp++ );
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gSPSetGeometryMode(gp++,G_SHADE | G_SHADING_SMOOTH);
	gDPSetCombineMode( gp++, G_CC_SHADE, G_CC_SHADE );
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
	gSPDisplayList( gp++, EntryList );

	return( gp );
}


Gfx *Stadium_DrawMain( Gfx *gp )
{
	gDPSetBlendColor( gp++, 0, 0, 0, 166 );
	gDPSetAlphaCompare( gp++, G_AC_THRESHOLD );
	gSPSetGeometryMode(gp++,G_SHADE | G_SHADING_SMOOTH);
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
	gDPSetCombineMode( &TexModeCombine, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gDPSetCombineMode( &ColModeCombine, G_CC_SHADE, G_CC_SHADE );
	gp = DrawObject( gp, MainObj, Materials );
	gp = DrawObject( gp, RailsObj, Materials );

	gDPPipeSync( gp++ );
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gDPSetRenderMode( gp++, G_RM_AA_OPA_SURF, G_RM_AA_OPA_SURF2 );
	gSPDisplayList( gp++, AdsList );


	gp = ScoreBrd_Draw( gp );


	return( gp );
}


Gfx *Stadium_DrawRoof( Gfx *gp, s32 db )
{
	vector
		vec;
	s32
		x,
		y,
		z,
		diff,
		draw = FALSE;

	vec.x = 0;
	vec.y = 18000;
	vec.z = 44550;
	VecMulMat( &vec, &View->Cam2World, &vec );

	if (vec.y <= 0)
	{
		if (vec.y == 0 && View->Camera.y > RoofMin.y)
			draw = TRUE;
	}
	else
	{
		y = vec.y + View->Camera.y;

		if (y > RoofMin.y)
		{
			diff = (RoofMin.y - View->Camera.y);

			x = View->Camera.x + ((vec.x * diff) / vec.y);
			z = View->Camera.z + ((vec.z * diff) / vec.y);

			if (x > RoofMin.x &&
					x < RoofMax.x &&
					z > RoofMin.z &&
					z < RoofMax.z)
			{
				draw = TRUE;
			}
		}
	}

	if (draw)
	{
		gDPSetBlendColor( gp++, 0, 0, 0, 166 );
		gDPSetAlphaCompare( gp++, G_AC_THRESHOLD );
		gSPSetGeometryMode(gp++,G_SHADE | G_SHADING_SMOOTH);
		gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
		gSPDisplayList( gp++, RoofList );

		gp = Lights_Draw( gp, db );
	}

	return( gp );
}
