////////////////////////////////////////////////////////////////////////////////////////////////
/* "Maelstrom" V1.0 Copyright Philip Corner 2003. Based on an original concept by Dave Theurer*/
////////////////////////////////////////////////////////////////////////////////////////////////

#include "mygba.h"

MULTIBOOT

#define FRONTBUFFER (u16*)0x6000000
#define BACKBUFFER (u16*)0x600A000

#define HALFLENGTH 100
//Half the length of the tunnel. Allows us to determine Z ordinates
//for centering around origin
#define XPIXELS	240
#define YPIXELS 160
#define MAXYAW 20
#define MAXPITCH 15
#define MAXXOFFSET 120
#define MAXYOFFSET 50
#define MAXSHIPS 10
#define MAXSHOTS 3
#define MAXVERTS 30
#define MAXLINES 30
#define MINZOOM 13500
#define MAXZOOM 16000
#define FRAMESKIP 2
//#define XPIXELS 160
//#define YPIXELS 120
//#define GAMMA 15

struct crosspoints
{
	int x ,y;
};

typedef struct
{
	int numpoints;
	struct crosspoints crossec[20];
}Tunnel;
//We'll define an array of these to hold the tunnel cross sections

typedef struct
{
	u16 rollangle;
	s16 x;
	s16 y;
}STransform;
/*We'll have an array of these to store the necessary 
transformations for each of the polygons*/

typedef struct
{
	s16 pitch;
	s16 yaw;
	s8 xoffset;
	s8 yoffset;
}VTransform;
/* We'll create an array of these with which to hold the view 
transformations for each of the positions on the tunnel*/

typedef struct
{
	int numpoints;
	int tvertex[40][3];
	int tline[60][2];
	int viewvertex[40][3];
	int eyevertex[40][2];
}ExtrudeTunnel;
/*We'll only need one of these. It contains the extruded tunnel. 
Tvertex are tunnel vertices in world space and only change upon a 
new level, while viewvertex and eyevertex change each frame as we 
view transform. Then we perspective and screen transform our points */

typedef struct
{
	u8 numpoints;
	u8 numlines;
	int mvertex[MAXVERTS][3];
	u8 mline[MAXLINES][2];			//References to vertices
//	int mtri[40][3];		//References to lines
//	int mnormal[40][3];	//A vector
}Model;

typedef struct
{
	u8 valid;	//Whether this enemy element is actually active
	u8 polygon;	//Which polygon is this object located on?
	u8 desiredposition;
	s16 depth;	//How far down the tunnel is the object?
	s16 speed;
	u8 model;	//Which model do we draw for this instance? (refers to above structure)
	s8 lastpress;
	int viewvertex[MAXVERTS][3];	//Although the model remains the same, each instance must hold its own transformed points
	int eyevertex[MAXVERTS][2];
//	int viewnormal[20][3];
}Ship;

void extrudetunnel(Tunnel *, ExtrudeTunnel *);
	/*Using the cross section "Tunnel" as input, this function
generates a 3D tunnel "ExtrudeTunnel"*/

void precalcspatial(Tunnel t);
	/*Using the cross section of the tunnel, derives the spatial 
transformations to place objects on each of the polygons*/

void perstrantunnel();
/* Passed in the view transformed tunnel from above, and outputs a
perpective version thereof*/

void rotatetunnel(void);

void screendraw(void);

void translatetunnel(void);

void transformenemies(void);	//Move all the enemies to the correct place. Account for the tunnel's rotation, then move to correct polygon

void translatenemies(void);

void perstranenemies(void);

void drawenemies(void);
//This function would ultimately want to draw the ships from furthest away to closest. We can easily achieve this with a sort using the Z values of each enemy

void vblfunc();

void mainloop();

void interpolate(Tunnel);

void precalcview(Tunnel);
/*Derives the view transformations needed from the spatial
transformations. This changes our view of the tunnel depending on
which of its polygons we currently occupy. "Tunnel" is only required
for the number of points/polygons in the tunnel*/

inline void SRand(int r);

inline int Rand(void);

void expireandcreateenemies();

void expireandcreateshots();

void moveanddestroyshots();
/*Compare our shots and see what the have hit. For each of our shots in flight, find which polygon it is on and compare depths with all
the ships also on that polygon. If our shot is deeper than an enemy also on that polygon, within a certain distance, then
both are destroyed. The test for the distance by which we have passed an enemy is necessary to account for the situation
in which an enemy moves onto a polygon behind a shot. If we didn't test, this shot would destroy that ship even though it has already
passed that position. Distance should be shot speed per frame*/

void moveenemies();

void changebuffer();

void BLine(int x0, int y0, int x1, int y1, unsigned char color);

Tunnel tunnelcs=
{
	12,
	{ 	{0, 50},
		{30, 40},
		{60, 20},
		{90, 0},
		{60, -20},
		{40, -40},
		{0, -60},
		{-40, -40},
		{-60, -20},
		{-90, 0},
		{-60, 20},
		{-30, 40}	}
};

s16 sinlook[]= {0, 71, 142, 214, 285, 356, 427, 498, 569, 640, 710, 781, 851, 920, 990, 1059, 1128, 1196, 1265, 1332, 1400, 1467, 1533, 1599, 1665, 1730, 1794, 1858, 1921, 1984, 2046, 2108, 2169, 2229, 2289, 2348, 2406, 2463, 2520, 2576, 2631, 2685, 2739, 2792, 2843, 2894, 2945, 2994, 3042, 3089, 3136, 3181, 3226, 3269, 3312, 3353, 3394, 3433, 3472, 3509, 3545, 3581, 3615, 3648, 3680, 3711, 3740, 3769, 3796, 3822, 3847, 3871, 3894, 3916, 3936, 3955, 3973, 3990, 4005, 4020, 4033, 4045, 4055, 4065, 4073, 4080, 4085, 4090, 4093, 4095, 4095, 4095, 4093, 4090, 4086, 4080, 4073, 4065, 4056, 4046, 4034, 4021, 4007, 3992, 3975, 3957, 3938, 3918, 3896, 3874, 3850, 3825, 3799, 3772, 3743, 3714, 3683, 3651, 3618, 3584, 3549, 3513, 3476, 3438, 3398, 3358, 3316, 3274, 3231, 3186, 3141, 3094, 3047, 2999, 2950, 2900, 2849, 2797, 2745, 2691, 2637, 2582, 2526, 2469, 2412, 2354, 2295, 2236, 2175, 2115, 2053, 1991, 1928, 1865, 1801, 1737, 1672, 1606, 1540, 1474, 1407, 1340, 1272, 1204, 1135, 1066, 997, 928, 858, 788, 718, 647, 577, 506, 435, 364, 293, 221, 150, 79, 7, -63, -135, -206, -277, -349, -420, -491, -562, -632, -703, -773, -843, -913, -982, -1052, -1120, -1189, -1257, -1325, -1392, -1459, -1526, -1592, -1658, -1723, -1787, -1851, -1915, -1977, -2040, -2101, -2162, -2223, -2282, -2341, -2400, -2457, -2514, -2570, -2625, -2680, -2733, -2786, -2838, -2889, -2939, -2989, -3037, -3084, -3131, -3176, -3221, -3265, -3307, -3349, -3390, -3429, -3468, -3505, -3542, -3577, -3611, -3644, -3676, -3707, -3737, -3766, -3793, -3820, -3845, -3869, -3892, -3913, -3934, -3953, -3971, -3988, -4004, -4018, -4031, -4043, -4054, -4064, -4072, -4079, -4085, -4089, -4093, -4095, -4095, -4095, -4093, -4090, -4086, -4081, -4074, -4066, -4057, -4047, -4035, -4023, -4008, -3993, -3977, -3959, -3940, -3920, -3899, -3876, -3853, -3828, -3802, -3775, -3746, -3717, -3686, -3655, -3622, -3588, -3553, -3517, -3480, -3442, -3402, -3362, -3321, -3279, -3235, -3191, -3146, -3099, -3052, -3004, -2955, -2905, -2855, -2803, -2750, -2697, -2643, -2588, -2532, -2476, -2418, -2360, -2301, -2242, -2182, -2121, -2060, -1998, -1935, -1872, -1808, -1743, -1679, -1613, -1547, -1481, -1414, -1347, -1279, -1211, -1143, -1074, -1005, -935, -866, -796, -725, -655, -584, -514, -443, -372, -300, -229, -158, -86, };
s16 coslook[]= {4096, 4095, 4093, 4090, 4086, 4080, 4073, 4065, 4056, 4045, 4033, 4020, 4006, 3991, 3974, 3956, 3937, 3917, 3895, 3873, 3849, 3824, 3798, 3770, 3742, 3712, 3681, 3650, 3617, 3583, 3547, 3511, 3474, 3435, 3396, 3356, 3314, 3272, 3228, 3184, 3138, 3092, 3045, 2996, 2947, 2897, 2846, 2794, 2742, 2688, 2634, 2579, 2523, 2466, 2409, 2351, 2292, 2232, 2172, 2111, 2050, 1988, 1925, 1861, 1798, 1733, 1668, 1603, 1537, 1470, 1403, 1336, 1268, 1200, 1132, 1063, 994, 924, 854, 784, 714, 644, 573, 502, 431, 360, 289, 218, 146, 75, 3, -67, -139, -210, -281, -352, -424, -495, -565, -636, -707, -777, -847, -917, -986, -1055, -1124, -1193, -1261, -1329, -1396, -1463, -1529, -1595, -1661, -1726, -1791, -1855, -1918, -1981, -2043, -2105, -2166, -2226, -2286, -2344, -2403, -2460, -2517, -2573, -2628, -2682, -2736, -2789, -2841, -2892, -2942, -2991, -3039, -3087, -3133, -3179, -3223, -3267, -3310, -3351, -3392, -3431, -3470, -3507, -3544, -3579, -3613, -3646, -3678, -3709, -3739, -3767, -3795, -3821, -3846, -3870, -3893, -3914, -3935, -3954, -3972, -3989, -4004, -4019, -4032, -4044, -4055, -4064, -4072, -4079, -4085, -4089, -4093, -4095, -4095, -4095, -4093, -4090, -4086, -4081, -4074, -4066, -4057, -4046, -4035, -4022, -4008, -3992, -3976, -3958, -3939, -3919, -3898, -3875, -3851, -3827, -3800, -3773, -3745, -3715, -3685, -3653, -3620, -3586, -3551, -3515, -3478, -3440, -3400, -3360, -3319, -3276, -3233, -3189, -3143, -3097, -3050, -3002, -2953, -2903, -2852, -2800, -2747, -2694, -2640, -2585, -2529, -2472, -2415, -2357, -2298, -2239, -2179, -2118, -2056, -1994, -1932, -1868, -1804, -1740, -1675, -1610, -1544, -1477, -1410, -1343, -1275, -1207, -1139, -1070, -1001, -932, -862, -792, -722, -651, -581, -510, -439, -368, -297, -225, -154, -82, -11, 59, 131, 202, 274, 345, 416, 487, 558, 628, 699, 769, 839, 909, 979, 1048, 1117, 1185, 1254, 1321, 1389, 1456, 1522, 1588, 1654, 1719, 1784, 1848, 1911, 1974, 2036, 2098, 2159, 2219, 2279, 2338, 2396, 2454, 2511, 2567, 2622, 2677, 2730, 2783, 2835, 2886, 2937, 2986, 3034, 3082, 3128, 3174, 3219, 3262, 3305, 3347, 3387, 3427, 3466, 3503, 3540, 3575, 3609, 3643, 3675, 3706, 3736, 3764, 3792, 3818, 3843, 3868, 3890, 3912, 3933, 3952, 3970, 3987, 4003, 4017, 4031, 4043, 4054, 4063, 4071, 4079, 4084, 4089, 4092, 4095, };
u8 itanlook[]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 32, 33, 33, 34, 35, 35, 36, 36, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, };

STransform spatialtransform[60];
//Array holding transforms for each polygon. Regenerated each level 
VTransform viewtransform[60];
/*Array holding view transforms for each polygon. Regenerated each
level*/

ExtrudeTunnel thistunnel;
//Holds the full 3D model of the current tunnel

Ship ship[MAXSHIPS+MAXSHOTS];
	//Holds info on all objects on the surface of the tunnel

	//Now defining the models we'll be using
Model model[]=
{
	{
		18,
		18,
		{
			{750, -750, 0},
			{900, -600, 0},
			{750, -300, 0},

			{150, -300, 0},
			{450, 0, 0},
			{1050, 0, 0},
			{1500, -600, 0},
			{1500, -1200, 0},
			{900, -2100, 0},
			{-900, -2100, 0},
			{-1500, -1200, 0},
			{-1500, -600, 0},
			{-1050, 0, 0},
			{-450, 0, 0},
			{-150, -300, 0},
			{-750, -300, 0},

			{-900, -600, 0},
			{-750, -750, 0} },
		{
			{0, 1},
			{1, 2},
			{2, 3},
			{3, 4},
			{4, 5},
			{5, 6},
			{6, 7},
			{7, 8},
			{8, 9},
			{9, 10},
			{10, 11},
			{11, 12},
			{12, 13},
			{13, 14},
			{14, 15},
			{15, 16},
			{16, 17},
			{17, 0}	}
	},

	{
		5,
		8,
		{
			{0, -500, -1900},
			{-1000, -1000, 0},
			{-1000, 0, 0},
			{1000, 0, 0},
			{1000, -1000, 0}	},
		{
			{0, 1},
			{0, 2},
			{0, 3},
			{0, 4},
			{1, 2},
			{2, 3},
			{3, 4},
			{4, 1}	}
	},
	{
		6,
		12,
		{
			{0, -500, 0},
			{-500, -500, -1500},
			{0, -1000, -1500},
			{500, -500, -1500},
			{0, 0, -1500},
			{0, -500, -2300}	},
		{
			{0, 1},
			{0, 2},
			{0, 3},
			{0, 4},
			{1, 2},
			{2, 3},
			{3, 4},
			{4, 1},
			{5, 1},
			{5, 2},
			{5, 3},
			{5, 4}	}
	}
};

int gamma=0;
int theta=0;
int frameready=0;
int dolly=MAXZOOM;
int _rand_rndseed = 1;
u16 *VideoBuffer;
int xoffset, yoffset;

int main()
{
	int i;

	ham_Init();
	ham_SetBgMode(4);
	ham_SetBgPalCol(0, COLOR_BLACK);
	ham_SetBgPalCol(1, COLOR_WHITE);

	ship[0].valid=1;
	ship[0].polygon=21;
	ship[0].desiredposition=21;
	ship[0].depth=-10000;
	ship[0].speed=0;
	ship[0].model=1;
	ship[0].lastpress=0;

	extrudetunnel(&tunnelcs, &thistunnel);
	precalcspatial(tunnelcs);
	interpolate(tunnelcs);
	precalcview(tunnelcs);
	for(i=0; i<tunnelcs.numpoints*3;i++)
	{
		spatialtransform[i].x*=100;
		spatialtransform[i].y*=100;
	}

	for(i=0;i<(thistunnel.numpoints)*2;i++)
	{
		thistunnel.viewvertex[i][0]=thistunnel.tvertex[i][0];
		thistunnel.viewvertex[i][1]=thistunnel.tvertex[i][1];
		thistunnel.viewvertex[i][2]=thistunnel.tvertex[i][2];

		thistunnel.viewvertex[i][0]*=100;
		thistunnel.viewvertex[i][1]*=100;
		thistunnel.viewvertex[i][2]*=100;
		
		thistunnel.tvertex[i][0]*=100;
		thistunnel.tvertex[i][1]*=100;
		thistunnel.tvertex[i][2]*=100;
	}

	ham_StartIntHandler(INT_TYPE_VBL, &vblfunc);
	while(1)
	{
		if(!frameready)		//If this true, we have finished rendering the current frame and need to do another one
			mainloop();
	}
}

void mainloop()
{
	int j;
	int zoomfraction;

	rotatetunnel();
	translatetunnel();
		//Translating the tunnel so none of the points have Z=0
	perstrantunnel();

	expireandcreateenemies();
	moveenemies();
	expireandcreateshots();
	moveanddestroyshots();
	transformenemies();
	translatenemies();
	perstranenemies();

	// All of the ships, ours and enemy's, move towards their desired positions
	for(j=0;j<MAXSHIPS;j++)
	{
		if(ship[j].valid)
		{
			if(ship[j].desiredposition!=ship[j].polygon)	//We haven't reached our desired position
			{
				if((ship[j].lastpress==-1)&&(ship[j].polygon==0))
					ship[j].polygon=thistunnel.numpoints*3-1;
				else
				{
					if((ship[j].lastpress==1)&&(ship[j].polygon==thistunnel.numpoints*3-1))
						ship[j].polygon=0;
					else
						ship[j].polygon+=ship[j].lastpress;
				}
			}
		}
	}

	zoomfraction=(1<<10)-((dolly-MINZOOM)<<10)/(MAXZOOM-MINZOOM);
	xoffset=((viewtransform[ship[0].polygon].xoffset*zoomfraction)>>10);
	yoffset=((viewtransform[ship[0].polygon].yoffset*zoomfraction)>>10);
	gamma=viewtransform[ship[0].polygon].yaw;
	theta=viewtransform[ship[0].polygon].pitch;

	changebuffer();	//This function sets us up to draw in the correct buffer
	
	/*We are now going to draw all the lines required to build the tunnel. The number of lines = numpoints * 3 (each 
	end, plus the joining lines going down the length of the wall)*/
	screendraw();
	drawenemies();

	frameready=1;
}

void vblfunc()
{
	static u8 skippedframes=0;

	if(frameready && skippedframes++>=FRAMESKIP)
	//Frameskip prevents the framerate exceeding a certain value, stopping the game becomming very fast when there are few objects in view
	{
		skippedframes=0;

		// The movement of the ship 0, ours, is controlled by the D-pad
		if(ship[0].desiredposition==ship[0].polygon)
		{
			if(F_CTRLINPUT_LEFT_PRESSED)
			{
				ship[0].lastpress=-1;
				if(ship[0].desiredposition==0)
					ship[0].desiredposition=thistunnel.numpoints*3-3;
				else
					ship[0].desiredposition-=3;
			}
			if(F_CTRLINPUT_RIGHT_PRESSED)
			{
				ship[0].lastpress=1;
				if(ship[0].desiredposition==thistunnel.numpoints*3-3)
					ship[0].desiredposition=0;
				else
					ship[0].desiredposition+=3;
			}
			if(F_CTRLINPUT_R_PRESSED)
			{
				if(dolly>MINZOOM)
					dolly-=60;
			}
			if(F_CTRLINPUT_L_PRESSED)
			{
				if(dolly<MAXZOOM)
					dolly+=60;
			}
		}
		ham_FlipBGBuffer();
		ham_ClearBackBuffer(0);
		frameready=0;	//Reset frameready so now mainloop can restart
	}

}

void extrudetunnel(Tunnel *t, ExtrudeTunnel *et)
{
	int i, L=0;
	et->numpoints=t->numpoints;
	/*Store numpoints in Extruded tunnel also, to avoid having to
	pass both objects into other functions later*/
	for(i=0;i<(t->numpoints);i++)
	{
		et->tvertex[i][0]=t->crossec[i].x;
		et->tvertex[i][1]=t->crossec[i].y;
		et->tvertex[i][2]=HALFLENGTH;
	}

	for(i=t->numpoints;i<(t->numpoints*2);i++)
	{
		et->tvertex[i][0]=t->crossec[i-t->numpoints].x;
		//Using the same points as above..
		et->tvertex[i][1]=t->crossec[i-t->numpoints].y;
		et->tvertex[i][2]=-HALFLENGTH;
	/*By giving points depth HALFLENGTH and -HALFLENGTH, we centre
	them around the origin. Then we can rotate and translate with the
	info in viewtransform */
	}
	for(i=0;i<t->numpoints-1;i++)
	//The seam between start and end will be handled separately
	{
		et->tline[L][0]=i;
		et->tline[L++][1]=i+1;
		et->tline[L][0]=i+1;
		et->tline[L++][1]=i+t->numpoints+1;
		et->tline[L][0]=i+t->numpoints+1;
		et->tline[L++][1]=i+t->numpoints;
	}
	et->tline[L][0]=t->numpoints-1;
	et->tline[L++][1]=0;
	et->tline[L][0]=0;
	et->tline[L++][1]=t->numpoints;
	et->tline[L][0]=t->numpoints;
	et->tline[L++][1]=t->numpoints*2-1;
}	
// We now have the points of our tunnel in 3D world space. We now
//need to transform them by eye. Rotate appropriately, then 
//translate.

void perstrantunnel()
{
	int i;

	for(i=0;i<(thistunnel.numpoints)*2;i++)
	{

		thistunnel.eyevertex[i][0]=thistunnel.viewvertex[i][0]/(thistunnel.viewvertex[i][2]>>6)+XPIXELS/2-xoffset;	//>>6 Equivalent to divide by 64

		thistunnel.eyevertex[i][1]=thistunnel.viewvertex[i][1]/(thistunnel.viewvertex[i][2]>>6)+YPIXELS/2-yoffset;	//YPIXELS/2 positions in centre of screen
	}
	/*When our vanishing point is in the centre of the screen, and our
	near clip plane is at -1, perspective simplifies to dividing by the
	Z (depth) of the vertex into the screen, so things move towards the
	centre of the screen the further away they are*/
}

void rotatetunnel()
{
	int i, tempz;
	for(i=0;i<(thistunnel.numpoints)*2;i++)
	{
/*		thistunnel.viewvertex[i][0]=thistunnel.tvertex[i][0]*coslook[20]+thistunnel.tvertex[i][1]*(-sinlook[20]);
		thistunnel.viewvertex[i][1]=thistunnel.tvertex[i][0]*sinlook[20]+thistunnel.tvertex[i][1]*coslook[20];
		thistunnel.viewvertex[i][2]=thistunnel.tvertex[i][2];
*/
			//Above is rot in Z axis (roll)

		thistunnel.viewvertex[i][1]=(thistunnel.tvertex[i][1]*coslook[theta]-thistunnel.tvertex[i][2]*sinlook[theta])>>12;	//>>12 Equivalent to /4096
		tempz=(thistunnel.tvertex[i][1]*sinlook[theta]+thistunnel.tvertex[i][2]*coslook[theta])>>12;

		thistunnel.viewvertex[i][0]=(thistunnel.tvertex[i][0]*coslook[gamma]+tempz*sinlook[gamma])>>12;
		thistunnel.viewvertex[i][2]=(thistunnel.tvertex[i][0]*(-sinlook[gamma])+tempz*coslook[gamma])>>12;

	}
}

void transformenemies()
{
	int i, j, tempz, tempx, tempy;

	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{
			for(i=0;i<(model[ship[j].model].numpoints);i++)
			{
			//To translate to the tunnel polygon we require, we first rotate by the polygon's orientation, then translate by its
			//position before rotating by the tunnel's rotation.

			//Rotate by polygon's orientation
				ship[j].viewvertex[i][0]=(model[ship[j].model].mvertex[i][0]*coslook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mvertex[i][1]*(-sinlook[spatialtransform[ship[j].polygon].rollangle]))>>12;
				ship[j].viewvertex[i][1]=(model[ship[j].model].mvertex[i][0]*sinlook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mvertex[i][1]*coslook[spatialtransform[ship[j].polygon].rollangle])>>12;
				ship[j].viewvertex[i][2]=model[ship[j].model].mvertex[i][2];

			//Translate by polygon's position, and enemy's depth
				ship[j].viewvertex[i][0]+=spatialtransform[ship[j].polygon].x;
				ship[j].viewvertex[i][1]+=spatialtransform[ship[j].polygon].y;
				ship[j].viewvertex[i][2]-=ship[j].depth;

			//Rotate by the tunnel's orientation
				tempy=(ship[j].viewvertex[i][1]*coslook[theta]-ship[j].viewvertex[i][2]*sinlook[theta])>>12;
				tempz=(ship[j].viewvertex[i][1]*sinlook[theta]+ship[j].viewvertex[i][2]*coslook[theta])>>12;

				tempx=(ship[j].viewvertex[i][0]*coslook[gamma]+tempz*sinlook[gamma])>>12;
				ship[j].viewvertex[i][2]=(ship[j].viewvertex[i][0]*(-sinlook[gamma])+tempz*coslook[gamma])>>12;
				ship[j].viewvertex[i][0]=tempx;
				ship[j].viewvertex[i][1]=tempy;
			}
		}
	}
}

void translatetunnel()
{
	int i;
	for(i=0;i<(thistunnel.numpoints)*2;i++)
	{
		thistunnel.viewvertex[i][2]-=dolly;
	}
}

void translatenemies()
{
	int i, j;
	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{
			for(i=0;i<model[ship[j].model].numpoints;i++)
			{
				ship[j].viewvertex[i][2]-=dolly;
			}
		}
	}
}

void perstranenemies()
{
	int i, j;
	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{
			for(i=0;i<(model[ship[j].model].numpoints);i++)
			{

				ship[j].eyevertex[i][0]=ship[j].viewvertex[i][0]/(ship[j].viewvertex[i][2]>>6)+XPIXELS/2-xoffset;	//>>6 Equivalent to divide by 64

				ship[j].eyevertex[i][1]=ship[j].viewvertex[i][1]/(ship[j].viewvertex[i][2]>>6)+YPIXELS/2-yoffset;	//YPIXELS/2 positions in centre of screen
			}
		}
	}

	/*When our vanishing point is in the centre of the screen, and our
	near clip plane is at -1, perspective simplifies to dividing by the
	Z (depth) of the vertex into the screen, so things move more towards the
	centre of the screen the further away they are*/
}

void screendraw()
{
	int i;
	int clipped[2][2];	//Will store the X & Y ordinates of the start and end points of the line.
	int t;

	for(i=0;i<(thistunnel.numpoints)*3;i++)
	{
		//In the event of a clip not being required, these values still need to be in the clipped array so the draw command will pick them up
		clipped[0][0]=thistunnel.eyevertex[thistunnel.tline[i][0]][0];
		clipped[0][1]=thistunnel.eyevertex[thistunnel.tline[i][0]][1];
		clipped[1][0]=thistunnel.eyevertex[thistunnel.tline[i][1]][0];
		clipped[1][1]=thistunnel.eyevertex[thistunnel.tline[i][1]][1];

		//Notice that the screen coordinates range from 0 to XPIXELS-1. Therefore, greater than OR equal to XPIXELS is off the screen and needs to be clipped
		if(thistunnel.eyevertex[thistunnel.tline[i][0]][0]>=XPIXELS)
		//if off right side..
		{
			if(thistunnel.eyevertex[thistunnel.tline[i][1]][0]<XPIXELS)	//Only on the screen if LESS than XPIXELS
			//If this is false, both points lie outside the viewspace in the same direction and therefore CANNOT intersect the viewspace. ie. No drawing necessary.
			//The above test is not necessary, but it does eliminate some of the lines completely outside the screen very quickly
			{
				t=((XPIXELS-thistunnel.eyevertex[thistunnel.tline[i][0]][0])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][1]][0]-thistunnel.eyevertex[thistunnel.tline[i][0]][0]);
				//Now use value of t found to generate the Y ordinate
				clipped[0][1]=(thistunnel.eyevertex[thistunnel.tline[i][0]][1]+(t*(thistunnel.eyevertex[thistunnel.tline[i][1]][1]-thistunnel.eyevertex[thistunnel.tline[i][0]][1])>>10));
				clipped[0][0]=XPIXELS-1;
			}
		}
		else
		if(thistunnel.eyevertex[thistunnel.tline[i][0]][0]<0)
		//else, if off left side..
		{
			if(thistunnel.eyevertex[thistunnel.tline[i][1]][0]>=0)
			//If this is false, both points lie outside the viewspace in the same direction and therefore CANNOT intersect the viewspace. ie. No drawing necessary.
			//The above test is not necessary, but it does eliminate some of the lines completely outside the screen very quickly
			{
				t=((0-thistunnel.eyevertex[thistunnel.tline[i][0]][0])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][1]][0]-thistunnel.eyevertex[thistunnel.tline[i][0]][0]);
				//Now use value of t found to generate the Y ordinate
				clipped[0][1]=(thistunnel.eyevertex[thistunnel.tline[i][0]][1]+(t*(thistunnel.eyevertex[thistunnel.tline[i][1]][1]-thistunnel.eyevertex[thistunnel.tline[i][0]][1])>>10));
				clipped[0][0]=0;
			}
		}

		if(clipped[0][1]>=YPIXELS)
		//Has the clipping produced a value on-screen? If not, try clipping the bottom boundary instead
		{
			t=((YPIXELS-thistunnel.eyevertex[thistunnel.tline[i][0]][1])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][1]][1]-thistunnel.eyevertex[thistunnel.tline[i][0]][1]);
			//Now use value of t found to generate the X ordinate
			clipped[0][0]=(thistunnel.eyevertex[thistunnel.tline[i][0]][0]+(t*(thistunnel.eyevertex[thistunnel.tline[i][1]][0]-thistunnel.eyevertex[thistunnel.tline[i][0]][0])>>10));
			clipped[0][1]=YPIXELS-1;
		}
		else
		if(clipped[0][1]<0)
		//Testing for the possibility of above the screen
		{
			t=((0-thistunnel.eyevertex[thistunnel.tline[i][0]][1])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][1]][1]-thistunnel.eyevertex[thistunnel.tline[i][0]][1]);
			//Now use value of t found to generate the X ordinate
			clipped[0][0]=(thistunnel.eyevertex[thistunnel.tline[i][0]][0]+(t*(thistunnel.eyevertex[thistunnel.tline[i][1]][0]-thistunnel.eyevertex[thistunnel.tline[i][0]][0])>>10));
			clipped[0][1]=0;
		}

		if(clipped[0][0]>=0 && clipped[0][0]<XPIXELS && clipped[0][1]>=0 && clipped[0][1]<YPIXELS)
		{
		// Now perform clipping on the other endpoint. Notice that if the above clause is false, our line lies completely
		//outside the viewspace and therefore we don't need to proceed with this clipping or the drawing
			if(thistunnel.eyevertex[thistunnel.tline[i][1]][0]>=XPIXELS)
			{
			//Notice we need no test here to find if they are off the screen in the same direction, as we have already done that above
				t=((XPIXELS-thistunnel.eyevertex[thistunnel.tline[i][1]][0])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][0]][0]-thistunnel.eyevertex[thistunnel.tline[i][1]][0]);
				//Now use value of t found to generate the Y ordinate
				clipped[1][1]=(thistunnel.eyevertex[thistunnel.tline[i][1]][1]+(t*(thistunnel.eyevertex[thistunnel.tline[i][0]][1]-thistunnel.eyevertex[thistunnel.tline[i][1]][1])>>10));
				clipped[1][0]=XPIXELS-1;
			}
			else
			if(thistunnel.eyevertex[thistunnel.tline[i][1]][0]<0)
			{
			//Notice we need no test here to find if they are off the screen in the same direction, as we have already done that above
				t=((0-thistunnel.eyevertex[thistunnel.tline[i][1]][0])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][0]][0]-thistunnel.eyevertex[thistunnel.tline[i][1]][0]);
				//Now use value of t found to generate the Y ordinate
				clipped[1][1]=(thistunnel.eyevertex[thistunnel.tline[i][1]][1]+(t*(thistunnel.eyevertex[thistunnel.tline[i][0]][1]-thistunnel.eyevertex[thistunnel.tline[i][1]][1])>>10));
				clipped[1][0]=0;
			}

			if(clipped[1][1]>=YPIXELS)
			//Has the clipping produced a value on-screen? If not, try clipping to the bottom boundary instead
			{
				t=((YPIXELS-thistunnel.eyevertex[thistunnel.tline[i][1]][1])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][0]][1]-thistunnel.eyevertex[thistunnel.tline[i][1]][1]);
				//Now use value of t found to generate the X ordinate
				clipped[1][0]=(thistunnel.eyevertex[thistunnel.tline[i][1]][0]+(t*(thistunnel.eyevertex[thistunnel.tline[i][0]][0]-thistunnel.eyevertex[thistunnel.tline[i][1]][0])>>10));
				clipped[1][1]=YPIXELS-1;
			}
			else
			if(clipped[1][1]<0)
			//Testing for the possibility of above the screen
			{
				t=((0-thistunnel.eyevertex[thistunnel.tline[i][1]][1])<<10)/(thistunnel.eyevertex[thistunnel.tline[i][0]][1]-thistunnel.eyevertex[thistunnel.tline[i][1]][1]);
				//Now use value of t found to generate the X ordinate
				clipped[1][0]=(thistunnel.eyevertex[thistunnel.tline[i][1]][0]+(t*(thistunnel.eyevertex[thistunnel.tline[i][0]][0]-thistunnel.eyevertex[thistunnel.tline[i][1]][0])>>10));
				clipped[1][1]=0;
			}
			//ham_PutLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],16000);	//16 Bit version
			//ham_PutLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],1);
			BLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],1);
		}
	}
}

void drawenemies()
{
	int i,j;
	int clipped[2][2];	//Will store the X & Y ordinates of the start and end points of the line.
	int t;

	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{
			for(i=0;i<model[ship[j].model].numlines;i++)
			{
				//In the event of a clip not being required, these values still need to be in the clipped array so the draw command will pick them up
				clipped[0][0]=ship[j].eyevertex[model[ship[j].model].mline[i][0]][0];
				clipped[0][1]=ship[j].eyevertex[model[ship[j].model].mline[i][0]][1];
				clipped[1][0]=ship[j].eyevertex[model[ship[j].model].mline[i][1]][0];
				clipped[1][1]=ship[j].eyevertex[model[ship[j].model].mline[i][1]][1];

				//Notice that the screen coordinates range from 0 to XPIXELS-1. Therefore, greater than OR equal to XPIXELS is off the screen and needs to be clipped
				if(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]>=XPIXELS)
				//if off right side..
				{
					if(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]<XPIXELS)	//Only on the screen if LESS than XPIXELS
					//If this is false, both points lie outside the viewspace in the same direction and therefore CANNOT intersect the viewspace. ie. No drawing necessary.
					//The above test is not necessary, but it does eliminate some of the lines completely outside the screen very quickly
					{
						t=((XPIXELS-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]);
						//Now use value of t found to generate the Y ordinate
						clipped[0][1]=(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1])>>10));
						clipped[0][0]=XPIXELS-1;
					}
				}
				else
				if(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]<0)
				//else, if off left side..
				{
					if(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]>=0)
					//If this is false, both points lie outside the viewspace in the same direction and therefore CANNOT intersect the viewspace. ie. No drawing necessary.
					//The above test is not necessary, but it does eliminate some of the lines completely outside the screen very quickly
					{
						t=((0-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]);
						//Now use value of t found to generate the Y ordinate
						clipped[0][1]=(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1])>>10));
						clipped[0][0]=0;
					}
				}

				if(clipped[0][1]>=YPIXELS)
				//Has the clipping produced a value on-screen? If not, try clipping the bottom boundary instead
				{
					t=((YPIXELS-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]);
					//Now use value of t found to generate the X ordinate
					clipped[0][0]=(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0])>>10));
					clipped[0][1]=YPIXELS-1;
				}
				else
				if(clipped[0][1]<0)
				//Testing for the possibility of above the screen
				{
					t=((0-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]);
					//Now use value of t found to generate the X ordinate
					clipped[0][0]=(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][0]][0])>>10));
					clipped[0][1]=0;
				}

				if(clipped[0][0]>=0 && clipped[0][0]<XPIXELS && clipped[0][1]>=0 && clipped[0][1]<YPIXELS)
				{
				// Now perform clipping on the other endpoint. Notice that if the above clause is false, our line lies completely
				//outside the viewspace and therefore we don't need to proceed with this clipping or the drawing
					if(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]>=XPIXELS)
					{
					//Notice we need no test here to find if they are off the screen in the same direction, as we have already done that above
						t=((XPIXELS-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]);
						//Now use value of t found to generate the Y ordinate
						clipped[1][1]=(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1])>>10));
						clipped[1][0]=XPIXELS-1;
					}
					else
					if(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]<0)
					{
					//Notice we need no test here to find if they are off the screen in the same direction, as we have already done that above
						t=((0-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]);
						//Now use value of t found to generate the Y ordinate
						clipped[1][1]=(ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1])>>10));
						clipped[1][0]=0;
					}

					if(clipped[1][1]>=YPIXELS)
					//Has the clipping produced a value on-screen? If not, try clipping the bottom boundary instead
					{
						t=((YPIXELS-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]);
						//Now use value of t found to generate the X ordinate
						clipped[1][0]=(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0])>>10));
						clipped[1][1]=YPIXELS-1;
					}
					else
					if(clipped[1][1]<0)
					//Testing for the possibility of above the screen
					{
						t=((0-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1])<<10)/(ship[j].eyevertex[model[ship[j].model].mline[i][0]][1]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][1]);
						//Now use value of t found to generate the X ordinate
						clipped[1][0]=(ship[j].eyevertex[model[ship[j].model].mline[i][1]][0]+(t*(ship[j].eyevertex[model[ship[j].model].mline[i][0]][0]-ship[j].eyevertex[model[ship[j].model].mline[i][1]][0])>>10));
						clipped[1][1]=0;
					}
					//ham_PutLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],16000);	//16 Bit version
					//ham_PutLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],1);
					BLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],1);
				}
			}
		}
	}
}

void precalcspatial(Tunnel t)
{
	u16 quadrantadd;
	s16 dx,dy;
	u16 m,adx,ady;
	u8 i;
	for(i=0;i<t.numpoints;i++)
	{
		if(i<(t.numpoints-1))
		{
			dx=t.crossec[i+1].x-t.crossec[i].x;
			dy=t.crossec[i+1].y-t.crossec[i].y;
		}
		else	//This clause accounts for wrap around
		{
			dx=t.crossec[0].x-t.crossec[i].x;
			dy=t.crossec[0].y-t.crossec[i].y;
		}

		if(dx==0)
		{
			if(dy>0)
				spatialtransform[i*3].rollangle=90;
			else
				spatialtransform[i*3].rollangle=270;
		}
		else
		{
			if(dy==0)
			{
				if(dx>0)
					spatialtransform[i*3].rollangle=0;
				else
					spatialtransform[i*3].rollangle=180;
			}
		}
		
		if((dx!=0)&&(dy!=0))
		{
			if(dx<0 && dy>0)
			{
				quadrantadd=90;
			}
			else
				if(dx<0 && dy<0)
					quadrantadd=180;
				else
					if(dx>0 && dy<0)
					{
						quadrantadd=270;
					}
					else
						quadrantadd=0;
			
			adx=abs(dx);
			ady=abs(dy);

			m=(ady<<6)/adx;
			switch(quadrantadd)
			{
			case 0:
			case 180:
				if(adx>ady)
				{
					m=(ady<<6)/adx;
					spatialtransform[i*3].rollangle=itanlook[m]+quadrantadd;
				}
				else
				{
					if(ady>adx)
					{
						m=(adx<<6)/ady;
						spatialtransform[i*3].rollangle=90+quadrantadd-(itanlook[m]);
					}
					else
						spatialtransform[i*3].rollangle=45+quadrantadd;
				}

				break;
			case 90:
			case 270:
				if(adx>ady)
				{
					m=(ady<<6)/adx;
					spatialtransform[i*3].rollangle=90+quadrantadd-(itanlook[m]);
				}
				else
				{
					if(ady>adx)
					{
						m=(adx<<6)/ady;
						spatialtransform[i*3].rollangle=itanlook[m]+quadrantadd;
					}
					else
						spatialtransform[i*3].rollangle=45+quadrantadd;
				}

				break;
			}

		//Now calculated the angle using the gradient of the vector

		spatialtransform[i*3].x=t.crossec[i].x+(dx>>1);
		spatialtransform[i*3].y=t.crossec[i].y+(dy>>1);
		/*Adding half the vector from this point to the next, we
		place ourselves at the centre of the polygon*/
		}
	}
}

void interpolate(Tunnel t)
{
	u8 i;
	s16 dx, dy, da;
	
	for(i=0;i<t.numpoints*3-3;i+=3)
	{	dx=spatialtransform[i+3].x-spatialtransform[i].x;
		dy=spatialtransform[i+3].y-spatialtransform[i].y;
		da=spatialtransform[i+3].rollangle-spatialtransform[i].rollangle;

		spatialtransform[i+1].x=spatialtransform[i].x+(dx/3);
		spatialtransform[i+1].y=spatialtransform[i].y+(dy/3);
		spatialtransform[i+1].rollangle=spatialtransform[i].rollangle+(da/3);

		spatialtransform[i+2].x=spatialtransform[i].x+((dx/3)*2);
		spatialtransform[i+2].y=spatialtransform[i].y+((dy/3)*2);
		spatialtransform[i+2].rollangle=spatialtransform[i].rollangle+((da/3)*2);
	}
		dx=spatialtransform[0].x-spatialtransform[t.numpoints*3-3].x;
		dy=spatialtransform[0].y-spatialtransform[t.numpoints*3-3].y;
		da=spatialtransform[0].rollangle-spatialtransform[t.numpoints*3-3].rollangle;

		spatialtransform[t.numpoints*3-2].x=spatialtransform[t.numpoints*3-3].x+(dx/3);
		spatialtransform[t.numpoints*3-2].y=spatialtransform[t.numpoints*3-3].y+(dy/3);
		spatialtransform[t.numpoints*3-2].rollangle=spatialtransform[t.numpoints*3-3].rollangle+(da/3);

		spatialtransform[t.numpoints*3-1].x=spatialtransform[t.numpoints*3-3].x+((dx/3)*2);
		spatialtransform[t.numpoints*3-1].y=spatialtransform[t.numpoints*3-3].y+((dy/3)*2);
		spatialtransform[t.numpoints*3-1].rollangle=spatialtransform[t.numpoints*3-3].rollangle+((da/3)*2);
		/*FIXME: Something is wrong with the angle calculation here. The algorithm always moves 
		towards the target angle without taking into account that it can sometimes wrap around in
		the other direction to get there faster. If abs(delta angle) > 180, it is faster to move the 
		other way and wrap around*/
}

void precalcview(Tunnel t)
{
	u8 i;
	s32 largestx=-1, largesty=-1;

	for(i=0;i<t.numpoints*3;i++)
	{
		if(abs(spatialtransform[i].x)>largestx)
			largestx=abs(spatialtransform[i].x);
		if(abs(spatialtransform[i].y)>largesty)
			largesty=abs(spatialtransform[i].y);
	}
	for(i=0;i<t.numpoints*3;i++)
	{
		viewtransform[i].yaw=-(((spatialtransform[i].x<<6)/largestx)*MAXYAW)>>6;
		viewtransform[i].pitch=-((((spatialtransform[i].y<<6)/largesty)*MAXPITCH)>>6);
		if(viewtransform[i].yaw<0)	//We have to have positive angles
			viewtransform[i].yaw+=360;
		if(viewtransform[i].pitch<0)
			viewtransform[i].pitch+=360;
		//We now have angles by which to rotate the tunnel (the Viewtransformation) for each position on the tunnel,
		//so when we move, the tunnel moves, too
		viewtransform[i].xoffset=-(((spatialtransform[i].x<<6)/largestx)*MAXXOFFSET)>>6;
		viewtransform[i].yoffset=-((((spatialtransform[i].y<<6)/largesty)*MAXYOFFSET)>>6);
		//Now have offset values to keep the ship in view when we zoom in. The values stored are the maximum.
		//The actual value used is reduced depending on the zooming in the Mainloop function
	}
}

void expireandcreateenemies(void)
{
	u8 i;
	u8 randomnum;

	//Destroy all ships which have reached the near end
	for(i=1;i<MAXSHIPS;i++)	//Remeber that WE are the first ship, and handled separately, hence start from '1' here
	{
		if(ship[i].valid)
		{
			if(ship[i].depth<-10000)
				ship[i].valid=0;
		}
	}

	//Now randomly create new ships
	i=0;
	randomnum=Rand()%10;

	if(randomnum==2)	//Create an object if number is this value
	{
		while((ship[++i].valid)&&(i<MAXSHIPS));	//Looking for an unused enemy slot
		if(i<MAXSHIPS)
		{
			ship[i].valid=1;
			ship[i].depth=10000;
			ship[i].polygon=((Rand()%thistunnel.numpoints)*3);
			//Notice we calculate modulus of "numpoints" then multiply by 3, rather than other way round, to ensure in middle of polygon
			ship[i].desiredposition=ship[i].polygon;
			ship[i].model=0;
			ship[i].speed=200;
			ship[i].lastpress=0;
		}
	}
}

void expireandcreateshots()
{
	u8 i;
	static u8 lastshot=MAXSHIPS;	//Contains index of last shot fired. Used to check if it's okay to fire again
	
	for(i=MAXSHIPS; i<MAXSHIPS+MAXSHOTS; i++)
	{
		if(ship[i].valid)
		{
			if(ship[i].depth>10000)
				ship[i].valid=0;
		}
	}

	if(F_CTRLINPUT_A_PRESSED && (ship[0].polygon%3==0))
	{
		i=MAXSHIPS-1;

		while((ship[++i].valid)&&(i<MAXSHIPS+MAXSHOTS));
		if(i<MAXSHIPS+MAXSHOTS)
		//Though it looks like we're doing an unnecessary test here, we need to know if we found a free element, or have used them all.
		//This test will be false if we exited While because we ran out of elements

		//Not only must we have some shots left, but they must also be a certain distance apart. We can fire only when prev shot fired is not valid,
		//or is more than a certain distance away, thus:
		{

			if((ship[lastshot].valid==0)||(ship[lastshot].depth>-8500))
			{
			//Last shot fired has either become invalid or is far enough away to allow us to fire again
				ship[i].valid=1;
				ship[i].depth=-10000;
				ship[i].polygon=ship[0].polygon;
				ship[i].desiredposition=ship[0].polygon;
				ship[i].model=2;
				ship[i].speed=350;
				ship[i].lastpress=0;
				lastshot=i;	//Update this to be the last shot fired
			}
		}
	}
}

void moveanddestroyshots()
{
	u8 shot,enemy;
	s8 currentclosestref=-1;
	s16 currentclosestdepth=0;
	//Assigning a value prevents a compiler warning, though it shouldn't strictly make a difference
	//if initialised or not because of the way we use this variable (innermost If causes warning)


	for(shot=MAXSHIPS; shot<MAXSHIPS+MAXSHOTS; shot++)
	{
		if(ship[shot].valid)
		{
			ship[shot].depth+=ship[shot].speed;
			for(enemy=1; enemy<MAXSHIPS; enemy++)
			{
				if(ship[enemy].valid)
				{
					if(ship[shot].polygon==ship[enemy].polygon)	//Comparing tunnel polygon pos of enemy and shot
					{
						//Depths are negative at nearside, positive at far
						if(abs(ship[enemy].depth-ship[shot].depth)<(ship[shot].speed+ship[enemy].speed))
						{
							if((currentclosestref==-1)||(currentclosestdepth>ship[enemy].depth))	//Haven't found any enemies hit yet
							{
								currentclosestref=enemy;
								currentclosestdepth=ship[enemy].depth;
							}
						}
					}
				}
			}
			if(currentclosestref!=-1)	//If -1, we didn't hit anything
			{
				ship[shot].valid=0;
				ship[currentclosestref].valid=0;
			}
			currentclosestref=-1;
		}
	}
}

void moveenemies()
{
	u8 i, randomnum=0;

	for(i=1;i<MAXSHIPS;i++)
	{

		//Skip enemies randomly between polygons
		if(ship[i].valid)
		{

			ship[i].depth-=ship[i].speed;	//Move ship up the tunnel

			randomnum=(Rand()%200);		//COMMENT this line out to make enemies stay on same polygon
			if(randomnum==2)
			{
				ship[i].lastpress=-1;
				if(ship[i].desiredposition==0)
					ship[i].desiredposition=thistunnel.numpoints*3-3;
				else
					ship[i].desiredposition-=3;
			}
			else
				if(randomnum==3)
				{
					ship[i].lastpress=1;
					if(ship[i].desiredposition==thistunnel.numpoints*3-3)
						ship[i].desiredposition=0;

					else
						ship[i].desiredposition+=3;
				}
		}
	}
}


inline void SRand(int r)
{ 
	_rand_rndseed=r;
} 

inline int Rand(void)
{
	if(_rand_rndseed&1)
	{
	_rand_rndseed >>= 1;
	_rand_rndseed ^= 0xb400;
	}
	else
		{
		_rand_rndseed >>= 1;
		}
	return(_rand_rndseed);
} 


void BLine(int x0, int y0, int x1, int y1, unsigned char color)
{
	int dx, dy, x_inc, y_inc, error=0, index, x, y;

	u16* vidMem;
	x = x0;
	y = y0;

	dx = x1-x0;
	dy = y1-y0;

	if (dx>0)
	{
		x_inc = 1;
	}
	else
	{
		x_inc = -1;
		dx = -dx;
	}

	if (dy>0)
	{
		y_inc = 1;
	}
	else
	{
		y_inc = -1;
		dy = -dy;
	}

	if(dx>dy)
	{

		for(index=0; index<=dx; index++)
		{
			vidMem = &VideoBuffer[ ((y*240) + x)>>1 ];
			if(x & 0x0001)//if(x % 2)	//Bitwise operation is probably faster
			*vidMem = (*vidMem & 0x00FF) | (color<<8);
			else
			*vidMem = (*vidMem & 0xFF00) | color;

			error+=dy;

			if(error>dx)
			{
				error-=dx;
				y+=y_inc;
			}
			x+=x_inc;

		}
	}
	else 
	{
		if(dy!=0)
		//If clause prevents function being used to draw single points
		{
			for(index=0; index <=dy; index++)
			{

				vidMem = &VideoBuffer[ ((y*240) + x)>>1 ];

				if(x & 0x0001)//if(x % 2)	//Bitwise operation is probably faster
				*vidMem = (*vidMem & 0x00FF) | (color<<8);
				else
				*vidMem = (*vidMem & 0xFF00) | color;

				error+=dx;

				if(error>0)
				{
					error -= dy;
					x+=x_inc;
				}
				y+=y_inc;
			}
		}
	}

}

void changebuffer()
{
	if(ham_GetBGBuffer()==0)
	{
		VideoBuffer=BACKBUFFER;
	}
	else
	{
		VideoBuffer=FRONTBUFFER;
	}
}
