#include "driver2.h"
#include "debris.h"
#include "motion_c.h"
#include "texture.h"
#include "pres.h"
#include "director.h"
#include "pause.h"
#include "sound.h"
#include "main.h"
#include "system.h"
#include "cars.h"
#include "camera.h"
#include "dr2roads.h"
#include "convert.h"
#include "map.h"
#include "mission.h"
#include "draw.h"
#include "models.h"
#include "players.h"
#include "shadow.h"
#include "cosmetic.h"
#include "denting.h"
#include "gamesnd.h"

#include "ASM/rndrasm.h"

struct DAMAGED_LAMP
{
	int index;
	char damage;
};

struct DAMAGED_OBJECT
{
	CELL_OBJECT cop;
	char active;
	char damage;
	int rot_speed;
	SVECTOR velocity;
	int vx;
};

struct SMOKE
{
	UnpaddedHackVector position;
	UnpaddedCharVector drift;
	UnpaddedCharVector drift_change;
	UnpaddedHackVector final_tail_pos;
	u_char step;
	u_char pos;
	short start_w;
	short final_w;
	char life;
	char halflife;
	u_short flags;
	u_char num;
	u_char t_step;
	short transparency;
};

struct DEBRIS
{
	VECTOR position;
	SVECTOR direction;
	u_short life;
	u_short flags;
	u_short num;
	u_short pos;
	RGB rgb;
	char step;
	char type;
};

struct LEAF
{
	VECTOR position;
	SVECTOR direction;
	u_short life;
	u_short flags;
	u_short num;
	u_short pos;
	RGB rgb;
	char step;
	char type;
	short sin_index1;
	short sin_index2;
	char sin_addition1;
	char sin_addition2;
};

struct TRI_POINT
{
	BVECTOR v0;
	BVECTOR v1;
	BVECTOR v2;
};

struct TRI_POINT_LONG
{
	VECTOR_NOPAD v0;
	VECTOR_NOPAD v1;
	VECTOR_NOPAD v2;
};

struct RAIN_TYPE
{
	VECTOR_NOPAD position;
	SVECTOR oldposition;
};

struct LAMP_STREAK
{
	SXYPAIR light_trails[4];
	int id;
	short clock;
	char set;
};

TEXTURE_DETAILS digit_texture;

TRI_POINT debris_rot1[32]; // offset 0xC0A60
TRI_POINT debris_rot2[32]; // offset 0xC0BE0
TRI_POINT debris_rot3[32]; // offset 0xC0D60
TRI_POINT leaf_rot[32]; // offset 0xC1DC0
TRI_POINT litter_rot[32]; // offset 0xC08D0

TRI_POINT* debris_rot_table[5] = {
	debris_rot1,
	leaf_rot,
	litter_rot,
	debris_rot2,
	debris_rot3,
};

CVECTOR debris_colour[4][31] =
{
	{
		{ 100, 100, 100, 0 },
		{ 214, 214, 244, 0 },
		{ 195, 79, 65, 0 },
		{ 83, 83, 101, 0 },
		{ 197, 142, 80, 0 },
		{ 222, 204, 196, 0 },
		{ 189, 168, 114, 0 },
		{ 203, 180, 121, 0 },
		{ 132, 145, 103, 0 },
		{ 168, 143, 122, 0 },
		{ 107, 107, 107, 0 },
		{ 192, 110, 110, 0 },
		{ 130, 130, 130, 0 },
		{ 101, 129, 145, 0 },
		{ 130, 130, 130, 0 },
		{ 181, 88, 88, 0 },
		{ 130, 105, 83, 0 },
		{ 164, 164, 164, 0 },
		{ 126, 143, 108, 0 },
		{ 140, 163, 181, 0 },
		{ 143, 129, 152, 0 },
		{ 156, 138, 118, 0 },
		{ 190, 190, 190, 0 },
		{ 203, 112, 112, 0 },
		{ 255, 242, 201, 0 },
		{ 167, 145, 90, 0 },
		{ 190, 190, 190, 0 },
		{ 150, 164, 184, 0 },
		{ 102, 102, 102, 0 },
		{ 140, 114, 99, 0 },
		{ 75, 63, 134, 0 },
	}, {
		{ 100, 100, 100, 0 },
		{ 83, 82, 97, 0 },
		{ 138, 179, 201, 0 },
		{ 114, 159, 183, 0 },
		{ 107, 61, 46, 0 },
		{ 162, 120, 76, 0 },
		{ 235, 227, 214, 0 },
		{ 197, 138, 121, 0 },
		{ 159, 169, 131, 0 },
		{ 146, 178, 195, 0 },
		{ 181, 151, 101, 0 },
		{ 230, 191, 139, 0 },
		{ 147, 147, 151, 0 },
		{ 182, 127, 138, 0 },
		{ 160, 165, 127, 0 },
		{ 175, 203, 225, 0 },
		{ 211, 175, 117, 0 },
		{ 209, 167, 118, 0 },
		{ 157, 147, 140, 0 },
		{ 210, 191, 145, 0 },
		{ 155, 185, 148, 0 },
		{ 158, 210, 254, 0 },
		{ 206, 176, 134, 0 },
		{ 236, 173, 117, 0 },
		{ 146, 146, 146, 0 },
		{ 88, 103, 128, 0 },
		{ 96, 96, 96, 0 },
		{ 125, 92, 92, 0 },
		{ 184, 174, 155, 0 },
		{ 42, 42, 42, 0 },
		{ 219, 37, 130, 0 },
	}, {
		{ 100, 100, 100, 0 },
		{ 112, 110, 112, 0 },
		{ 230, 212, 214, 0 },
		{ 178, 181, 175, 0 },
		{ 120, 120, 120, 0 },
		{ 102, 101, 115, 0 },
		{ 70, 83, 100, 0 },
		{ 206, 108, 88, 0 },
		{ 220, 136, 107, 0 },
		{ 97, 97, 97, 0 },
		{ 139, 116, 150, 0 },
		{ 173, 173, 173, 0 },
		{ 104, 133, 173, 0 },
		{ 224, 196, 139, 0 },
		{ 108, 146, 141, 0 },
		{ 82, 82, 82, 0 },
		{ 123, 123, 123, 0 },
		{ 140, 67, 67, 0 },
		{ 103, 92, 121, 0 },
		{ 219, 192, 148, 0 },
		{ 160, 185, 195, 0 },
		{ 158, 173, 130, 0 },
		{ 183, 183, 183, 0 },
		{ 126, 98, 84, 0 },
		{ 126, 125, 156, 0 },
		{ 36, 74, 203, 0 },
		{ 105, 105, 105, 0 },
		{ 162, 179, 183, 0 },
		{ 102, 130, 162, 0 },
		{ 149, 88, 88, 0 },
		{ 119, 145, 129, 0 },
	}, {
		{ 100, 100, 100, 0 },
		{ 153, 149, 150, 0 },
		{ 118, 110, 131, 0 },
		{ 135, 110, 89, 0 },
		{ 191, 102, 66, 0 },
		{ 95, 90, 101, 0 },
		{ 157, 171, 186, 0 },
		{ 203, 184, 132, 0 },
		{ 221, 100, 100, 0 },
		{ 149, 149, 149, 0 },
		{ 100, 118, 145, 0 },
		{ 78, 78, 78, 0 },
		{ 105, 128, 107, 0 },
		{ 203, 94, 94, 0 },
		{ 163, 178, 152, 0 },
		{ 167, 119, 117, 0 },
		{ 117, 139, 142, 0 },
		{ 160, 160, 160, 0 },
		{ 92, 92, 92, 0 },
		{ 229, 144, 136, 0 },
		{ 145, 167, 149, 0 },
		{ 198, 173, 173, 0 },
		{ 137, 163, 175, 0 },
		{ 173, 173, 173, 0 },
		{ 143, 143, 143, 0 },
		{ 218, 198, 154, 0 },
		{ 104, 134, 78, 0 },
		{ 172, 116, 86, 0 },
		{ 155, 170, 185, 0 },
		{ 122, 142, 147, 0 },
		{ 193, 193, 193, 0 }
	}
};

u_char grassColour[4][3] = {
	{110, 115, 67},
	{64, 55, 49},
	{91, 104, 56},
	{118, 108, 89}
};


TRI_POINT_LONG debris1_vert =
{
	{-16,0,0},
	{0,-16,0},
	{16,16,0}
};

TRI_POINT_LONG debris2_vert =
{
	{-4,4,0},
	{0,-4,0},
	{4,4,0}
};

TRI_POINT_LONG debris3_vert =
{
	{-80,0,15},
	{0, 0,15},
	{80,0,-15}
};

TRI_POINT_LONG leaf_vert =
{
	{-30,5,7},
	{0,0,10},
	{0,0,-7}
};

TRI_POINT_LONG litter_vert =
{
	{-25,0,25},
	{25,0,25},
	{-25,0,-25}
};

TRI_POINT_LONG* debris_data[5] = {
	&debris1_vert,
	&leaf_vert,
	&litter_vert,
	&debris2_vert,
	&debris3_vert,
};

VECTOR dummy = { 0 };

int gNight = 0;
u_char gRainCount = 30;
int gEffectsTimer = 41;

int NextDamagedPmeter = 0;
int SmashablesHit = 0;
DAMAGED_OBJECT damaged_object[MAX_SMASHED_OBJECTS];

int next_debris = 0;
short debris_alloc[MAX_DEBRIS];

int next_leaf = 0;
short leaf_alloc[MAX_LEAVES];

int next_smoke = 0;
short smoke_alloc[MAX_SMOKE];

int gNextRainDrop = 0;
short gRainAlloc[MAX_RAIN_DROPS];

RAIN_TYPE gRain[MAX_RAIN_DROPS];

char PoolPrimData[16] = {

	0x0,  0x8,  0x2,  0x9,
	0x8,  0x1,  0x9,  0x3,
	0x4,  0xA,  0x6,  0xB,
	0xA,  0x5,  0xB,  0x7
};

int LightSortCorrect = 0;

TEXTURE_DETAILS smoke_texture;
TEXTURE_DETAILS debris_texture;
TEXTURE_DETAILS litter_texture;
TEXTURE_DETAILS cop_texture;
TEXTURE_DETAILS light_texture;
TEXTURE_DETAILS gTyreTexture;
TEXTURE_DETAILS flare_texture;
TEXTURE_DETAILS sea_texture;
TEXTURE_DETAILS bird_texture1;
TEXTURE_DETAILS bird_texture2;
TEXTURE_DETAILS lensflare_texture;
TEXTURE_DETAILS sun_texture;
TEXTURE_DETAILS moon_texture;
TEXTURE_DETAILS drop_texture;
TEXTURE_DETAILS collon_texture;

TEXTURE_DETAILS texturePedHead;
TEXTURE_DETAILS tannerShadow_texture;

TEXTURE_DETAILS lightref_texture;
TEXTURE_DETAILS light_pool_texture;

LAMP_STREAK Known_Lamps[MAX_LAMP_STREAKS];
int NewLamp[MAX_LAMP_STREAKS];
int LightIndex = 0;

int variable_weather = 0;

int gDoLeaves = 1;

SMOKE* smoke_table;
SMOKE smoke[MAX_SMOKE];

SVECTOR leaf_rotvec;
LEAF leaf[MAX_LEAVES];

SVECTOR debris_rotvec;
DEBRIS debris[MAX_DEBRIS];

int StreakCount1 = 0;
int main_cop_light_pos = 0;
int NextDamagedLamp = 0;

CELL_OBJECT ground_debris[MAX_GROUND_DEBRIS];
int groundDebrisIndex = 0;

DAMAGED_LAMP damaged_lamp[MAX_DAMAGED_LAMPS];

MATRIX debris_mat;
MATRIX leaf_mat;

// [D] [T]
void PlacePoolForCar(CAR_DATA *cp, CVECTOR *col, int front, int in_car)
{
	short brightness;
	POLY_FT4 *poly;
	int i;
	SVECTOR s[27];
	SVECTOR sout[27];
	VECTOR s1[12];
	VECTOR mid_position;
	VECTOR toss;
	VECTOR *pos;
	CAR_COSMETICS* car_cos;
	CVECTOR color;
	int Z;
	int sub_level;
	int car_road_height;
	int count;

	pos = (VECTOR*)cp->hd.where.t;
	car_cos = &car_cosmetics[cp->ap.model];

	// [A] there was check that prevented in_car lights in game, but it was working in quick replays...

	if (front) 
	{
		s1[3].vz = -(car_cos->colBox.vz + 50);
		s1[8].vz = s1[3].vz - 1160;
		s1[6].vz = s1[3].vz;
		s1[7].vz = s1[3].vz;

		if (in_car) 
		{
			// slightly shifted vertices to make it look more beautiful
			s1[1].vz = s1[8].vz + 600;
			s1[0].vx = 136;
			s1[1].vx = -344;
			s1[2].vx = -21;
			s1[3].vx = -143;
			s1[4].vx = 344;
			s1[5].vx = -136;
			s1[6].vx = 143;
			s1[7].vx = 21;
			s1[8].vz = s1[8].vz - 400;
			s1[9].vz = s1[3].vz + 10;
			s1[8].vx = -82;
			s1[10].vx = 82;
			s1[9].vx = -82;
			s1[11].vx = 82;
			s1[4].vz = s1[1].vz;
			s1[5].vz = s1[1].vz;
			s1[10].vz = s1[8].vz;
			s1[11].vz = s1[9].vz;

			LightSortCorrect = -800;
			
			sub_level = 3;
		}
		else 
		{
			s1[1].vz = s1[8].vz + 100;
			s1[0].vx = 136;
			s1[1].vx = -344;
			s1[2].vx = -21;
			s1[3].vx = -143;
			s1[4].vx = 344;
			s1[5].vx = -136;
			s1[6].vx = 143;
			s1[7].vx = 21;
			s1[10].vx = 82;
			s1[11].vx = 82;
			s1[8].vx = -82;
			s1[9].vx = -82;
			s1[4].vz = s1[1].vz;
			s1[5].vz = s1[1].vz;
			s1[9].vz = s1[3].vz;
			s1[10].vz = s1[8].vz;
			s1[11].vz = s1[3].vz;

			sub_level = 3;

			if (player[CurrentPlayerView].cameraView == 2 && cp == &car_data[player[CurrentPlayerView].playerCarId])
				LightSortCorrect = -320;
			else
				LightSortCorrect = -200;
		}
	}
	else
	{
		// back light
	
		s1[0].vx = -204;
		s1[1].vx = 204;
		s1[2].vx = -204;
		s1[3].vx = 204;
		s1[3].vz = (car_cos->colBox.vz - 10);
		s1[1].vz = s1[3].vz + 204;

		sub_level = 0;
	}

	s1[0].vz = s1[1].vz;
	s1[2].vz = s1[3].vz;

	SetRotMatrix(&cp->hd.drawCarMat);

	mid_position.vx = 0;
	mid_position.vy = 0;
	mid_position.vz = -500;

	_MatrixRotate(&mid_position);

	mid_position.vx += pos->vx;
	mid_position.vy += pos->vy;
	mid_position.vz += pos->vz;

	car_road_height = MapHeight(&mid_position);
	
	if (sub_level == 3)
		count = 12;
	else
		count = 4;

	// adjust height and poisition for each vertex
	for(i = 0; i < count; i++)
	{
		int temp_y;

		s1[i].vy = 0;
		_MatrixRotate(&s1[i]);

		toss.vx = s1[i].vx + pos->vx;
		toss.vy = s1[i].vy + pos->vy;
		toss.vz = s1[i].vz + pos->vz;

		s[i].vx = toss.vx - camera_position.vx;
		s[i].vz = toss.vz - camera_position.vz;

		temp_y = MapHeight(&toss);

		if (ABS(-temp_y + car_road_height) > 500)
			s[i].vy = -car_road_height - camera_position.vy;
		else
			s[i].vy = -temp_y - camera_position.vy;
	}

	if (FIXEDH(s[0].vy * s[1].vz - s[0].vz * s[1].vy) * s[2].vx +
		FIXEDH(s[0].vz * s[1].vx - s[0].vx * s[1].vz) * s[2].vy + 
		FIXEDH(s[0].vx * s[1].vy - s[0].vy * s[1].vx) * s[2].vz >= 0)
	{
		gte_SetRotMatrix(&inv_camera_matrix);
		gte_SetTransVector(&dummy);

		if (sub_level == 0) 
		{
			gte_ldv3(&s[0], &s[1], &s[2]);
			gte_rtpt();

			gte_stszotz(&Z);

			if (Z > 40)
				Z -= 40;

			if (Z > 49)
			{
				poly = (POLY_FT4 *)current->primptr;

				// [A] Emit poly only after ot z checked
				setPolyFT4(poly);
				setSemiTrans(poly, 1);

				*(ushort*)&poly->u0 = *(ushort*)&light_pool_texture.coords.u0;
				*(ushort*)&poly->u1 = *(ushort*)&light_pool_texture.coords.u1;
				*(ushort*)&poly->u2 = *(ushort*)&light_pool_texture.coords.u2;
				*(ushort*)&poly->u3 = *(ushort*)&light_pool_texture.coords.u3;

				poly->r0 = col->r / 2;
				poly->g0 = col->g / 2;
				poly->b0 = col->b / 2;

				gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

				poly->tpage = light_pool_texture.tpageid | 0x20;
				poly->clut = light_pool_texture.clutid;

				gte_ldv0(&s[3]);
				gte_rtps();
				gte_stsxy(&poly->x3);

				addPrim(current->ot + (Z >> 1), poly);
				current->primptr += sizeof(POLY_FT4);
			}
		}
		else if (sub_level == 3)
		{
			for(i = 0; i < 12; i++)
			{
				gte_ldv0(&s[i]);
				gte_rtv0tr();
				gte_stsv(&sout[i]);
			}

			// draw front light quads
			for(i = 0; i < 4; i++)
			{
				char* VertIdx;
				
				VertIdx = PoolPrimData + i * 4;

				gte_SetRotMatrix(&identity);
				gte_SetTransVector(&dummy);

				if (i & 2)
					brightness = LeftLight + LeftLight * 4;
				else
					brightness = RightLight + RightLight * 4;

				if (brightness)
				{
					int test = col->r * brightness;

					color.r = MIN(255, col->r * brightness >> 4);
					color.g = MIN(255, col->g * brightness >> 4);
					color.b = MIN(255, col->b * brightness >> 4);

					if (i & 1) 
					{
						sQuad(sout + VertIdx[0],
						      sout + VertIdx[2],
						      sout + VertIdx[3],
						      sout + VertIdx[1], &color, LightSortCorrect);
					}
					else
					{
						sQuad(sout + VertIdx[1],
						      sout + VertIdx[3],
						      sout + VertIdx[2],
						      sout + VertIdx[0], &color, LightSortCorrect);
					}
				}
			}
		}

		LightSortCorrect = -10;
	}
}

// [D] [T]
int AllocateLeaf(void)
{
	if (next_leaf < MAX_LEAVES)
		return leaf_alloc[next_leaf++];

	return -1;
}

// [D] [T]
void ReleaseLeaf(short num)
{
	leaf_alloc[--next_leaf] = num;
}

// [D] [T]
void AddLeaf(VECTOR *Position, int num_leaves, int Type)
{
	int num;
	int temprand;
	int i;

	if (gDoLeaves == 0)
		return;

	Position->vy = -Position->vy;
	Position->pad = MapHeight(Position);
	for (i = 0; i < num_leaves; i++)
	{
		num = AllocateLeaf();
		
		if (num == -1)
			return;
	
		LEAF* myleaf = &leaf[num];
		
		temprand = rand();

		myleaf->position.vx = Position->vx + (temprand & 0xfe);
		myleaf->position.vz = Position->vz + (temprand >> 8 & 0xfe);

		if (Type == 1)
			myleaf->position.vy = -Position->vy;
		else
			myleaf->position.vy = -Position->pad;
	
		myleaf->position.pad = Position->pad;

		myleaf->life = 600;
		myleaf->flags = 0x2;
	
		myleaf->direction.vx = 0;
		myleaf->direction.vy = 0;
		myleaf->direction.vz = 0;
		
		myleaf->pos = (temprand >> 7) & 0xff;
		myleaf->step = (temprand & 7) + 1;

		if (Type == 1)
		{
			// tree leaves
			if ((temprand & 3) == 0)
			{
				myleaf->rgb.r = 110;
				myleaf->rgb.g = 112;
				myleaf->rgb.b = 15;
			}
			else if ((temprand & 3) == 1)
			{
				myleaf->rgb.r = 20;
				myleaf->rgb.g = 44;
				myleaf->rgb.b = 0;
			}
			else
			{
				myleaf->rgb.r = 60;
				myleaf->rgb.g = 50;
				myleaf->rgb.b = 30;
			}
		}
		else
		{
			// news, papers etc
			myleaf->rgb.r = myleaf->rgb.g = myleaf->rgb.b = temprand & 0x1f;
		}

		// apply colors
		if (gTimeOfDay == 3)
		{
			*(u_int*)&myleaf->rgb = *(u_int*)&myleaf->rgb >> 2 & 0x3f3f3f3f;
		}
		else if (gWeather == 1 || gWeather == 2)
		{
			*(u_int*)&myleaf->rgb = *(u_int*)&myleaf->rgb >> 1 & 0x7f7f7f7f;
		}

		myleaf->sin_index1 = temprand & 0xfff;
		myleaf->sin_index2 = temprand >> 4 & 0xfff;
		myleaf->sin_addition2 = -(temprand >> 8 & 7);
		myleaf->sin_addition1 = -(temprand & 3);
	
		myleaf->type = Type;
	}
}

// [D] [T]
void SwirlLeaves(CAR_DATA *cp)
{
	int XDiff;
	int ZDiff;
	LEAF *lpLeaf;
	int count;
	VECTOR plpos;

	lpLeaf = leaf;
	plpos.vx = cp->hd.where.t[0];
	plpos.vy = cp->hd.where.t[1];
	plpos.vz = cp->hd.where.t[2];

	if (cp->hd.wheel_speed + 39999U <= 0x1d4be || gDoLeaves == 0 || pauseflag != 0)
		return;

	for (count = 0; count < MAX_LEAVES; count++)
	{
		XDiff = plpos.vx - lpLeaf->position.vx;
		ZDiff = plpos.vz - lpLeaf->position.vz;

		if ((lpLeaf->flags & 0x2) && 
			lpLeaf->position.vy + plpos.vy > -180 &&
			XDiff > -360 && XDiff < 360 && ZDiff > -360 && ZDiff < 360)
		{
			// set lifting direction
			lpLeaf->direction.vy = -25 - (rand() & 0x1f);
			lpLeaf->position.vy -= 1;
		}
		
		lpLeaf++;
	}
}

// [D] [T]
void InitDebrisNames(void)
{
	GetTextureDetails("SMOKE", &smoke_texture);
	GetTextureDetails("DEBRIS", &debris_texture);
	GetTextureDetails("LITTER", &litter_texture);
	GetTextureDetails("COPLIGHT", &cop_texture);
	GetTextureDetails("LIGHT", &light_texture);
	GetTextureDetails("SKID", &gTyreTexture);
	GetTextureDetails("FLARE", &flare_texture);
	GetTextureDetails("SPLASH", &sea_texture);
	GetTextureDetails("SWBIRD1", &bird_texture1);
	GetTextureDetails("SWBIRD2", &bird_texture2);
	GetTextureDetails("LENSFLR", &lensflare_texture);
	GetTextureDetails("SKYSUN", &sun_texture);
	GetTextureDetails("SKYMOON", &moon_texture);
	GetTextureDetails("DROP", &drop_texture);

	GetTextureDetails("DIGIT", &digit_texture);
	SetCLUT16Flags(digit_texture.clutid, 6, 0);

	GetTextureDetails("COLLON", &collon_texture);
	GetTextureDetails("JEANS1", &jeans_texture);
	GetTextureDetails("ARM1", &arm1_texture);
	GetTextureDetails("FOREARM1", &forearm1_texture);
	GetTextureDetails("CHEST1", &chest1_texture);
	GetTextureDetails("LOWHEAD", &head1_texture);
	GetTextureDetails("ADDCAM", &addcam);
	GetTextureDetails("FRAMEADV", &frameadv);
	GetTextureDetails("AUTO", &autocam);
	GetTextureDetails("CHASEC", &chasecar);
	GetTextureDetails("CHOOSECA", &choosecar);
	GetTextureDetails("CLOCK", &clock);
	GetTextureDetails("DELCAM", &delcam);
	GetTextureDetails("EDITCAM", &editcam);
	GetTextureDetails("FIXEDCA", &fixedcam);
	GetTextureDetails("INCAR", &incar);
	GetTextureDetails("LENSCHA", &lenschan);
	GetTextureDetails("LOOKCAR", &lookcar);
	GetTextureDetails("MOVECMP", &movecam);
	GetTextureDetails("MOVECAM", &movecampos);
	GetTextureDetails("OK", &ok);
	GetTextureDetails("PAUSE", &pause);
	GetTextureDetails("PLAYCAM", &playcam);
	GetTextureDetails("PLAYPAU", &playpause);
	GetTextureDetails("SAVE2CA", &save2card);
	GetTextureDetails("RESTREP", &restart);
	GetTextureDetails("HEAD1", &texturePedHead);
	GetTextureDetails("TSHADOW", &tannerShadow_texture);

	head1_texture.coords.u1 = head1_texture.coords.u0 + 8;
	head1_texture.coords.u3 = head1_texture.coords.u1;

	GetTextureDetails("LIGHTREF", &lightref_texture);
	GetTextureDetails("LIGHT", &light_pool_texture);

	InitButtonTextures();
	InitTannerShadow();
}

// [D] [T]
void InitDebris(void)
{
	int i, j;
	TRI_POINT_LONG temptri;

	StreakCount1 = 0;
	next_debris = 0;
	next_smoke = 0;
	next_leaf = 0;
	NextDamagedLamp = 0;
	smoke_table = NULL;

	main_cop_light_pos = rand() % 7;

	gDoLeaves = (NumPlayers == 1); // [A] disable in multiplayer (temporarily) due to their flicker

	for (i = 0; i < MAX_LAMP_STREAKS; i++)
	{
		Known_Lamps[i].set = 0;
	}

	for (i = 0; i < MAX_DEBRIS; i++)
	{
		debris_alloc[i] = i;

		debris[i].pos = 0;
		debris[i].flags = 0;
		debris[i].num = i;
	}

	for (i = 0; i < MAX_GROUND_DEBRIS; i++)
	{
		ground_debris[i].type = 0xffff;
	}

	groundDebrisIndex = 0;

	debris_rotvec.vz = 0;
	debris_rotvec.vy = 0;
	debris_rotvec.vx = 0;

	leaf_rotvec.vz = 0;
	leaf_rotvec.vy = 0;
	leaf_rotvec.vx = 0;

	for (i = 0; i < 32; i++)
	{
		RotMatrixXYZ(&debris_mat, &debris_rotvec);
		RotMatrixXYZ(&leaf_mat, &leaf_rotvec);

		debris_rotvec.vx += 128;
		debris_rotvec.vy += 256;
		debris_rotvec.vz += 128;
		leaf_rotvec.vy += 128;

		for (j = 0; j < 5; j++)
		{
			if (j == 1)
				SetRotMatrix(&leaf_mat);
			else
				SetRotMatrix(&debris_mat);

			temptri = *debris_data[j];

			_MatrixRotate((VECTOR *)&temptri.v0);
			_MatrixRotate((VECTOR *)&temptri.v1);
			_MatrixRotate((VECTOR *)&temptri.v2);

			debris_rot_table[j][i].v0.vx = temptri.v0.vx;
			debris_rot_table[j][i].v0.vy = temptri.v0.vy;
			debris_rot_table[j][i].v0.vz = temptri.v0.vz;

			debris_rot_table[j][i].v1.vx = temptri.v1.vx;
			debris_rot_table[j][i].v1.vy = temptri.v1.vy;
			debris_rot_table[j][i].v1.vz = temptri.v1.vz;

			debris_rot_table[j][i].v2.vx = temptri.v2.vx;
			debris_rot_table[j][i].v2.vy = temptri.v2.vy;
			debris_rot_table[j][i].v2.vz = temptri.v2.vz;
		}
	}

	for (i = 0; i < MAX_SMOKE; i++)
	{
		smoke_alloc[i] = i;
		
		smoke[i].pos = 0;
		smoke[i].flags = 0;
		smoke[i].num = i;
	}

	for (i = 0; i < MAX_LEAVES; i++)
	{
		leaf_alloc[i] = i;

		leaf[i].pos = 0;
		leaf[i].flags = 0;
		leaf[i].num = i;
	}

	ClearMem((char*)&damaged_lamp, sizeof(damaged_lamp));

	for (i = 0; i < MAX_SMASHED_OBJECTS; i++)
		damaged_object[i].active = 0;

	LightSortCorrect = -10;
	SmashablesHit = 0;
}

// [D] [T]
int AllocateDebris(void)
{
	if (next_debris < MAX_DEBRIS)
		return debris_alloc[next_debris++];

	return -1;
}

// [D] [T]
void ReleaseDebris(short num)
{
	debris_alloc[--next_debris] = num;
}


// [D] [T]
int AllocateSmoke(void)
{
	if (next_smoke < MAX_SMOKE)
		return smoke_alloc[next_smoke++];

	return -1;
}

// [D] [T]
void ReleaseSmoke(short num)
{
	smoke_alloc[--next_smoke] = num;
}

// [D] [T]
void AddGroundDebris(void)
{
	int seed;
	int number;
	int zbound;
	int xbound;
	int type;
	CELL_OBJECT *cop;
	MODEL *model;
	int count;
	VECTOR Position;

	if (car_data[0].hd.wheel_speed + 99999U <= 0x30d3e)
		return;

	count = 0;
	cop = ground_debris;

	do {
		if (cop->type != 0xffff)
		{
			model = modelpointers[cop->type];

			xbound = ABS(cop->pos.vx - camera_position.vx);
			zbound = ABS(cop->pos.vz - camera_position.vz);

			if (xbound <= 9000 && zbound <= 9000 && (xbound > 7000 || zbound > 7000))
			{
				if (next_leaf >= MAX_LEAVES)
					return;

				seed = rand();

				Position.vy = cop->pos.vy - 45;
				Position.vx = cop->pos.vx + ((seed & 0x3ff) - 512);
				Position.vz = cop->pos.vz + ((rand() & 0x3ff) - 512);

				number = seed & 7;

				if (model->flags2 & MODEL_FLAG_TREE)
				{
					type = 1;	// ground leaves
				}
				else 
				{
					number = seed & 3;
					type = 2;	// paper
				}

				AddLeaf(&Position, number + 1, type);
			}
		}

		count++;
		cop++;
	} while (count < MAX_GROUND_DEBRIS);
}

// [D] [T] [A]
void DrawSmashable_sprites(void)
{
	DAMAGED_OBJECT *dam;
	MODEL *model;
	int count;
	VECTOR pos;
	MATRIX object_matrix;

	dam = damaged_object;
	count = 0;
	do {
		if (dam->active)
		{
			model = modelpointers[dam->cop.type];

			InitMatrix(object_matrix);

			if ((model->shape_flags & SHAPE_FLAG_SPRITE) == 0)
				RotMatrixY(dam->rot_speed * dam->damage * 3 & 0xfff, &object_matrix);

			RotMatrixZ(dam->rot_speed * dam->damage & 0xfff, &object_matrix);

			pos.vx = dam->vx - camera_position.vx;
			pos.vy = dam->cop.pos.vy - camera_position.vy;
			pos.vz = dam->cop.pos.vz - camera_position.vz;

			Apply_Inv_CameraMatrix(&pos);

			gte_SetRotMatrix(&object_matrix);
			gte_SetTransVector(&pos);

			pos.vx = dam->vx;
			pos.vy = dam->cop.pos.vy;
			pos.vz = dam->cop.pos.vz;

			SetFrustrumMatrix();

			if (FrustrumCheck(&pos, model->bounding_sphere) != -1)
			{
				if (model->shape_flags & SHAPE_FLAG_SPRITE)
				{
					UNIMPLEMENTED();

					if (gWeather - 1U < 2 || gTimeOfDay == 3)
					{
						plotContext.colour = NightAmbient << 0x10 | NightAmbient << 8 | NightAmbient | 0x2c000000;
					}
					else 
					{
						plotContext.colour = 0x2c808080;
					}

					// [A]
					// TODO:
					//current->primptr = Asm_PlotSprite(model, current->primptr, current->ot, 0);
				}
				else
				{
					PlotMDL_less_than_128(model);
				}
			}
		}
	
		count++;
		dam++;
	} while (count < MAX_SMASHED_OBJECTS);
}


// [D] [T]
int MoveSmashable_object(void)
{
	DAMAGED_OBJECT *dam;
	int count;

	dam = damaged_object;
	count = 0;

	do {
		if (dam->active) 
		{
			if (dam->cop.pos.vy < 50 - player[0].pos[1]) 
			{
				dam->vx += dam->velocity.vx;
	
				dam->cop.pos.vy += dam->velocity.vy;
				dam->cop.pos.vz += dam->velocity.vz;

				dam->velocity.vy += 10;

				dam->damage++;
			}
			else 
			{
				dam->active = 0;
			}

		}
		count++;
		dam++;
	} while (count < MAX_SMASHED_OBJECTS);

	return 0;
}

// [D] [T]
void DisplayLightReflections(VECTOR* v1, CVECTOR* col, short size, TEXTURE_DETAILS* texture)
{
	POLY_FT4* poly;
	SVECTOR vert[4];
	CVECTOR thiscol;
	int z;

	if (wetness > 9 && v1->vy > camera_position.vy)
	{
		thiscol.r = col->r * wetness >> 16;
		thiscol.g = col->g * wetness >> 16;
		thiscol.b = col->b * wetness >> 16;
		
		gte_SetTransVector(v1);

		Apply_Inv_CameraMatrix(v1);

		gte_SetTransVector(v1);
		gte_SetRotMatrix(&face_camera);

		vert[0].vx = -size;
		vert[2].vx = -size;
		vert[1].vx = size;
		vert[3].vx = size;

		vert[0].vy = 0;
		vert[0].vz = 0;
		vert[1].vy = 0;
		vert[1].vz = 0;
		vert[2].vy = 0;
		vert[2].vz = 0;
		vert[3].vy = 0;
		vert[3].vz = 0;

		gte_ldv0(&vert[0]);

		gte_rtps();

		gte_stsz(&z);

		if (z >= 150)
		{
			poly = (POLY_FT4*)current->primptr;
			setPolyFT4(poly);
			setSemiTrans(poly, 1);

			gte_stsxy(&poly->x0);

			vert[2].vy = (z >> 3) + 0xfa;
			vert[3].vy = (z >> 3) + 0xfa;

			gte_ldv3(&vert[1], &vert[2], &vert[3]);

			gte_rtpt();

			gte_stsxy3(&poly->x1, &poly->x2, &poly->x3);

			poly->u0 = texture->coords.u0;
			poly->v0 = texture->coords.v0;
			poly->u1 = texture->coords.u1;
			poly->v1 = texture->coords.v1;
			poly->u2 = texture->coords.u2;
			poly->v2 = texture->coords.v2;
			poly->u3 = texture->coords.u3;
			poly->v3 = texture->coords.v3;

			poly->tpage = texture->tpageid | 0x20;
			poly->clut = texture->clutid;

			poly->r0 = thiscol.r;
			poly->g0 = thiscol.g;
			poly->b0 = thiscol.b;

			addPrim(current->ot + (z >> 4), poly);
			current->primptr += sizeof(POLY_FT4);
		}
	}
}

// [D] [T]
int find_lamp_streak(int LampId)
{
	int count;

	for (count = 0; count < MAX_LAMP_STREAKS; count++)
	{
		if (Known_Lamps[count].id == LampId)
		{
			Known_Lamps[count].set = 1;
			return count;
		}
	}

	// allocate new streak
	if (StreakCount1 < MAX_LAMP_STREAKS)
	{
		NewLamp[StreakCount1++] = LampId;
	}

	return -1;
}

// [D] [T]
void AddSmallStreetLight(CELL_OBJECT *cop, int x, int y, int z, int type)
{
	int count;
	DAMAGED_LAMP* dam;
	int halo_size;
	short size;
	short angle;
	VECTOR v1;
	VECTOR v2;
	VECTOR v3;
	SVECTOR pos;
	CVECTOR col;
	CVECTOR col1;
	SVECTOR dpos;

	dam = damaged_lamp;
	col = {140, 140, 140};
	col1 = {45, 45, 45};

	angle = cop->yang;

	if (type == 0) 
	{
		halo_size = 25;
		size = 150;
	}
	else if (type == 1)
	{
		halo_size = 50;
		size = 150;
	}
	else
	{
		halo_size = 100;
		size = 300;
	}

	count = 0;
	do {
		if (dam->index == cop->pos.vx + cop->pos.vz) 
		{
			if (dam->damage > 2) 
				return;

			// do flicker
			col.r = col.g = col.b = (rand() & 0x3f) - (dam->damage * 32 - 90);
			col1.r = col1.g = col1.b = col.r / 2;

			break;
		}

		count++;
		dam++;
	} while (count < 4);

	dpos.vx = cop->pos.vx - camera_position.vx;
	dpos.vy = cop->pos.vy - camera_position.vy;
	dpos.vz = cop->pos.vz - camera_position.vz;

	if (angle & 0xf)
	{
		pos.vx = x;
		pos.vy = y;
		pos.vz = z;

		gte_SetRotMatrix(&matrixtable[angle]);
		gte_ldv0(&pos);

		gte_rtv0();

		gte_stsv(&pos);

		v1.vx = dpos.vx + pos.vx;
		v1.vy = dpos.vy + pos.vy;
		v1.vz = dpos.vz + pos.vz;
	}
	else
	{
		angle >>= 4;

		if (angle == 0)
		{
			v1.vx = dpos.vx + x;
			v1.vz = dpos.vz + z;
		}
		else if (angle == 1)
		{
			v1.vx = dpos.vx + z;
			v1.vz = dpos.vz - x;
		}
		else if (angle == 2)
		{
			v1.vx = dpos.vx - x;
			v1.vz = dpos.vz - z;
		}
		else if (angle == 3)
		{
			v1.vx = dpos.vx - z;
			v1.vz = dpos.vz + x;
		}

		v1.vy = dpos.vy + y;
	}

	v2.vx = v1.vx;
	v2.vz = v1.vz;
	v2.vy = -camera_position.vy - MapHeight((VECTOR*)&cop->pos);

	LightSortCorrect = -30;
	col.cd = 0;

	v3 = v1;

	LightIndex = find_lamp_streak(cop->pos.vx + cop->pos.vz + x); // [A] was pointer.

	if (LightIndex > -1)
		col.cd = 0x60;
	
	ShowLight(&v3, &col, halo_size, &light_texture);
	ShowLight1(&v3, &col1, size, &light_texture);

	DisplayLightReflections(&v2, &col1, halo_size * 2, &lightref_texture);

	LightSortCorrect = -10;
}

// [D] [T]
void AddLightEffect(CELL_OBJECT *cop, int x, int y, int z, int type, int colour)
{
	short yang;
	int angle;
	int size;
	VECTOR v1;
	VECTOR v2;
	VECTOR dpos;
	SVECTOR pos;
	CVECTOR col;

	yang = cop->yang;
	angle = yang;

	dpos.vx = cop->pos.vx - camera_position.vx;
	dpos.vy = cop->pos.vy - camera_position.vy;
	dpos.vz = cop->pos.vz - camera_position.vz;
	
	if (yang & 0xf) 
	{
		pos.vx = x;
		pos.vy = y;
		pos.vz = z;

		gte_SetRotMatrix(&matrixtable[angle]);
		gte_ldv0(&pos);

		gte_rtv0();

		gte_stsv(&pos);

		v1.vx = dpos.vx + pos.vx;
		v1.vy = dpos.vy + pos.vy;
		v1.vz = dpos.vz + pos.vz;
	}
	else
	{
		yang >>= 4;

		if (yang == 0)
		{
			v1.vx = dpos.vx + x;
			v1.vz = dpos.vz + z;
		}
		else if (yang == 1) 
		{
			v1.vx = dpos.vx + z;
			v1.vz = dpos.vz - x;
		}
		else if (yang == 2)
		{
			v1.vx = dpos.vx - x;
			v1.vz = dpos.vz - z;
		}
		else if (yang == 3)
		{
			v1.vx = dpos.vx - z;
			v1.vz = dpos.vz + x;
		}

		v1.vy = dpos.vy + y;
	}

	if (colour == 0)
	{
		col.r = 40;
		col.g = 10;
		col.b = 0;
	}
	else if (colour == 1) 
	{
		col.r = 0;
		col.g = 40;
		col.b = 0;
	}
	else if (colour == 2)
	{
		col.g = 70;
		col.r = 85;
		col.b = 5;
	}
	else if (colour == 3)
	{
		col.g = 40;
		col.b = 35;
		col.r = 40;
	}

	if (type == 0)
	{
		size = 400;
	}
	else if (type == 1)
	{
		size = 300;
	}
	else if (type == 2)
	{
		size = 200;
	}
	else if (type == 3)
	{
		size = 150;
	}

	v2.vx = v1.vx;
	v2.vz = v1.vz;
	v2.vy = -camera_position.vy - MapHeight((VECTOR*)&cop->pos);

	LightSortCorrect = -10;
	col.cd = 0;

	Apply_Inv_CameraMatrix(&v1);

	gte_SetRotMatrix(&identity);
	gte_SetTransVector(&v1);

	ShowLight1(&v1, &col, size, &light_texture);
	DisplayLightReflections(&v2, &col, (size << 0xd) >> 0x10, &lightref_texture);
}

// [D] [T]
void PreLampStreak(void)
{
	int count;

	for (count = 0; count < MAX_LAMP_STREAKS && StreakCount1 > 0; count++)
	{
		if (Known_Lamps[count].set == 0)
		{
			Known_Lamps[count].id = NewLamp[--StreakCount1];
			Known_Lamps[count].clock = FrameCnt - 2;
		}
	}

	for (count = 0; count < MAX_LAMP_STREAKS; count++)
	{
		Known_Lamps[count].set = 0;
		NewLamp[count] = 0;
	}

	StreakCount1 = 0;
}

// [D] [T]
int damage_lamp(CELL_OBJECT *cop)
{
	int old_damage;
	int count;
	DAMAGED_LAMP *dam;

	count = 0;
	dam = damaged_lamp;

	do {

		// [A] slow but works
		if (dam->index == cop->pos.vx + cop->pos.vz)
		{
			old_damage = dam->damage;
			dam->damage++;

			if (old_damage < 3) 
				return ((old_damage + 1) < 3) ^ 1;

			return 0;
		}

		dam++;
		count++;
	} while (count < MAX_DAMAGED_LAMPS);

	// store new cell object
	dam = &damaged_lamp[NextDamagedLamp];
	dam->damage = 0;
	dam->index = cop->pos.vx + cop->pos.vz; // copy

	if (++NextDamagedLamp > MAX_DAMAGED_LAMPS)
		NextDamagedLamp = 0;

	return 0;
}

// [D] [T]
int damage_object(CELL_OBJECT *cop, VECTOR *velocity)
{
	DAMAGED_OBJECT *dam;
	PACKED_CELL_OBJECT *pcop;

	dam = &damaged_object[NextDamagedPmeter];
	SmashablesHit++;
	
	if (NextDamagedPmeter < MAX_SMASHED_OBJECTS-1)
		NextDamagedPmeter++;
	else
		NextDamagedPmeter = 0;

	dam->active = 1;
	dam->damage = 0;

	dam->cop = *cop; // copy

	dam->vx = cop->pos.vx;

	dam->velocity.vx = velocity->vx >> 10;
	dam->velocity.vz = velocity->vz >> 10;

	pcop = pcoplist[cop->pad];
	pcop->value = 0xffff;
	pcop->pos.vy = 1;

	if (dam->velocity.vx < 0)
		dam->velocity.vy = velocity->vx;
	else 
		dam->velocity.vy = -velocity->vx;

	if (dam->velocity.vz < 0)
		dam->velocity.vy += velocity->vz;
	else
		dam->velocity.vy -= velocity->vz;

	dam->velocity.vy /= 2;

	if ((rand() & 1) == 0)
		dam->rot_speed = -dam->velocity.vy;
	else
		dam->rot_speed = dam->velocity.vy;

	if (dam->velocity.vy < -67)
		dam->velocity.vy = -67;

	cop->pos.vx = OBJECT_SMASHED_MARK;

	return 0;
}

// [D] [T]
void ShowFlare(VECTOR* v1, CVECTOR* col, short size, int rotation)
{
	POLY_FT4* poly;
	SVECTOR vert[4];
	SVECTOR direction;
	MATRIX temp_matrix;
	int z;

	gte_SetTransVector(v1);

	direction.vy = 0;
	direction.vx = 0;
	direction.vz = rotation;

	RotMatrixXYZ(&temp_matrix, &direction);

	gte_SetRotMatrix(&temp_matrix);

	vert[0].vx = -size;
	vert[0].vy = -size;
	vert[0].vz = 0;

	vert[1].vx = -size;
	vert[1].vy = size;
	vert[1].vz = 0;

	vert[2].vx = size;
	vert[2].vy = -size;
	vert[2].vz = 0;

	vert[3].vx = size;
	vert[3].vy = size;
	vert[3].vz = 0;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);

	gte_rtpt();

	poly = (POLY_FT4*)current->primptr;

	poly->u0 = flare_texture.coords.u0;
	poly->v0 = flare_texture.coords.v0;
	poly->u1 = flare_texture.coords.u1;
	poly->v1 = flare_texture.coords.v1;
	poly->u2 = flare_texture.coords.u2;
	poly->v2 = flare_texture.coords.v2 - 1;
	poly->u3 = flare_texture.coords.u3;
	poly->v3 = flare_texture.coords.v3 - 1;

	setPolyFT4(poly);
	setSemiTrans(poly, 1);

	poly->r0 = col->r >> 1;
	poly->g0 = col->g >> 1;
	poly->b0 = col->b >> 1;

	gte_stsz(&z);

	if (z >> 3 > 39)
	{
		z = (z >> 3) + LightSortCorrect;

		if (z < 1)
			z = 1;

		gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);
		gte_ldv0(&vert[3]);

		gte_rtps();
		gte_stsxy(&poly->x3);

		poly->tpage = flare_texture.tpageid | 0x20;
		poly->clut = flare_texture.clutid;

		addPrim(current->ot + z, poly);

		current->primptr += sizeof(POLY_FT4);
	}
}

// [D] [T]
void AddTrafficLight(CELL_OBJECT *cop, int x, int y, int z, int flag, int yang)
{
	int tempfade;
	int lDiffAnglesX, lDiffAnglesY;
	int AbsX, AbsY;
	CVECTOR a, c;
	VECTOR v1, v2;

	v1.vy = (cop->pos.vy - camera_position.vy) + y;
	v1.vx = (cop->pos.vx - camera_position.vx) + FIXEDH(RCOS(yang) * x + RSIN(yang) * z);
	v1.vz = (cop->pos.vz - camera_position.vz) + FIXEDH(RCOS(yang) * z - RSIN(yang) * x);

	a.cd = 0;
	
	if (flag & 0x200) 
	{
		a.r = 255;
		a.g = 25;
		a.b = 25;
	}
	else if (flag & 0x400) 
	{
		a.r = 255;
		a.g = 100;
		a.b = 35;
	}
	else if (flag & 0x800)
	{
		a.r = 25;
		a.g = 255;
		a.b = 25;
	}

	lDiffAnglesX = DIFF_ANGLES(0, camera_angle.vx);// (camera_angle.vx + 2048) & 0xfff) - 2048;
	AbsX = ABS(lDiffAnglesX);

	lDiffAnglesY = DIFF_ANGLES(yang, -camera_angle.vy & 4095); //((-camera_angle.vy & 0xfffU) - yang + 2048 & 0xfff) - 2048;
	AbsY = ABS(lDiffAnglesY);

	c.r = a.r >> 3;
	c.b = a.b >> 3;
	c.g = a.g >> 3;

	v2.vx = v1.vx;
	v2.vz = v1.vz;
	v2.vy = -camera_position.vy - MapHeight((VECTOR*)&cop->pos);

	if (gNight)
		ShowGroundLight(&v2, &c, 300);
	
	if (AbsY < 1000)
	{
		if (AbsX + AbsY < 1000)
		{
			tempfade = 1000 - (AbsX + AbsY);

			if (tempfade < 0)
				tempfade = 0;

			a.r = (a.r * tempfade) >> 10;
			a.b = (a.b * tempfade) >> 10;
			a.g = (a.g * tempfade) >> 10;

			LightSortCorrect = -140;
			LightIndex = find_lamp_streak(cop->pos.vx + cop->pos.vz + x + y); // [A] was pointer.

			if (LightIndex < 0)
				a.cd = 0;
			else
				a.cd = 0x20;

			ShowLight(&v1, &a, 30, &light_texture);

			a.r >>= 1;
			a.b >>= 1;
			a.g >>= 1;

			if (gNight)
				ShowFlare(&v1, &a, 150, lDiffAnglesX + lDiffAnglesY + v1.vx + v1.vz >> 3 & 0x1ffe);

			DisplayLightReflections(&v2, &a, 50, &lightref_texture);
		}

		
		LightSortCorrect = -10;
	}
}

// [D] [T]
void InitFXPos(VECTOR *vec, SVECTOR *svec, CAR_DATA *cp)
{
	SVECTOR svectmp;

	gte_SetRotMatrix(&cp->hd.drawCarMat);
	gte_SetTransVector(vec);

	gte_ldv0(svec);

	gte_rtv0tr();

	gte_stlvnl(vec);
}

int CarLightFadeBack = 0;
int CarLightFadeFront = 0;

// [D] [T]
void FindCarLightFade(MATRIX *carToCamera)
{
	int zz;

	zz = carToCamera->m[2][2] / 4;
	
	CarLightFadeFront = zz - 124;
	CarLightFadeBack = -124 - zz;
}

// [D] [T]
void ShowCarlight(SVECTOR *v1, CAR_DATA *cp, CVECTOR *col, short size, short flare_size, TEXTURE_DETAILS *texture,int flag)
{
	int CarLightFade;
	VECTOR v1t;
	VECTOR v1l;
	CVECTOR flareCol;

	CarLightFade = 255;

	if (flag != 0xFF)
	{
		if (flag & 1)
			CarLightFade = CarLightFadeFront;
		else
			CarLightFade = CarLightFadeBack;

		if (CarLightFade < 0)
			return;
	}
	else
		flag = 0;

	flareCol.r = (col->r * CarLightFade) >> 10;
	flareCol.b = (col->b * CarLightFade) >> 10;
	flareCol.g = (col->g * CarLightFade) >> 10;
	flareCol.cd = flag;

	gte_SetRotMatrix(&cp->hd.drawCarMat);

	v1l.vx = cp->hd.where.t[0] - camera_position.vx;
	v1l.vz = cp->hd.where.t[2] - camera_position.vz;
	v1l.vy = -camera_position.vy - cp->hd.where.t[1];

	InitFXPos(&v1l, v1, cp);

	if (wetness > 9) 
	{
		v1t.vx = v1l.vx;
		v1t.vz = v1l.vz;
		v1t.vy = -camera_position.vy - MapHeight((VECTOR *)cp->hd.where.t);

		DisplayLightReflections(&v1t, &flareCol, flare_size / 2, &lightref_texture);
	}

	col->cd = flag;

	ShowLight(&v1l, &flareCol, size, texture);

	flareCol.r /= 2;
	flareCol.g /= 2;
	flareCol.b /= 2;

	ShowFlare(&v1l, &flareCol, flare_size, (v1->vx + v1->vz) / 4 + (cp->hd.direction - camera_angle.vy) * 2);
}

// [D] [T]
void ShowLight1(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
{
	POLY_FT4* poly;
	SVECTOR vert[4];
	int z;

	poly = (POLY_FT4*)current->primptr;

	vert[0].vz = 0;
	vert[1].vz = 0;
	vert[2].vz = 0;
	vert[3].vz = 0;

	vert[0].vx = -size;
	vert[0].vy = -size;

	vert[1].vx = size;
	vert[1].vy = -size;

	vert[2].vx = -size;
	vert[2].vy = size;

	vert[3].vx = size;
	vert[3].vy = size;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);

	gte_rtpt();

	poly->u0 = (texture->coords).u0;
	poly->v0 = (texture->coords).v0;
	poly->u1 = (texture->coords).u1;
	poly->v1 = (texture->coords).v1;
	poly->u2 = (texture->coords).u2;
	poly->v2 = (texture->coords).v2;
	poly->u3 = (texture->coords).u3;
	poly->v3 = (texture->coords).v3;;

	setPolyFT4(poly);
	SetSemiTrans(poly, 1);

	poly->r0 = col->r;
	poly->g0 = col->g;
	poly->b0 = col->b;

	gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

	gte_stsz(&z);

	if (z > 39)
	{
		gte_ldv0(&vert[3]);

		gte_rtps();

		poly->tpage = texture->tpageid | 0x20;
		poly->clut = texture->clutid;

		z = (z >> 3) + LightSortCorrect;

		if (z < 0) 
			z = 0;

		gte_stsxy(&poly->x3);

		addPrim(current->ot + z, poly);

		current->primptr += sizeof(POLY_FT4);
	}
}

// [D] [T]
void ShowLight(VECTOR *v1, CVECTOR *col, short size, TEXTURE_DETAILS *texture)
{
	int x, y;
	POLY_FT3 *null;
	POLY_G4 *trail;
	POLY_FT4 *poly;
	int i;
	SVECTOR vert[4];
	int z;
	int index;
	SXYPAIR* trails;
	short* clock;
	int tail_width;

	// apply transform
	Apply_Inv_CameraMatrix(v1);

	gte_SetRotMatrix(&identity);
	gte_SetTransVector(v1);

	vert[0].vz = 0;
	vert[1].vz = 0;
	vert[2].vz = 0;
	vert[3].vz = 0;

	vert[0].vx = -size;
	vert[0].vy = -size;

	vert[1].vx = size;
	vert[1].vy = -size;

	vert[2].vx = -size;
	vert[2].vy = size;

	vert[3].vx = size;
	vert[3].vy = size;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);
	gte_rtpt();

	gte_stsz(&z);

	// discard if too close
	if (z < 38)
		return;

	poly = (POLY_FT4 *)current->primptr;
	setPolyFT4(poly);
	setSemiTrans(poly, 1);

	poly->u0 = texture->coords.u0;
	poly->v0 = texture->coords.v0;
	poly->u1 = texture->coords.u1;
	poly->v1 = texture->coords.v1;
	poly->u2 = texture->coords.u2;
	poly->v2 = texture->coords.v2;
	poly->u3 = texture->coords.u3;
	poly->v3 = texture->coords.v3;

	poly->r0 = col->r;
	poly->g0 = col->g;
	poly->b0 = col->b;

	gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

	gte_ldv0(&vert[3]);
	gte_rtps();

	gte_stsxy(&poly->x3);

	poly->tpage = texture->tpageid | 0x20;
	poly->clut = texture->clutid;

	z = (z >> 3) + LightSortCorrect;

	if (z < 0)
		z = 0;

	if (z < 10000)
		tail_width = (10000 - z) >> 0xd;
	else
		tail_width = 0;

	addPrim(current->ot + z, poly);
	current->primptr += sizeof(POLY_FT4);

	
	if (CameraCnt <= 4 || NumPlayers > 1)	// [A] don't draw trails in multiplayer
		return;

	if ((col->cd & 0x20) && gLightsOn) 
	{
		trails = Known_Lamps[LightIndex].light_trails;
		clock = &Known_Lamps[LightIndex].clock;
	}
	else 
	{
		// get light index by flag
		if (col->cd & 0x2) 
			index = 0;
		else if (col->cd & 0x4) 
			index = 1;
		else if (col->cd & 0x8) 
			index = 2;
		else if (col->cd & 0x10) 
			index = 3;
		else
			index = -1;

		if (index == -1)
			return;
		
		trails = car_data[gcar_num].ap.light_trails[index];
		clock = &car_data[gcar_num].ap.old_clock[index];
	}

#ifndef PSX
	x = (poly->x0 + poly->x3) / 2.0f;
	y = (poly->y0 + poly->y3) / 2.0f;
#else
	x = (poly->x0 + poly->x3) / 2;
	y = (poly->y0 + poly->y3) / 2;
#endif
	
	// unified drawing both for car and lamps
	if (CameraChanged == 0 && *clock == (FrameCnt & 0xffffU)-1)
	{
		int old_x, old_y;

		old_x = trails[FrameCnt - 3U & 3].x;
		old_y = trails[FrameCnt - 3U & 3].y;

		trails[FrameCnt & 3].x = x;
		trails[FrameCnt & 3].y = y;

		if (size > 1 && ABS(old_x - x) + ABS(old_y - y) > 1)
		{
			int angle, width;
			VERTTYPE dx, dy;

			trail = (POLY_G4 *)current->primptr;

			setPolyG4(trail);
			setSemiTrans(trail, 1);

			angle = -ratan2(old_x - x,old_y - y) & 0xfff;
			width = ABS(poly->x0 - poly->x3);

#ifdef PSX
			dx = RCOS(angle) * width * 3;
			dy = RSIN(angle) * width * 3;
			
			if (col->cd & 0x40)
			{
				dx >>= 0x10;
				dy >>= 0x10;
			}
			else
			{
				dx >>= 0xf;
				dy >>= 0xf;
			}
			
			trail->x0 = x + dx * tail_width;
			trail->y0 = y + dy * tail_width;

			trail->x1 = x - dx * tail_width;
			trail->y1 = y - dy * tail_width;

			trail->x2 = old_x + dx;
			trail->y2 = old_y + dy;

			trail->x3 = old_x - dx;
			trail->y3 = old_y - dy;
#else
			// [A] slightly bigger light trail
			dx = RCOS(angle);
			dy = RSIN(angle);
			
			if (col->cd & 0x40)
			{
				dx = dx / 40000.0f;
				dy = dy / 40000.0f;
			}
			else
			{
				dx = dx / 32768.0f;
				dy = dy / 32768.0f;
			}

			trail->x0 = x + dx * width * 3 * tail_width;
			trail->y0 = y + dy * width * 3 * tail_width;

			trail->x1 = x - dx * width * 3 * tail_width;
			trail->y1 = y - dy * width * 3 * tail_width;

			trail->x2 = old_x + dx * width * 3;
			trail->y2 = old_y + dy * width * 3;

			trail->x3 = old_x - dx * width * 3;
			trail->y3 = old_y - dy * width * 3;
#endif

			if (col->cd & 0x18) 
			{
				trail->r0 = trail->r1 = col->r / 2;
				trail->g0 = trail->g1 = col->g / 2;
				trail->b0 = trail->b1 = col->b / 2;
			}
			else 
			{
				trail->r0 = trail->r1 = col->r;
				trail->g0 = trail->g1 = col->g;
				trail->b0 = trail->b1 = col->b;
			}
			
			trail->r2 = trail->r3 = 0;
			trail->g2 = trail->g3 = 0;
			trail->b2 = trail->b3 = 0;

			addPrim(current->ot + z, trail);
			current->primptr += sizeof(POLY_G4);

			null = (POLY_FT3 *)current->primptr;

			setPolyFT3(null);
			null->x0 = -1;
			null->y0 = -1;
			null->x1 = -1;
			null->y1 = -1;
			null->x2 = -1;
			null->y2 = -1;
			null->tpage = 0x20;

			addPrim(current->ot + z, null);
			current->primptr += sizeof(POLY_FT3);
	}
		}
	else
	{
		for (i = 0; i < 4; i++)
		{
			trails[i].x = x;
			trails[i].y = y;
		}
	}

	*clock = FrameCnt;
}

// [D] [T]
void ShowGroundLight(VECTOR *v1, CVECTOR *col, short size)
{
	POLY_FT4 *poly;
	SVECTOR vert[4];
	int z;

#ifndef PSX
	// [A] limit drawing distance to avoid artifacts
	if (ABS(v1->vx) > 32000 || ABS(v1->vz) > 32000)
		return;
#endif

	gte_SetRotMatrix(&inv_camera_matrix);
	gte_SetTransVector(&dummy);

	// [A] might be incorrect
	vert[0].vx = v1->vx - size;
	vert[0].vy = v1->vy;
	vert[0].vz = v1->vz + size;

	vert[1].vx = v1->vx + size;
	vert[1].vy = v1->vy;
	vert[1].vz = v1->vz + size;

	vert[2].vx = v1->vx - size;
	vert[2].vy = v1->vy;
	vert[2].vz = v1->vz - size;

	vert[3].vx = v1->vx + size;
	vert[3].vy = v1->vy;
	vert[3].vz = v1->vz - size;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);

	gte_rtpt();

	gte_stsz(&z);

	if (z - 150 < 9851) 
	{
		poly = (POLY_FT4 *)current->primptr;

		setPolyFT4(poly);
		setSemiTrans(poly, 1);

		*(ushort*)&poly->u0 = *(ushort*)&light_texture.coords.u0;
		*(ushort*)&poly->u1 = *(ushort*)&light_texture.coords.u1;
		*(ushort*)&poly->u2 = *(ushort*)&light_texture.coords.u2;
		*(ushort*)&poly->u3 = *(ushort*)&light_texture.coords.u3;

		poly->tpage = light_texture.tpageid | 0x20;
		poly->clut = light_texture.clutid;;

		poly->r0 = col->r;
		poly->g0 = col->g;
		poly->b0 = col->b;

		gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

		gte_ldv0(&vert[3]);

		gte_rtps();
		gte_stsxy(&poly->x3);

		addPrim(current->ot + (z >> 3), poly);
		current->primptr += sizeof(POLY_FT4);
	}
}

// [D] [T]
void RoundShadow(VECTOR *v1, CVECTOR *col, short size)
{
	POLY_FT4* poly;
	SVECTOR vert[4];
	int z;

	gte_SetRotMatrix(&inv_camera_matrix);
	gte_SetTransVector(&dummy);

	vert[0].vz = 0;
	vert[1].vz = 0;
	vert[2].vz = 0;
	vert[3].vz = 0;

	vert[0].vx = v1->vx - size;
	vert[0].vy = v1->vy;
	vert[0].vz = size + v1->vz;

	vert[1].vx = size + v1->vx;
	vert[1].vy = v1->vy;
	vert[1].vz = size + v1->vz;

	vert[2].vx = v1->vx - size;
	vert[2].vy = v1->vy;
	vert[2].vz = v1->vz - size;

	vert[3].vx = size + v1->vx;
	vert[3].vy = v1->vy;
	vert[3].vz = v1->vz - size;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);

	gte_rtpt();

	poly = (POLY_FT4*)current->primptr;

	poly->u0 = light_texture.coords.u0;
	poly->v0 = light_texture.coords.v0;
	poly->u1 = light_texture.coords.u1;
	poly->v1 = light_texture.coords.v1;
	poly->u2 = light_texture.coords.u2;
	poly->v2 = light_texture.coords.v2;
	poly->u3 = light_texture.coords.u3;
	poly->v3 = light_texture.coords.v3;

	poly->tpage = light_texture.tpageid | 0x40;
	poly->clut = light_texture.clutid;

	setPolyFT4(poly);
	setSemiTrans(poly, 1);

	poly->r0 = col->r;
	poly->g0 = col->g;
	poly->b0 = col->b;

	gte_stsz(&z);

	if (z - 150 < 9851)
	{
		gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

		gte_ldv0(&vert[3]);

		gte_rtps();

		gte_stsxy(&poly->x3);

		addPrim(current->ot + (z >> 3), poly);
		current->primptr += sizeof(POLY_FT4);
	}
}

// [D] [T weird and unused]
void DisplayWater(SMOKE *smoke)
{
	POLY_FT4* poly;
	VECTOR v;
	SVECTOR vert[4];
	
	int size;
	int z;
	int z1;
	int z2;
	int z3;
	int z4;

	gte_SetRotMatrix(&inv_camera_matrix);
	gte_SetTransVector(&dummy);

	size = smoke->start_w;

	vert[0].vx = (smoke->position.vx - size) - camera_position.vx;
	vert[0].vy = 50 - camera_position.vy;
	vert[0].vz = (smoke->position.vz + size) - camera_position.vz;

	vert[1].vx = (smoke->position.vx + size) - camera_position.vx;
	vert[1].vy = 50 - camera_position.vy;
	vert[1].vz = (smoke->position.vz + size) - camera_position.vz;

	vert[2].vx = (smoke->position.vx - size) - camera_position.vx;
	vert[2].vy = 50 - camera_position.vy;
	vert[2].vz = (smoke->position.vz - size) - camera_position.vz;

	vert[3].vx = (smoke->position.vx + size) - camera_position.vx;
	vert[3].vy = 50 - camera_position.vy;
	vert[3].vz = (smoke->position.vz - size) - camera_position.vz;

	poly = (POLY_FT4*)current->primptr;

	gte_ldv3(&vert[0], &vert[1], &vert[2]);

	gte_rtpt();

	setPolyFT4(poly);

	poly->r0 = NightAmbient;
	poly->g0 = NightAmbient;
	poly->b0 = NightAmbient;
	poly->tpage = sea_texture.tpageid;

	gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

	gte_stsz3(&z1, &z2, &z3);

	gte_ldv0(&vert[3]);

	gte_rtps();

	gte_stsxy(&poly->x3);

	poly->u0 = sea_texture.coords.u0;
	poly->v0 = sea_texture.coords.v0;
	poly->u1 = sea_texture.coords.u1;
	poly->v1 = sea_texture.coords.v1;
	poly->u2 = sea_texture.coords.u2;
	poly->v2 = sea_texture.coords.v2;
	poly->u3 = sea_texture.coords.u3;
	poly->v3 = sea_texture.coords.v3;
	poly->clut = sea_texture.clutid;

	gte_stsz(&z4);

	z = z1 + z2 + z3 + z4 >> 5;

	if (z < 9)
		z = 9;

	addPrim(current->ot + z, poly);

	current->primptr += sizeof(POLY_FT4);
}

// [D] [T]
void DisplaySpark(SMOKE *spark)
{
	int colorind;
	POLY_G3 *poly;
	SVECTOR v[3];
	SVECTOR TrailPos;
	int z;

	TrailPos.vx = spark->position.vx - camera_position.vx;
	TrailPos.vy = spark->position.vy - camera_position.vy;
	TrailPos.vz = spark->position.vz - camera_position.vz;

	gte_SetRotMatrix(&inv_camera_matrix);
	gte_SetTransVector(&dummy);

	v[0].vx = TrailPos.vx;
	v[0].vy = TrailPos.vy;
	v[0].vz = TrailPos.vz;

	v[1].vx = TrailPos.vx + 5;
	v[1].vy = TrailPos.vy;
	v[1].vz = TrailPos.vz + 5;

	v[2].vx = spark->final_tail_pos.vx - camera_position.vx;
	v[2].vy = spark->final_tail_pos.vy - camera_position.vy;
	v[2].vz = spark->final_tail_pos.vz - camera_position.vz;

	poly = (POLY_G3 *)current->primptr;

	gte_ldv3(&v[0], &v[1], &v[2]);
	gte_rtpt();

	setPolyG3(poly);

	if (spark->start_w == 2) 
	{
		colorind = spark->drift.vy * 3 & 3;

		if (gTimeOfDay == 1)
		{
			poly->r0 = grassColour[colorind][0];
			poly->g0 = grassColour[colorind][1];
			poly->b0 = grassColour[colorind][2];

			poly->r1 = grassColour[colorind][0];
			poly->g1 = grassColour[colorind][1];
			poly->b1 = grassColour[colorind][2];

			colorind = (spark->drift.vy + 2U) * 3 & 3;
			poly->r2 = grassColour[colorind][0];
			poly->g2 = grassColour[colorind][1];
			poly->b2 = grassColour[colorind][2];
		}
		else 
		{
			poly->r0 = 0;
			poly->g0 = (spark->transparency >> 3);
			poly->r1 = 0;
			poly->b0 = (spark->transparency >> 4);
			poly->g1 = (spark->transparency >> 3);
			poly->r2 = 12;
			poly->g2 = 4;
			poly->b2 = 4;
			poly->b1 = (spark->transparency >> 4);
		}
	}
	else
	{
		poly->r0 = spark->transparency;
		poly->g0 = spark->transparency;
		poly->b0 = spark->transparency / 2;
		poly->r1 = spark->transparency;
		poly->g1 = spark->transparency;
		poly->b1 = spark->transparency / 2;
		poly->r2 = spark->transparency;
		poly->g2 = 0;
		poly->b2 = 0;
	}

	gte_stsz(&z);

	if (z > 50)
	{
		if (z >> 3 < 9)
			z = 72;

		gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);
		addPrim(current->ot + (z >> 3), poly);

		current->primptr += sizeof(POLY_G3);
	}
}

int SmokeCnt = 0;

// [D] [T]
void GetSmokeDrift(VECTOR *Wind)
{
	static int SinTabIndex1 = 10;
	static int SinTabIndex2 = 0;
	static int SinTabIndex3 = 5;
	static int SinX = 0;
	static int CosX = 0;
	static int WindMagnitude = 0;

	if (SmokeCnt != CameraCnt)
	{
		SmokeCnt = CameraCnt;

		SinTabIndex1 = SinTabIndex1 + 3 & 0xfff;
		SinTabIndex2 = SinTabIndex2 + 5 & 0xfff;
		SinTabIndex3 = SinTabIndex3 + 7 & 0xfff;

		SinX = isin(SinTabIndex1) + isin(SinTabIndex2);
		CosX = icos(SinTabIndex1) + icos(SinTabIndex2);
		WindMagnitude = isin(SinTabIndex3) >> 0xb;
	}

	Wind->vy = 0;
	Wind->vx = FIXEDH(WindMagnitude * CosX);
	Wind->vz = FIXEDH(-WindMagnitude * SinX);
}

// [D] [T]
void Setup_Debris(VECTOR *ipos, VECTOR *ispeed, int num_debris, int type)
{
	int num;
	int seed;
	int col;
	int i;
	DEBRIS *mydebris;

	col = type >> 0x10;

	for (i = 0; i < num_debris; i++)
	{
		num = AllocateDebris();
		if (num == -1)
			return;

		mydebris = &debris[num];

		mydebris->position.vx = ipos->vx;
		mydebris->position.vy = ipos->vy;
		mydebris->position.vz = ipos->vz;

		if ((type & 0xffffU) < 8)
		{
			seed = rand();
			
			mydebris->step = seed;
			mydebris->rgb.r = debris_colour[GameLevel][col].r;
			mydebris->rgb.g = debris_colour[GameLevel][col].g;
			mydebris->rgb.b = debris_colour[GameLevel][col].b;

			if (gTimeOfDay == 3) 
			{
				mydebris->rgb.r >>= 1;
				mydebris->rgb.g >>= 1;
				mydebris->rgb.b >>= 1;
			}
			
			mydebris->direction.vx = FIXED((ispeed->vx + ((seed & 0x7f) - 64) * num_debris) * 1024);
			mydebris->direction.vy = -FIXED((seed & 0x7f00) * num_debris * 2);
			mydebris->direction.vz = FIXED(ispeed->vz + ((rand() & 0x7fff) - 16384) * num_debris * 2);
		}
		else 
		{
			mydebris->step = 2;
			
			mydebris->rgb.b = 60;
			mydebris->rgb.g = 60;
			mydebris->rgb.r = 60;

			mydebris->direction.vx = FIXED(ispeed->vx);
			mydebris->direction.vy = -FIXED(ispeed->vy);
			mydebris->direction.vz = FIXED(ispeed->vz);
		}

		mydebris->type = type & 7;

		mydebris->life = 128;
		mydebris->flags = 0x2;
		mydebris->pos = i & 0x1f;
	}
}

// [D] [T]
void Setup_Smoke(VECTOR *ipos, int start_w, int end_w, int SmokeType, int WheelSpeed, VECTOR *Drift, int Exhaust)
{
	int num;
	SMOKE *mysmoke;

	num = AllocateSmoke();
	if (num == -1)
		return;

	mysmoke = &smoke[num];

	if (SmokeType == SMOKE_FIRE) 
	{
		mysmoke->position.vx = ipos->vx;
		mysmoke->position.vy = ipos->vy;
		mysmoke->start_w = start_w;
		mysmoke->final_w = end_w;
		mysmoke->position.vz = ipos->vz;
		mysmoke->step = end_w - start_w >> 7 << 2;
		mysmoke->flags = 0x1000 | 0x4 | 0x2;
		mysmoke->life = 20;
		mysmoke->halflife = 10;

		if (WheelSpeed > 200000)
			mysmoke->transparency = (800000 - WheelSpeed >> 0xb);
		else
			mysmoke->transparency = 0xff;

		mysmoke->t_step = end_w - start_w >> 2;
	}
	else if (SmokeType == SMOKE_BLACK)
	{
		if (Exhaust)
		{
			mysmoke->position.vx = ipos->vx + (rand() & 9);
			mysmoke->position.vy = ipos->vy;
			mysmoke->position.vz = ipos->vz + (rand() & 9);
			
			mysmoke->flags = 0x2000 | 0x4 | 0x2;
			mysmoke->transparency = 140;
			mysmoke->t_step = 5;
			mysmoke->step = 1;
		}
		else
		{
			mysmoke->position.vx = ipos->vx;
			mysmoke->position.vy = ipos->vy;
			mysmoke->position.vz = ipos->vz;

			if (WheelSpeed > 200000)
				mysmoke->transparency = (800000 - WheelSpeed >> 0xb);
			else
				mysmoke->transparency = 255;
			
			mysmoke->t_step = end_w - start_w >> 5;
			mysmoke->step = (end_w - start_w) / 128 * 4;
		}

		mysmoke->start_w = start_w;
		mysmoke->final_w = end_w;
		
		mysmoke->flags = 0x2000 | 0x4 | 0x2;
		mysmoke->life = 40;
		mysmoke->halflife = 20;
	}
	else if (SmokeType == SMOKE_WHITE || SmokeType == 5)
	{
		if (Exhaust)
		{
			mysmoke->position.vx = ipos->vx + (rand() & 7);
			mysmoke->position.vy = ipos->vy;
			mysmoke->position.vz = ipos->vz + (rand() & 7);

			mysmoke->flags = 0x4000 | 0x4 | 0x2;
			mysmoke->transparency = 55;
			mysmoke->t_step = 2;
			mysmoke->step = 1;
			mysmoke->life = 24;
		}
		else
		{
			mysmoke->position.vx = ipos->vx + (rand() & 0x3f);
			mysmoke->position.vy = ipos->vy;
			mysmoke->position.vz = ipos->vz + (rand() & 0x3f);
			mysmoke->flags = 0x10 | 0x4 | 0x2;

			if (SmokeType == 5) // UNUSED
			{
				mysmoke->life = 32;
				mysmoke->step = end_w >> 5 << 2;
				mysmoke->t_step = end_w - start_w >> 5;
				mysmoke->transparency = WheelSpeed;
			}
			else
			{
				mysmoke->life = 128;
				mysmoke->step = (end_w - start_w) / 128 * 8;
				mysmoke->t_step = (end_w - start_w >> 6);
				mysmoke->transparency = 80;
			}
		}

		mysmoke->start_w = start_w;
		mysmoke->final_w = end_w;
		mysmoke->halflife = 64;
	}
	else if (SmokeType == 6) // UNUSED
	{
		mysmoke->position.vx = ipos->vx;
		mysmoke->position.vy = ipos->vy;
		mysmoke->position.vz = ipos->vz;
		mysmoke->flags = 0x40 | 0x4 | 0x2;
		mysmoke->transparency = 160;
		mysmoke->step = 20;
		mysmoke->t_step = 5;
		mysmoke->start_w = start_w;
		mysmoke->final_w = end_w;
		mysmoke->life = 78;
		mysmoke->halflife = 32;
	}
	else
	{
		mysmoke->position.vx = ipos->vx + (rand() & 0x3f);
		mysmoke->position.vy = ipos->vy;
		mysmoke->flags = 0x20 | 0x4 | 0x2;
		mysmoke->transparency = 60;
		mysmoke->t_step = 5;
		mysmoke->start_w = start_w;
		mysmoke->final_w = end_w;
		mysmoke->position.vz = ipos->vz + (rand() & 0x3f);

		mysmoke->step = (end_w / 128) * 8;
		mysmoke->life = 128;
		mysmoke->halflife = 64;
	}

	if (Exhaust != 0) 
	{
		mysmoke->drift.vx = Drift->vx;
		mysmoke->drift.vy = Drift->vy;
		mysmoke->drift.vz = Drift->vz;

		mysmoke->drift_change.vx = 0;
		mysmoke->drift_change.vy = 0;
		mysmoke->drift_change.vz = 0;
	}
	else
	{
		mysmoke->drift.vx = 0;
		mysmoke->drift.vy = 0;
		mysmoke->drift.vz = 0;

		if (mysmoke->life < 40 || SmokeType == 4)
		{
			mysmoke->drift_change.vx = 0;
			mysmoke->drift_change.vy = 1;
			mysmoke->drift_change.vz = 0;
		}
		else 
		{
			mysmoke->drift_change.vx = Drift->vx;
			mysmoke->drift_change.vy = Drift->vy;
			mysmoke->drift_change.vz = Drift->vz;
		}
	}
}

// [D] [T]
void Setup_Sparks(VECTOR *ipos, VECTOR *ispeed, int num_sparks, char SparkType)
{
	int num;
	int seed;
	int vz;
	int vy;
	int vx;
	int i;

	SMOKE *mysmoke;

	if (pauseflag != 0 || 0 >= num_sparks)
		return;

	for (i = 0; i < num_sparks; i++)
	{
		num = AllocateSmoke();
	
		if (num == -1)
			return;

		mysmoke = &smoke[num];

		seed = rand();

		mysmoke->position.vx = mysmoke->final_tail_pos.vx = ipos->vx;
		mysmoke->position.vy = mysmoke->final_tail_pos.vy = ipos->vy;
		mysmoke->position.vz = mysmoke->final_tail_pos.vz = ipos->vz;

		mysmoke->pos = (i << 2);
		mysmoke->step = seed & 0x3f;
		
		mysmoke->drift.vx = 2;
		mysmoke->flags = 0x8 | 0x2;

		vx = ispeed->vx;
		vy = ispeed->vy;
		vz = ispeed->vz;

		mysmoke->drift_change.vx = FIXED((vx - 16384 + (seed & 0x7f00)) * 4);
		mysmoke->drift_change.vy = -FIXED((vy + (seed & 0x7f)) * 1024);
		mysmoke->drift_change.vz = FIXED(vz - 64 + (rand() & 0x7f) >> 2);

		if (SparkType == 1)
		{
			mysmoke->life = 10;
			mysmoke->transparency = 200;
			mysmoke->t_step = 2;
			mysmoke->start_w = 0;
		}
		else if (SparkType == 2)
		{
			mysmoke->life = 10;
			mysmoke->transparency = 0xff;
			mysmoke->t_step = 10;
			mysmoke->start_w = 2;
			mysmoke->drift.vy = rand() & 3;
		}
		else
		{
			mysmoke->life = 40;
			mysmoke->transparency = 0xff;
			mysmoke->t_step = 10;
			mysmoke->start_w = 1;
		}
	}
}

// [D] [T]
void DisplayDebris(DEBRIS *debris, char type)
{
	int uVar3;
	TRI_POINT* tv;
	POLY_GT4 *poly1;
	POLY_FT3 *poly;
	SVECTOR v[4];
	VECTOR debrisvec;
	int z;

	debrisvec.vx = debris->position.vx - camera_position.vx;
	debrisvec.vy = debris->position.vy - camera_position.vy;
	debrisvec.vz = debris->position.vz - camera_position.vz;

	if (debrisvec.vx >= -10000 && 
		debrisvec.vz >= -10000 && 
		debrisvec.vx <= 10000 &&
		debrisvec.vz <= 10000)
	{
		tv = debris_rot_table[debris->type & 7] + (debris->pos >> 3 & 0x1fU);

		v[0].vx = tv->v0.vx + debrisvec.vx;
		v[0].vy = tv->v0.vy + debrisvec.vy;
		v[0].vz = tv->v0.vz + debrisvec.vz;

		v[1].vx = tv->v1.vx + debrisvec.vx;
		v[1].vy = tv->v1.vy + debrisvec.vy;
		v[1].vz = tv->v1.vz + debrisvec.vz;

		v[2].vx = tv->v2.vx + debrisvec.vx;
		v[2].vy = tv->v2.vy + debrisvec.vy;
		v[2].vz = tv->v2.vz + debrisvec.vz;

		gte_ldv3(&v[0], &v[1], &v[2]);
		gte_rtpt();

		if (type - 1U < 2) 
		{
			poly1 = (POLY_GT4 *)current->primptr;

			gte_stsxy3(&poly1->x0, &poly1->x1, &poly1->x2);
			gte_stsz(&z);

			if (z > 223)
			{
				*(ushort*)&poly1->u0 = *(ushort*)&litter_texture.coords.u0;
				*(ushort*)&poly1->u1 = *(ushort*)&litter_texture.coords.u1;
				*(ushort*)&poly1->u2 = *(ushort*)&litter_texture.coords.u2;
				*(ushort*)&poly1->u3 = *(ushort*)&litter_texture.coords.u3;

				poly1->clut = litter_texture.clutid;
				poly1->tpage = litter_texture.tpageid;

				v[3].vx = debrisvec.vx - tv->v0.vx;
				v[3].vy = debrisvec.vy - tv->v0.vy;
				v[3].vz = debrisvec.vz - tv->v0.vz;

				gte_ldv0(&v[3]);
				gte_rtps();

				gte_stsxy(&poly1->x3);

				if (type == 2) 
					uVar3 = (debris->rgb.b + combointensity) * 0x10000 | (debris->rgb.g + combointensity) * 0x100 | 0x3c000000 | debris->rgb.r + combointensity;
				else
					uVar3 = debris->rgb.b << 0x10 | debris->rgb.g << 8 | 0x3c000000 | debris->rgb.r;

				*(u_int *)&poly1->r0 = uVar3;
				*(u_int *)&poly1->r2 = uVar3;
				*(u_int *)&poly1->r1 = uVar3 + 0x202020;
				*(u_int *)&poly1->r3 = uVar3 + 0x303030;

				setPolyGT4(poly1);
				addPrim(current->ot + (z >> 3), poly1);
				current->primptr += sizeof(POLY_GT4);
			}
		}
		else
		{
			poly = (POLY_FT3 *)current->primptr;

			gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);
			gte_stsz(&z);

			if (z > 223)
			{
				*(ushort*)&poly->u0 = *(ushort*)&debris_texture.coords.u0;
				*(ushort*)&poly->u1 = *(ushort*)&debris_texture.coords.u1;
				*(ushort*)&poly->u2 = *(ushort*)&debris_texture.coords.u2;

				poly->r0 = debris->rgb.r;
				poly->g0 = debris->rgb.g;
				poly->b0 = debris->rgb.b;

				poly->tpage = debris_texture.tpageid;
				poly->clut = debris_texture.clutid;

				setPolyFT3(poly);
				addPrim(current->ot + (z >> 3), poly);
				current->primptr += sizeof(POLY_FT3);
			}
		}
	}
	else
	{
		debris->life = 1;
	}
}

// [D] [T]
void DisplaySmoke(SMOKE* smoke)
{
	int Z;
	int tmode;
	POLY_FT4* poly;
	VECTOR v;
	SVECTOR smokemesh[4];
	int smoke_z_offset;

	smoke_z_offset = 0;

	v.vx = smoke->position.vx - camera_position.vx;
	v.vy = smoke->position.vy - camera_position.vy;
	v.vz = smoke->position.vz - camera_position.vz;

	if (ABS(v.vx) > 20480 || ABS(v.vz) > 20480)
		return;

	Apply_Inv_CameraMatrix(&v);

	gte_SetTransVector(&v);
	gte_SetRotMatrix(&identity);

	smokemesh[0].vx = -smoke->start_w;
	smokemesh[0].vy = -smoke->start_w;
	smokemesh[0].vz = 0;

	smokemesh[1].vx = smoke->start_w;
	smokemesh[1].vy = -smoke->start_w;
	smokemesh[1].vz = 0;

	smokemesh[2].vx = -smoke->start_w;
	smokemesh[2].vy = smoke->start_w;
	smokemesh[2].vz = 0;

	smokemesh[3].vx = smoke->start_w;
	smokemesh[3].vy = smoke->start_w;
	smokemesh[3].vz = 0;

	poly = (POLY_FT4*)current->primptr;

	gte_ldv3(&smokemesh[0], &smokemesh[1], &smokemesh[2]);
	gte_rtpt();

	tmode = 0x20;

	if (smoke->flags & 0x4000)
	{
		poly->r0 = smoke->transparency;
		poly->g0 = smoke->transparency;
		poly->b0 = smoke->transparency;
	}
	else if (smoke->flags & 0x2000)
	{
		if (gNight)
		{
			poly->r0 = smoke->transparency / 2;
			poly->g0 = smoke->transparency / 2;
			poly->b0 = smoke->transparency / 2;
		}
		else
		{
			poly->r0 = smoke->transparency >> 2;
			poly->g0 = smoke->transparency >> 2;
			poly->b0 = smoke->transparency >> 2;
		}

		tmode = 0x40;
		smoke_z_offset = 17;
	}
	else if (smoke->flags & 0x1000)
	{
		if ((smoke->transparency >> 3) + 50 & 0xff < 60)
			poly->g0 = (smoke->transparency >> 3);
		else
			poly->g0 = smoke->transparency >> 2;

		smoke_z_offset = 18;

		poly->r0 = smoke->transparency;
		poly->b0 = smoke->transparency >> 3;
	}
	else if (smoke->flags & 0x20)
	{
		poly->r0 = smoke->transparency >> 1;
		poly->g0 = smoke->transparency;
		poly->b0 = smoke->transparency + 10;

		tmode = 0x40;
	}
	else if (smoke->flags & 0x40)
	{
		if (gNight == 0)
		{
			poly->r0 = smoke->transparency >> 2;
			poly->g0 = smoke->transparency >> 2;
			poly->b0 = smoke->transparency >> 2;
		}
		else
		{
			poly->r0 = smoke->transparency >> 3;
			poly->g0 = smoke->transparency >> 3;
			poly->b0 = smoke->transparency + 10 >> 3;
		}
	}
	else
	{
		if (gNight == 0)
		{
			poly->r0 = smoke->transparency;
			poly->g0 = smoke->transparency;
			poly->b0 = smoke->transparency + 10;
		}
		else
		{
			poly->r0 = smoke->transparency / 2;
			poly->g0 = smoke->transparency / 2;
			poly->b0 = smoke->transparency + 10 >> 1;
		}

		smoke_z_offset = 25;
	}

	gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);

	gte_stsz(&Z);

	gte_ldv0(&smokemesh[3]);
	gte_rtps();

	poly->u0 = smoke_texture.coords.u0;
	poly->v0 = smoke_texture.coords.v0;
	poly->u1 = smoke_texture.coords.u1;
	poly->v1 = smoke_texture.coords.v1;
	poly->u2 = smoke_texture.coords.u2;
	poly->v2 = smoke_texture.coords.v2 - 4; // [A] ???
	poly->u3 = smoke_texture.coords.u3;
	poly->v3 = smoke_texture.coords.v3 - 4; // [A] ???

	poly->tpage = smoke_texture.tpageid | tmode;
	poly->clut = smoke_texture.clutid;

	setPolyFT4(poly);
	setSemiTrans(poly, 1);

	if (Z >> 3 > 0)
	{
		int x, y;

		smoke_z_offset = (Z >> 3) - smoke_z_offset;

		if (smoke_z_offset < 9)
			smoke_z_offset = 9;

		gte_stsxy(&poly->x3);
		addPrim(current->ot + smoke_z_offset, poly);

		// reduce size?
		if ((poly->x1 - poly->x0) / 2 > 100)
		{
			x = (poly->x0 + poly->x3) / 2;
			y = (poly->y0 + poly->y3) / 2;

			// FIXME:  * 0x10000 is needed because clipping issue
			poly->x0 = (y - 50) << 0x10;
			poly->y0 = x - 50;

			poly->x1 = (y - 50) << 0x10;
			poly->y1 = x + 50;

			poly->x2 = (y + 50) << 0x10;
			poly->y2 = x - 50;

			poly->x3 = (y + 50) << 0x10;
			poly->y3 = x + 50;
		}

		current->primptr += sizeof(POLY_FT4);
	}
}

int NoRainIndoors = 0;

// [D] [T]
void HandleDebris(void)
{
	int Height;
	SMOKE *sm;
	DEBRIS *db;
	LEAF *lf;
	int i;
	VECTOR Drift;
	
	GetSmokeDrift(&Drift);

	MoveHubcap();

	SetRotMatrix(&inv_camera_matrix);

	gte_SetTransVector(&dummy);

	db = debris;
	i = next_debris;

	while (i > 0)
	{
		if (db->flags & 0x2)
		{
			DisplayDebris(db, 0);
			
			if (pauseflag == 0)
			{
				if (--db->life == 0 || db->position.vy > 0) 
				{
					db->flags = 0;
					ReleaseDebris(db->num);
				}
				else
				{
					db->position.vx += db->direction.vx;
					db->position.vy += db->direction.vy;
					db->position.vz += db->direction.vz;
					db->pos += db->step;

					if (db->type == 3) 
						db->direction.vy += 1;
					else
						db->direction.vy += 6;
				}
			}
		}
		db++;
		i--;
	}

	lf = leaf;
	i = next_leaf;

	// Move leaves
	while (i > 0)
	{
		if (lf->flags & 0x2) 
		{
			DisplayDebris((DEBRIS *)lf, lf->type);
			
			if (pauseflag == 0)
			{
				if (lf->life == 1) 
				{
					lf->flags = 0;
					ReleaseLeaf(lf->num);
				}
				else
				{
					int sn1, sn2, cs1, cs2;

					GetSmokeDrift(&Drift);

					Height = -(lf->position.pad + 20);

					// first we move debris
					// SwirlLeaves basically changed direction vector
					// in order to lift leaf from ground fast
					if (lf->position.vy < Height)
					{
						lf->position.vx += lf->direction.vx + Drift.vx;
						lf->position.vy += lf->direction.vy + Drift.vy;
						lf->position.vz += lf->direction.vz + Drift.vz;

						lf->pos += lf->step;
						lf->pos &= 0xff;
					}
					else
					{
						lf->position.vy = Height;
					}

					lf->sin_index1 += lf->sin_addition1;
					lf->sin_index2 += lf->sin_addition2;
					lf->sin_index1 &= 0xfff;
					lf->sin_index2 &= 0xfff;

					sn1 = RSIN(lf->sin_index1);
					sn2 = RSIN(lf->sin_index2);

					cs1 = RCOS(lf->sin_index1);
					cs2 = RCOS(lf->sin_index2);

					// then we compute completely new direction
					lf->direction.vy = ((sn1 + sn2) >> 0xb) + 4;
					lf->direction.vx = ((sn1 + sn2) * 5 >> 0xb);
					lf->direction.vz = ((cs1 + cs2) * 5 >> 0xb);
				}
			}
		}
		lf++;
		i--;
	}

	for (i = 0; i < MAX_SMOKE; i++)
	{
		sm = &smoke[i];

		if (sm->flags & 0x2)
		{
			if (sm->flags & 0x8)
			{
				DisplaySpark(sm);	// yup, smoke particles are sparks too
			}
			else if (sm->flags & 0x4)
			{
				if (sm->flags & 0x8000)
					DisplayWater(sm);	// Really obsolete, water was only a thing in Driver 1
				else
					DisplaySmoke(sm);
			}
		}

		if (sm->flags & 0x2 && pauseflag == 0)
		{
			if (sm->flags & 0x8000) 
			{
				// OBSOLETE DRIVER 1 CODE
				/*
				ROUTE_DATA routeData;
				ROADS_GetRouteData(sm->position.vx - sm->start_w, sm->position.vz - sm->start_w, &routeData);
				ROADS_GetRouteData(sm->position.vx + sm->start_w, sm->position.vz + sm->start_w, &routeData);

				if (sm->start_w < 800 && (modelpointers[routeData.type]->shape_flags & MODEL_FLAG_ALLEY)) 
					sm->start_w += sm->step;
				else*/
					sm->start_w -= sm->step;
			}
			else if (sm->flags & 0x8)
			{
				sm->position.vx += sm->drift_change.vx;
				sm->position.vy += sm->drift_change.vy;
				sm->position.vz += sm->drift_change.vz;
				sm->drift_change.vy += 6;

				if (sm->drift.vx == 0)
				{
					sm->final_tail_pos.vx += sm->drift_change.vx;
					sm->final_tail_pos.vy += sm->drift_change.vy - 12;
					sm->final_tail_pos.vz += sm->drift_change.vz;
				}
				else
				{
					sm->drift.vx -= 1;
				}

				if (sm->position.vy > 0)
				{
					sm->flags = 0;
					ReleaseSmoke(sm->num);
					continue;
				}
			}
			else 
			{
				if (sm->start_w < 800)
					sm->start_w += sm->step;

				sm->position.vx += sm->drift.vx;
				sm->position.vy += sm->drift.vy;
				sm->position.vz += sm->drift.vz;
						
				if (sm->halflife < sm->life)
				{
					sm->drift.vx -= sm->drift_change.vx;
					sm->drift.vy -= sm->drift_change.vy;
					sm->drift.vz -= sm->drift_change.vz;
				}
			}

			if (sm->flags & 0x900C)
			{
				sm->transparency -= sm->t_step;

				if (sm->transparency < 1)
				{
					sm->transparency = 0;
					sm->life = 1;
				}

				if (--sm->life == 0)
				{
					sm->flags = 0;
					ReleaseSmoke(sm->num);
				}
			}
		}
	}

	if (pauseflag == 0)
	{
		main_cop_light_pos++;
		main_cop_light_pos &= 7;

		  variable_weather = 1;

		if (variable_weather != 0)
		{
			static int weather_level = 0;

			weather_level = RSIN(CameraCnt) + RSIN(CameraCnt / 4);

			if (weather_level < 1)
				gRainCount = -weather_level >> 8;
			else
				gRainCount = weather_level >> 8;

			if (gRainCount > MAX_RAIN_DROPS)
				gRainCount = MAX_RAIN_DROPS;
		}
	}
}

// [D] [T]
void add_haze(int top_col, int bot_col, short ot_pos)
{
	TILE *polys;

	if (top_col > 9 && FastForward == 0)
	{
		polys = (TILE *)current->primptr;

		setTile(polys);

		polys->r0 = top_col;
		polys->g0 = top_col;
		polys->b0 = top_col;

		setSemiTrans(polys, 1);
#ifdef PSX
		polys->x0 = 0;
		polys->w = 320;
#else
		polys->x0 = -500;
		polys->w = 1200;
#endif
		polys->y0 = 0;
		polys->h = 256;
		addPrim(current->ot + ot_pos, polys);

		current->primptr += sizeof(TILE);

		POLY_FT3* null = (POLY_FT3*)current->primptr;
		setPolyFT3(null);

		null->x0 = -1;
		null->y0 = -1;
		null->x1 = -1;
		null->y1 = -1;
		null->x2 = -1;
		null->y2 = -1;
		null->tpage = 0x20;
		addPrim(current->ot + ot_pos, null);

		current->primptr += sizeof(POLY_FT3);
	}
}


// [D] [T]
void SetupRain(void)
{
	gNextRainDrop = 0;

	do {

		gRainAlloc[gNextRainDrop] = gNextRainDrop;
		gRain[gNextRainDrop].oldposition.pad = gNextRainDrop;

		gNextRainDrop++;
	} while (gNextRainDrop < MAX_RAIN_DROPS);

	gNextRainDrop = 0;
}

// [D] [T]
int AllocateRainDrop(void)
{
	if (gNextRainDrop < MAX_RAIN_DROPS)
		return gRainAlloc[gNextRainDrop++];

	return -1;
}

// [D] [T]
void ReleaseRainDrop(int RainIndex)
{
	gNextRainDrop--;
	gRainAlloc[gNextRainDrop] = RainIndex;
}

// [D] [T]
void DisplaySplashes(void)
{
	static u_int rand;

	int d1;
	int d2;
	VECTOR CamGnd;
	VECTOR Gnd1;
	VECTOR Gnd2;
	VECTOR Position;
	CVECTOR col = { 25, 25, 25 };
	int ang;
	int SplashNo, SplashFrac;

	if (pauseflag != 0)
		return;

	if (gRainCount >> 2 > 30)
		SplashNo = 30;
	else
		SplashNo = (gRainCount >> 2);

	SplashFrac = FIXEDH(SplashNo * FrAng * 3);

	gte_SetRotMatrix(&identity); // [A] norot

	CamGnd.vx = camera_position.vx;
	CamGnd.vz = camera_position.vz;
	CamGnd.vy = -camera_position.vy - MapHeight(&CamGnd);

	ang = FrAng - camera_angle.vy;

	Gnd1.vx = RSIN(ang) + camera_position.vx;
	Gnd1.vz = RCOS(ang) + camera_position.vz;

	Gnd1.vx = Gnd1.vx - CamGnd.vx;
	Gnd1.vy = -camera_position.vy - MapHeight(&Gnd1) - CamGnd.vy;
	Gnd1.vz = Gnd1.vz - CamGnd.vz;

	ang = -FrAng - camera_angle.vy;

	Gnd2.vx = RSIN(ang) + camera_position.vx;
	Gnd2.vz = RCOS(ang) + camera_position.vz;

	Gnd2.vx = Gnd2.vx - CamGnd.vx;
	Gnd2.vy = (-camera_position.vy - MapHeight(&Gnd2)) - CamGnd.vy;
	Gnd2.vz = Gnd2.vz - CamGnd.vz;

	while (--SplashFrac >= 0)
	{
		d2 = rand * 0x19660d + 0x3c6ef35f;
		d1 = d2 >> 4 & 0xfff;
		rand = d2 * 0x19660d + 0x3c6ef35f;
		d2 = rand >> 0xe & 0xfff;

		Position.vx = FIXEDH(Gnd1.vx * d1 + Gnd2.vx * d2);
		Position.vy = FIXEDH(Gnd1.vy * d1 + Gnd2.vy * d2) + CamGnd.vy;
		Position.vz = FIXEDH(Gnd1.vz * d1 + Gnd2.vz * d2);

		ShowLight(&Position, &col, 12, &drop_texture);
	}
}

#define RAIN_DROP_SPEED 30		// [A] was 20

// [D] [T]
void DrawRainDrops(void)
{
	int z;
	int Count;
	POLY_GT3 *poly;
	RAIN_TYPE *RainPtr;
	int col;
	int bright;
	SVECTOR v;
	VECTOR drift;

	if (gNextRainDrop == 0)
		return;

	GetSmokeDrift(&drift);

	bright = 70;

	if (bright < 50)
		bright = 50;

	if (gNight != 0)
		bright -= -15;

	col = bright >> 1 | (bright >> 1) << 8;
	col |= col | col << 0x10;

	DisplaySplashes();

	gte_SetRotMatrix(&inv_camera_matrix);
	gte_SetTransVector(&dummy);

	RainPtr = gRain;
	Count = gNextRainDrop-1;

	poly = (POLY_GT3 *)current->primptr;
	
	while (Count > 0) 
	{
		v.vx = RainPtr->position.vx - camera_position.vx;
		v.vy = RainPtr->position.vy - camera_position.vy;
		v.vz = RainPtr->position.vz - camera_position.vz;

		if (pauseflag) 
		{
			// [A] old position imitation
			// works effectively when player not moving
			gte_ldv0(&v);
			gte_rtps();

			gte_stsxy(&poly->x0);

			v.vy += RAIN_DROP_SPEED;
			v.vx -= drift.vx * 2;
			v.vz -= drift.vz * 2;
		}
		else
		{
			RainPtr->position.vy += RAIN_DROP_SPEED;
			RainPtr->position.vx -= drift.vx * 2;
			RainPtr->position.vz -= drift.vz * 2;

			*(u_int *)&poly->x0 = *(u_int *)&RainPtr->oldposition;
		}

		gte_ldv0(&v);
		gte_rtps();

		*(u_int *)&poly->r2 = col;
		*(u_int *)&poly->r1 = col;
		*(u_int *)&poly->r0 = 0;

		setPolyGT3(poly);
		setSemiTrans(poly, 1);
		
		gte_stsxy(&poly->x2);

		gte_stsz(&z);

		if (z - 151U < 2000 && 
			poly->x2 > -101 && poly->x2 < 421 && 
			poly->y2 > -51 && poly->y2 < 257) 
		{
			if (*(u_int *)&RainPtr->oldposition != 0) 
			{				
				poly->x1 = poly->x2 - ((z >> 10) - 1);
				poly->x2 = poly->x2 + ((z >> 10) - 1);
				poly->y1 = poly->y2;

				*(u_int *)&RainPtr->oldposition = *(u_int *)&poly->x2;

				poly->clut = light_texture.clutid;
				poly->tpage = light_texture.tpageid | 0x20;

				*(ushort *)&poly->u0 = *(ushort *)&light_texture.coords.u0;
				*(ushort *)&poly->u1 = *(ushort *)&light_texture.coords.u1;
				*(ushort *)&poly->u2 = *(ushort *)&light_texture.coords.u2;

				addPrim(current->ot + (z >> 1), poly);
				poly++;
			}
			else
				*(u_int *)&RainPtr->oldposition = *(u_int *)&poly->x2;
		}
		else 
		{
			if(pauseflag == 0)
				ReleaseRainDrop(RainPtr->oldposition.pad);
		}

		RainPtr++;
		Count--;
	}

	current->primptr = (char*)poly;
}


// [D] [T]
void AddRainDrops(void)
{
	static u_int rand;

	int first;
	int RainIndex;
	int tmp;
	int i;
	SVECTOR v;
	ROUTE_DATA routeData;
	int count;
	RAIN_TYPE* rt;

	count = gRainCount;
	first = 1;

	if (pauseflag != 0 || NoRainIndoors != 0)
		return;

	wetness += count / 6;

	if (wetness > 7000)
		wetness = 7000;

	gte_SetRotMatrix(&camera_matrix);
	gte_SetTransVector(&camera_position);

	i = 0;
	while (i < count) 
	{
		i++;

		RainIndex = AllocateRainDrop();

		if (RainIndex < 0)
			return;

		tmp = rand * 0x19660d + 0x3c6ef35f;
		v.vz = (tmp >> 0x14 & 0x1ffU) + 400;
	
		tmp = tmp * 0x19660d + 0x3c6ef35f;
		v.vy = -((tmp >> 0x14) & 0x1ff);
	
		rand = tmp * 0x19660d + 0x3c6ef35f;
		v.vx = ((rand >> 0x14) & 0x1ff) - 256;

		if (v.vz > 512) 
		{
			int depth;
			depth = (v.vz >> 8) + 1;
			
			v.vx *= depth;
			v.vy *= depth;
		}

		gte_ldv0(&v);
		gte_rtv0tr();

		rt = &gRain[RainIndex];

		rt->oldposition.vx = 0;
		rt->oldposition.vy = 0;
		rt->oldposition.vz = 0;
		rt->oldposition.pad = RainIndex;

		gte_stlvnl(&rt->position);

		// OBSOLETE DRIVER 1 CODE
		/*
		if(first)
		{
			ROADS_GetRouteData(rt->position.vx, rt->position.vz, &routeData);

			if (modelpointers[routeData.type]->flags2 & MODEL_FLAG_INDOORS)
				break;
		}
		*/

		first = 0;
	}
}

int lightning = 0;
int LightningTimer = 10;
int ThunderTimer = 0;
static int ThunderDistance = 0;

// [D] [T]
void DoLightning(void)
{
	if (pauseflag != 0)
		return;

	if (LightningTimer < -10) 
		LightningTimer =  rand() & 0xff;
	else 
		LightningTimer--;
	
	if (LightningTimer < 0 && (rand() & 1) == 0)
	{
		RequestThunder();

		if ((rand() & 1) == 0) 
		{
			NightAmbient = NightAmbient << 1;

			if (NightAmbient > 128) 
				NightAmbient = 128;

			lightning = 2;
			return;
		}

		add_haze(50, 0, 4222);
	}

	lightning = 0;
}


// [D] [T]
void InitThunder(void)
{
	ThunderTimer = -1;
	ThunderDistance = rand() % 5000;
}


// [D] [T]
void RequestThunder(void)
{
	ThunderTimer = ThunderDistance >> 8;

	if (ThunderTimer < 1)
		ThunderTimer = 1;
}


// [D] [T]
void DoThunder(void)
{
	if (pauseflag)
		return;

	ThunderDistance = (ThunderDistance + 40) - rand() % 80;

	if (ThunderDistance < 250)
		ThunderDistance = 250;

	if (ThunderDistance > 5000) 
		ThunderDistance = 5000;

	if (ThunderTimer > -1)
	{
		ThunderTimer--;
		
		if(ThunderTimer == 0) 
			StartSound(-1, SOUND_BANK_SFX, 8, -ThunderDistance, (rand() % 2048) + 3072);
	}
}


// [D] [T]
void DoWeather(int weather)
{
	if (weather != 1)
		return;

	if(pauseflag == 0)
	{
		if (gEffectsTimer < 41) 
		{
			if ((FrameCnt & 7U) == 0) 
				gEffectsTimer++;
		}
		else
		{
			AddRainDrops();

			if ((FrameCnt & 7U) == 0)
				gRainCount++;

			if (gRainCount > 35)
				gRainCount = 35;
		}
	}

	DrawRainDrops();
}

// [D] [T]
int GetDebrisColour(CAR_DATA *cp)
{
	int car_model;

	car_model = MissionHeader->residentModels[cp->ap.model];

	if (car_model == 0)
		return 1;

	if(car_model < 8)
		return (car_model-1) * 6 + cp->ap.palette + 7;

	return car_model - 6;
}





