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

#include "mygba.h"
#include "stdio.h"

MULTIBOOT

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

#define MEM_FUNC_IN_IWRAM __attribute__ ((section (".iwram"), long_call))

#define RELEASE	//Disables printing, because it doesn't work on actual hardware
//#define INDESTRUCTIBLE
//#define FASTDRAW

#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 MAXTRIS 8
#define MAXQUADS 6
#define MINZOOM 13500
#define MAXZOOM 16000
#define FRAMESKIP 2
#define ENEMYMODEL 1
#define SHOTMODEL 3
#define NUMMODELS 4
#define NUMLIVES 3

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 numtris;
	u8 numquads;
	int mvertex[MAXVERTS][3];
	int collision;
	u8 mtri[MAXTRIS][3];
	u8 mquad[MAXQUADS][4];
	u8 numlines;
	u8 mline[MAXLINES][2];		//References to vertices
	int mnormalt[MAXTRIS][3];	//A vector
	int mnormalq[MAXQUADS][3];
}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];
	u8 visible[MAXLINES];		//Flags that indicate if each line is visible
	int viewnormalt[MAXTRIS][3];
	int viewnormalq[MAXQUADS][3];
}Ship;

typedef struct
{
	u8 numverts;
	s8 vertex[5][2];
}Character;

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) MEM_FUNC_IN_IWRAM;

void screendraw(void);

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

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 printscreen(char *string, int x, int y);

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 destroythings();

void precalcnormals();

void precalclines();

void findvisible();

void moveenemies();

void changebuffer();

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

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

Character character[]=
{
	{
		5,
		{
			{-4, 4},
			{4, 4},
			{4,-4},
			{-4,-4},
			{-4,4}		}
	},
	
	{
		3,
		{
			{0,4},
			{0,-4},
			{-1,-3}		}
	},
	
	{
		4,
		{
			{4,4},
			{-4,4},
			{4,-4},
			{-4,-4}		}
	},
	
	{
		5,
		{
			{-3,4},
			{3,2},
			{-3,0},
			{3,-2},
			{-3,-4}		}
	},

	{
		4,
		{
			{0,4},
			{0,-4},
			{-4,0},
			{4,0}		}
	},

	{
		5,
		{
			{-4,4},
			{0,4},
			{4,1},
			{-4,-4},
			{4,-4}		}
	},
	
	{
		5,
		{
			{0,-4},
			{-4,0},
			{0,4},
			{4,0},
			{-4,0}		}
	},
	
	{
		3,
		{
			{0,4},
			{4,-4},
			{-1,-3}		}
	},
	
	{
		5,
		{
			{-4,4},
			{4,4},
			{-4,-4},
			{4,-4},
			{-4,4}		}
	},
	
	{
		5,
		{
			{4,4},
			{4,-4},
			{-4,-4},
			{-4,0},
			{4,0}		}
	}
};
	//Now defining the models we'll be using
Model model[NUMMODELS]=
{
	{	//Our ship
		5,	//Numpoints
		4,	//Numtris
		1,	//Numquads
		{
			{0, -500, -1900},
			{-1000, -1000, 0},
			{-1000, 0, 0},
			{1000, 0, 0},
			{1000, -1000, 0}	},
		1900,	// Collision distance
		{
			{0, 2, 1},	//Tris
			{0, 1, 4},
			{0, 4, 3},
			{0, 3, 2}	},
		{
			{1, 2, 3, 4}	//Quads
					}
	},

	{	// Enemy (cube)
		8,
		0,
		6,
		{
			{-1000, 0, 1000},
			{1000, 0, 1000},
			{1000, 2000, 1000},
			{-1000, 2000, 1000},
			{-1000, 0, -1000},
			{1000, 0, -1000},
			{1000, 2000, -1000},
			{-1000, 2000, -1000}	},
		1000,
		{	},
		{
			{3, 2, 1, 0},
			{2, 6, 5, 1},
			{6, 7, 4, 5},
			{7, 3, 0, 4},
			{7, 6, 2, 3},
			{1, 5, 4, 0}	}
	},
	
	{	// Enemy (pyramid)
		5,
		4,
		1,
		{
			{-1000, 0, -1000},
			{1000, 0, -1000},
			{0, -1500, 0},
			{1000, 0, 1000},
			{-1000, 0, 1000}	},
		1000,
		{
			{2, 1, 0},
			{2, 3, 1},
			{2, 4, 3},
			{2, 0, 4}	},
		{
			{1, 3, 4, 0}	}
	},


	{	//Shot
		6,
		8,
		0,
		{
			{0, -500, 0},
			{-500, -500, -1500},
			{0, -1000, -1500},
			{500, -500, -1500},
			{0, 0, -1500},
			{0, -500, -2300}	},
		2300,
		{
			{2, 1, 0},
			{0, 1, 4},
			{3, 0, 4},
			{3, 2, 0},
			{2, 3, 5},
			{3, 4, 5},
			{1, 2, 5},
			{4, 1, 5}	}

	}

};

int yaw=0;
int pitch=0;
int frameready=0;
int dolly=MAXZOOM;
int _rand_rndseed = 1;
int score=0;
int lives=NUMLIVES;
char alives[2];	//Need an extra character for the trailing NULL
char ascore[8];
u16 *VideoBuffer;
int xoffset, yoffset;

#ifndef RELEASE
TOOL_PROFILER_INIT
#endif
int main()
{
	int i;

	ham_Init();
	ham_SetBgMode(4);
	ham_SetBgPalCol(0, COLOR_BLACK);
	ham_SetBgPalCol(1, COLOR_WHITE);
	ham_SetBgPalCol(2, COLOR_WHITE);//These are separate palette entries to allow strobing of ships but not tunnel when destroyed

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

	extrudetunnel(&tunnelcs, &thistunnel);
	precalcspatial(tunnelcs);
	interpolate(tunnelcs);
	precalcview(tunnelcs);
	precalcnormals();
	precalclines();
	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;
	}
	sprintf(ascore,"%d", score);
	sprintf(alives,"%d",lives);

	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;
	static int fade=50;
	int zoomfraction;

	rotatetunnel();
	perstrantunnel();

	if(ship[0].valid)
	{
		expireandcreateenemies();
		moveenemies();
		expireandcreateshots();
		destroythings();

		// 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);
		yaw=viewtransform[ship[0].polygon].yaw;
		pitch=viewtransform[ship[0].polygon].pitch;

	}
	else	//We've been destroyed. Check if we have lives left and behave appropriately
	{

		if(fade>0)
		{
			ham_FadePalCol(1,5);
			fade--;
		}
		else
		{
			if(lives>0)
			{
				fade=50;
				ship[0].valid=1;
				ham_SetBgPalCol(1, COLOR_WHITE);
				for(j=1;j<MAXSHIPS+MAXSHOTS;j++)
					ship[j].valid=0;
				lives--;
				sprintf(alives,"%d",lives);
			}
			else
			{
				//Keep strobing the colours until a button is pressed
				if(!(F_CTRLINPUT_A_PRESSED||F_CTRLINPUT_B_PRESSED||F_CTRLINPUT_START_PRESSED||F_CTRLINPUT_SELECT_PRESSED))
					ham_FadePalCol(1,5);
				else
				{
					//Once a button has been pressed, reset everything
					fade=50;
					ship[0].valid=1;
					ship[0].polygon=21;
					ship[0].desiredposition=21;
					ship[0].depth=-10000;
					ship[0].speed=0;
					ship[0].model=0;
					ship[0].lastpress=0;
					ham_SetBgPalCol(1, COLOR_WHITE);
					for(j=1;j<MAXSHIPS+MAXSHOTS;j++)
						ship[j].valid=0;
					score=0;
					sprintf(ascore,"%d",score);
					lives=NUMLIVES;
					sprintf(alives,"%d",lives);
				}
			}
		}
	}

	transformenemies();
	findvisible();
	perstranenemies();

	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)*/
#ifndef RELEASE
	TOOL_PROFILER_START
#endif
	screendraw();
	drawenemies();

	printscreen(ascore, 30, 145);
	printscreen(alives, 200, 145);
#ifndef RELEASE
	TOOL_PROFILER_END
#endif
	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) && ship[0].valid)
		{
			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*/
}

MEM_FUNC_IN_IWRAM void rotatetunnel()
{
	int i, tempz;
	for(i=0;i<(thistunnel.numpoints)*2;i++)
	{
		thistunnel.viewvertex[i][1]=(thistunnel.tvertex[i][1]*coslook[pitch]-thistunnel.tvertex[i][2]*sinlook[pitch])>>12;	//>>12 Equivalent to /4096
		tempz=(thistunnel.tvertex[i][1]*sinlook[pitch]+thistunnel.tvertex[i][2]*coslook[pitch])>>12;

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

MEM_FUNC_IN_IWRAM void transformenemies()
{
	int i, j, tempz;

	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{

			for(i=0;i<(model[ship[j].model].numpoints);i++)
			{
			
			//Rotate by polygon's orientation. Translate by polygon's position, and enemy's depth
				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)+spatialtransform[ship[j].polygon].x;
				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)+spatialtransform[ship[j].polygon].y;
				ship[j].viewvertex[i][2]=model[ship[j].model].mvertex[i][2]-ship[j].depth;

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

				ship[j].viewvertex[i][0]=(ship[j].viewvertex[i][0]*coslook[yaw]+tempz*sinlook[yaw])>>12;
				ship[j].viewvertex[i][2]=((ship[j].viewvertex[i][0]*(-sinlook[yaw])+tempz*coslook[yaw])>>12)-dolly;
			}
			for(i=0;i<(model[ship[j].model].numtris);i++)
			{
			/*Rotate each normal by the spatialtransform.*/

			//Rotate by polygon's orientation. Notice that the translations are not required (only orientation is important)
				ship[j].viewnormalt[i][0]=(model[ship[j].model].mnormalt[i][0]*coslook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mnormalt[i][1]*(-sinlook[spatialtransform[ship[j].polygon].rollangle]))>>12;
				ship[j].viewnormalt[i][1]=(model[ship[j].model].mnormalt[i][0]*sinlook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mnormalt[i][1]*coslook[spatialtransform[ship[j].polygon].rollangle])>>12;

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

				ship[j].viewnormalt[i][0]=(ship[j].viewnormalt[i][0]*coslook[yaw]+tempz*sinlook[yaw])>>12;
				ship[j].viewnormalt[i][2]=(ship[j].viewnormalt[i][0]*(-sinlook[yaw])+tempz*coslook[yaw])>>12;
			}
				//Now do quads
			for(i=0;i<(model[ship[j].model].numquads);i++)
			{
			/*Rotate each normal by the spatialtransform.*/

			//Rotate by polygon's orientation. Notice that the translations are not required (only orientation is important)
				ship[j].viewnormalq[i][0]=(model[ship[j].model].mnormalq[i][0]*coslook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mnormalq[i][1]*(-sinlook[spatialtransform[ship[j].polygon].rollangle]))>>12;
				ship[j].viewnormalq[i][1]=(model[ship[j].model].mnormalq[i][0]*sinlook[spatialtransform[ship[j].polygon].rollangle]+model[ship[j].model].mnormalq[i][1]*coslook[spatialtransform[ship[j].polygon].rollangle])>>12;

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

				ship[j].viewnormalq[i][0]=(ship[j].viewnormalq[i][0]*coslook[yaw]+tempz*sinlook[yaw])>>12;
				ship[j].viewnormalq[i][2]=(ship[j].viewnormalq[i][0]*(-sinlook[yaw])+tempz*coslook[yaw])>>12;

			}

		}
	}
}

void findvisible()
{
	int i,j;
	//Take each valid ship and find which of its polygons are visible
	for(j=0;j<MAXSHIPS+MAXSHOTS;j++)
	{
		if(ship[j].valid)
		{
			for(i=0;i<MAXLINES;i++)
				ship[j].visible[i]=0;

			for(i=0;i<(model[ship[j].model].numtris);i++)
			{
				if(((ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mtri[i][0]][0]][0]*ship[j].viewnormalt[i][0])+(ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mtri[i][0]][0]][1]*ship[j].viewnormalt[i][1])+(((ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mtri[i][0]][0]][2]))*ship[j].viewnormalt[i][2]))>0)
				{
					//If quad is visible, set its associated lines to be visible.
					//This finds the lines in each of the tris (as defined in "model") and makes visible if appropriate
					ship[j].visible[model[ship[j].model].mtri[i][0]]=1;
					ship[j].visible[model[ship[j].model].mtri[i][1]]=1;
					ship[j].visible[model[ship[j].model].mtri[i][2]]=1;
				}
			}
			for(i=0;i<(model[ship[j].model].numquads);i++)
			{
				if(((ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mquad[i][0]][0]][0]*ship[j].viewnormalq[i][0])+(ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mquad[i][0]][0]][1]*ship[j].viewnormalq[i][1])+(((ship[j].viewvertex[model[ship[j].model].mline[model[ship[j].model].mquad[i][0]][0]][2]))*ship[j].viewnormalq[i][2]))>0)
				{
					//If quad is visible, set its associated lines to be visible.
					//This finds the lines in each of the tris (as defined in "model") and makes visible if appropriate
					ship[j].visible[model[ship[j].model].mquad[i][0]]=1;
					ship[j].visible[model[ship[j].model].mquad[i][1]]=1;
					ship[j].visible[model[ship[j].model].mquad[i][2]]=1;
					ship[j].visible[model[ship[j].model].mquad[i][3]]=1;
				}
			}
		}
	}
}

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;
			}
			BLine(clipped[0][0],clipped[0][1],clipped[1][0],clipped[1][1],2);
		}
	}
}

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++)
			{
				if(ship[j].visible[i])	//Only draw line if visible
				{
				//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;
					}
					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);
}

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=(Rand()%2)+ENEMYMODEL;	//Pick an enemy model
			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=SHOTMODEL;
				ship[i].speed=350;
				ship[i].lastpress=0;
				lastshot=i;	//Update this to be the last shot fired
			}
		}
	}
}

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;
				}
		}
	}

	for(i=MAXSHIPS;i<MAXSHIPS+MAXSHOTS;i++)
	{
		if(ship[i].valid)
			ship[i].depth+=ship[i].speed;
	}
}

void destroythings()
{
	int i,shot;
	for(i=1;i<MAXSHIPS;i++)
	{
		//Compare each enemy against us
		if(ship[i].valid)
		{
#ifndef INDESTRUCTIBLE
			if(ship[i].polygon==ship[0].polygon)
			{
				if(ship[i].depth<(-10000+model[ship[0].model].collision+model[ship[i].model].collision))
				{
					ship[0].valid=0;	//We have been destroyed
					return;
				}
			}
#endif
		//Compare each enemy against valid shots
			for(shot=MAXSHIPS; shot<MAXSHIPS+MAXSHOTS; shot++)
			{
				if(ship[shot].valid)
				{
					if(ship[i].polygon==ship[shot].polygon)
					{
						//We have collided if the bounding distance of shot and ship overlap
						if(abs(ship[i].depth-ship[shot].depth)<(model[ship[i].model].collision+model[ship[shot].model].collision))
						{
							ship[i].valid=0;
							ship[shot].valid=0;
							score++;
							sprintf(ascore,"%d", score);
						}
					}
				}
			}
		}
	}
}

void precalcnormals()      /* This function sets the normal */
{
        int vector1[3],vector2[3];
	int i,j;

        for(i=0;i<NUMMODELS;i++)
	{
		for(j=0;j<model[i].numtris;j++)
		{
		/*Get vectors in the plane*/
			vector1[0]=model[i].mvertex[model[i].mtri[j][1]][0] - model[i].mvertex[model[i].mtri[j][0]][0];
			vector2[0]=model[i].mvertex[model[i].mtri[j][2]][0] - model[i].mvertex[model[i].mtri[j][0]][0];

			vector1[1]=model[i].mvertex[model[i].mtri[j][1]][1] - model[i].mvertex[model[i].mtri[j][0]][1];
			vector2[1]=model[i].mvertex[model[i].mtri[j][2]][1] - model[i].mvertex[model[i].mtri[j][0]][1];

			vector1[2]=model[i].mvertex[model[i].mtri[j][1]][2] - model[i].mvertex[model[i].mtri[j][0]][2];
			vector2[2]=model[i].mvertex[model[i].mtri[j][2]][2] - model[i].mvertex[model[i].mtri[j][0]][2];

			model[i].mnormalt[j][0]=(vector1[1]*vector2[2]-vector1[2]*vector2[1])>>12;
			model[i].mnormalt[j][1]=(vector1[2]*vector2[0]-vector1[0]*vector2[2])>>12;
			model[i].mnormalt[j][2]=(vector1[0]*vector2[1]-vector1[1]*vector2[0])>>12;
#ifndef RELEASE
			ham_VBAText("Tri point: 0 X: %d Y: %d Z: %d \n", model[i].mvertex[model[i].mtri[j][0]][0], model[i].mvertex[model[i].mtri[j][0]][1], model[i].mvertex[model[i].mtri[j][0]][2]);
			ham_VBAText("Tri point: 1 X: %d Y: %d Z: %d \n", model[i].mvertex[model[i].mtri[j][1]][0], model[i].mvertex[model[i].mtri[j][1]][1], model[i].mvertex[model[i].mtri[j][1]][2]);
			ham_VBAText("Tri point: 2 X: %d Y: %d Z: %d \n\n", model[i].mvertex[model[i].mtri[j][2]][0], model[i].mvertex[model[i].mtri[j][2]][1], model[i].mvertex[model[i].mtri[j][2]][2]);

			ham_VBAText("Normal: X: %d Y: %d Z: %d \n\n", model[i].mnormalt[j][0], model[i].mnormalt[j][1], model[i].mnormalt[j][2]);
#endif
		}

		for(j=0;j<model[i].numquads;j++)
		{
		/*Get vectors in the plane*/
			vector1[0]=model[i].mvertex[model[i].mquad[j][1]][0] - model[i].mvertex[model[i].mquad[j][0]][0];
			vector2[0]=model[i].mvertex[model[i].mquad[j][3]][0] - model[i].mvertex[model[i].mquad[j][0]][0];

			vector1[1]=model[i].mvertex[model[i].mquad[j][1]][1] - model[i].mvertex[model[i].mquad[j][0]][1];
			vector2[1]=model[i].mvertex[model[i].mquad[j][3]][1] - model[i].mvertex[model[i].mquad[j][0]][1];

			vector1[2]=model[i].mvertex[model[i].mquad[j][1]][2] - model[i].mvertex[model[i].mquad[j][0]][2];
			vector2[2]=model[i].mvertex[model[i].mquad[j][3]][2] - model[i].mvertex[model[i].mquad[j][0]][2];

			model[i].mnormalq[j][0]=(vector1[1]*vector2[2]-vector1[2]*vector2[1])>>12;
			model[i].mnormalq[j][1]=(vector1[2]*vector2[0]-vector1[0]*vector2[2])>>12;
			model[i].mnormalq[j][2]=(vector1[0]*vector2[1]-vector1[1]*vector2[0])>>12;
#ifndef RELEASE
			ham_VBAText("Quad point: 0 X: %d Y: %d Z: %d \n", model[i].mvertex[model[i].mquad[j][0]][0], model[i].mvertex[model[i].mquad[j][0]][1], model[i].mvertex[model[i].mquad[j][0]][2]);
			ham_VBAText("Quad point: 1 X: %d Y: %d Z: %d \n", model[i].mvertex[model[i].mquad[j][1]][0], model[i].mvertex[model[i].mquad[j][1]][1], model[i].mvertex[model[i].mquad[j][1]][2]);
			ham_VBAText("Quad point: 2 X: %d Y: %d Z: %d \n", model[i].mvertex[model[i].mquad[j][2]][0], model[i].mvertex[model[i].mquad[j][2]][1], model[i].mvertex[model[i].mquad[j][2]][2]);
			ham_VBAText("Quad point: 3 X: %d Y: %d Z: %d \n\n", model[i].mvertex[model[i].mquad[j][3]][0], model[i].mvertex[model[i].mquad[j][3]][1], model[i].mvertex[model[i].mquad[j][3]][2]);

			ham_VBAText("Normal: X: %d Y: %d Z: %d \n\n", model[i].mnormalq[j][0], model[i].mnormalq[j][1], model[i].mnormalq[j][2]);
#endif
		}
	}
}

void precalclines()
{
	//From the model definitions which refer to points, compute the lines, without repetition, that define all the polygons,
	//and use these to re-define the polygons in terms of these lines
	
	int modeli,trii,quadi,temp;
	int linei=0;
	for(modeli=0;modeli<NUMMODELS;modeli++)	//For each model
	{
		for(trii=0;trii<model[modeli].numtris;trii++)
		{
			while((!(((model[modeli].mline[linei][0]==model[modeli].mtri[trii][0])&&(model[modeli].mline[linei][1]==model[modeli].mtri[trii][1])) || ((model[modeli].mline[linei][1]==model[modeli].mtri[trii][0])&&(model[modeli].mline[linei][0]==model[modeli].mtri[trii][1])))) && linei<model[modeli].numlines )
			// For each pair of points, we go through all the lines currently found and see if any match. If they do, we assign this line's index to the tri
				linei++;
			if(linei<model[modeli].numlines)
			{
				temp=model[modeli].mtri[trii][0];
				model[modeli].mtri[trii][0]=linei;
			}
			//..otherwise, we need to create a new line and add it to the list
			else
			{
				temp=model[modeli].mtri[trii][0];
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mtri[trii][0];
				model[modeli].mline[model[modeli].numlines][1]=model[modeli].mtri[trii][1];
				model[modeli].mtri[trii][0]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;

			while((!(((model[modeli].mline[linei][0]==model[modeli].mtri[trii][1])&&(model[modeli].mline[linei][1]==model[modeli].mtri[trii][2])) || ((model[modeli].mline[linei][1]==model[modeli].mtri[trii][1])&&(model[modeli].mline[linei][0]==model[modeli].mtri[trii][2])))) && linei<model[modeli].numlines )
				linei++;
			if(linei<model[modeli].numlines)
			{
				model[modeli].mtri[trii][1]=linei;
			}
			else
			{
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mtri[trii][1];
				model[modeli].mline[model[modeli].numlines][1]=model[modeli].mtri[trii][2];
				model[modeli].mtri[trii][1]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;

			while((!(((model[modeli].mline[linei][0]==model[modeli].mtri[trii][2])&&(model[modeli].mline[linei][1]==temp)) || ((model[modeli].mline[linei][1]==model[modeli].mtri[trii][2])&&(model[modeli].mline[linei][0]==temp)))) && linei<model[modeli].numlines )
				linei++;
			if(linei<model[modeli].numlines)
			{
				model[modeli].mtri[trii][2]=linei;
			}
			else
			{
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mtri[trii][2];
				model[modeli].mline[model[modeli].numlines][1]=temp;
				model[modeli].mtri[trii][2]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;
		}
		for(quadi=0;quadi<model[modeli].numquads;quadi++)
		{
			while((!(((model[modeli].mline[linei][0]==model[modeli].mquad[quadi][0])&&(model[modeli].mline[linei][1]==model[modeli].mquad[quadi][1])) || ((model[modeli].mline[linei][1]==model[modeli].mquad[quadi][0])&&(model[modeli].mline[linei][0]==model[modeli].mquad[quadi][1])))) && linei<model[modeli].numlines )
			// For each pair of points, we go through all the lines currently found and see if any match. If they do, we assign this line's index to the quad
				linei++;
			if(linei<model[modeli].numlines)
			{
				temp=model[modeli].mquad[quadi][0];
				model[modeli].mquad[quadi][0]=linei;
			}
			//..otherwise, we need to create a new line and add it to the list
			else
			{
				temp=model[modeli].mquad[quadi][0];
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mquad[quadi][0];
				model[modeli].mline[model[modeli].numlines][1]=model[modeli].mquad[quadi][1];
				model[modeli].mquad[quadi][0]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;

			while((!(((model[modeli].mline[linei][0]==model[modeli].mquad[quadi][1])&&(model[modeli].mline[linei][1]==model[modeli].mquad[quadi][2])) || ((model[modeli].mline[linei][1]==model[modeli].mquad[quadi][1])&&(model[modeli].mline[linei][0]==model[modeli].mquad[quadi][2])))) && linei<model[modeli].numlines )
				linei++;
			if(linei<model[modeli].numlines)
			{
				model[modeli].mquad[quadi][1]=linei;
			}
			else
			{
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mquad[quadi][1];
				model[modeli].mline[model[modeli].numlines][1]=model[modeli].mquad[quadi][2];
				model[modeli].mquad[quadi][1]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;
			
			while((!(((model[modeli].mline[linei][0]==model[modeli].mquad[quadi][2])&&(model[modeli].mline[linei][1]==model[modeli].mquad[quadi][3])) || ((model[modeli].mline[linei][1]==model[modeli].mquad[quadi][2])&&(model[modeli].mline[linei][0]==model[modeli].mquad[quadi][3])))) && linei<model[modeli].numlines )
				linei++;
			if(linei<model[modeli].numlines)
			{
				model[modeli].mquad[quadi][2]=linei;
			}
			else
			{
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mquad[quadi][2];
				model[modeli].mline[model[modeli].numlines][1]=model[modeli].mquad[quadi][3];
				model[modeli].mquad[quadi][2]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;
			
			while((!(((model[modeli].mline[linei][0]==model[modeli].mquad[quadi][3])&&(model[modeli].mline[linei][1]==temp)) || ((model[modeli].mline[linei][1]==model[modeli].mquad[quadi][3])&&(model[modeli].mline[linei][0]==temp)))) && linei<model[modeli].numlines )
				linei++;
			if(linei<model[modeli].numlines)
			{
				model[modeli].mquad[quadi][3]=linei;
			}
			else
			{
				model[modeli].mline[model[modeli].numlines][0]=model[modeli].mquad[quadi][3];
				model[modeli].mline[model[modeli].numlines][1]=temp;
				model[modeli].mquad[quadi][3]=model[modeli].numlines;
				model[modeli].numlines++;
			}
			linei=0;
		}
	}
}

void printscreen(char *string, int x, int y)
{
	/* Passed a string which it then prints out at the passed position */
	int i, currentx, currenty;
	currentx=x;
	currenty=y;
	while(*string)	//While not the end of the string
	{
		switch(*string)
		{
			case '0':
				for(i=0; i<character[0].numverts-1; i++)
				{
					BLine(character[0].vertex[i][0]+currentx,character[0].vertex[i][1]+currenty,character[0].vertex[i+1][0]+currentx,character[0].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '1':
				for(i=0; i<character[1].numverts-1; i++)
				{
					BLine(character[1].vertex[i][0]+currentx,character[1].vertex[i][1]+currenty,character[1].vertex[i+1][0]+currentx,character[1].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '2':
				for(i=0; i<character[2].numverts-1; i++)
				{
					BLine(character[2].vertex[i][0]+currentx,character[2].vertex[i][1]+currenty,character[2].vertex[i+1][0]+currentx,character[2].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '3':
				for(i=0; i<character[3].numverts-1; i++)
				{
					BLine(character[3].vertex[i][0]+currentx,character[3].vertex[i][1]+currenty,character[3].vertex[i+1][0]+currentx,character[3].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '4':
				for(i=0; i<character[4].numverts-1; i++)
				{
					BLine(character[4].vertex[i][0]+currentx,character[4].vertex[i][1]+currenty,character[4].vertex[i+1][0]+currentx,character[4].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '5':
				for(i=0; i<character[5].numverts-1; i++)
				{
					BLine(character[5].vertex[i][0]+currentx,character[5].vertex[i][1]+currenty,character[5].vertex[i+1][0]+currentx,character[5].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '6':
				for(i=0; i<character[6].numverts-1; i++)
				{
					BLine(character[6].vertex[i][0]+currentx,character[6].vertex[i][1]+currenty,character[6].vertex[i+1][0]+currentx,character[6].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '7':
				for(i=0; i<character[7].numverts-1; i++)
				{
					BLine(character[7].vertex[i][0]+currentx,character[7].vertex[i][1]+currenty,character[7].vertex[i+1][0]+currentx,character[7].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '8':
				for(i=0; i<character[8].numverts-1; i++)
				{
					BLine(character[8].vertex[i][0]+currentx,character[8].vertex[i][1]+currenty,character[8].vertex[i+1][0]+currentx,character[8].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
			case '9':
				for(i=0; i<character[9].numverts-1; i++)
				{
					BLine(character[9].vertex[i][0]+currentx,character[9].vertex[i][1]+currenty,character[9].vertex[i+1][0]+currentx,character[9].vertex[i+1][1]+currenty,1);
				}
				currentx+=10;
				break;
		}
		string++;
	}
}

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);
} 

#define swap(a,b)           {a^=b; b^=a; a^=b;}

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

	u16* vidMem;

	if(x0>x1)
	/*The line needs to run left to right for the double pixel drawing to work. It also allows us to change X changes,
	which could otherwise be increments or decrements, into explicit increments, saving time*/
	{
		swap(x0,x1);
		swap(y0,y1);
	}

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

	x = x0;

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

	if(dx>dy)
	{
		
		vidMem = &VideoBuffer[ ((y0*240) + x)>>1 ];

		for(index=0; index<=dx-1; index++)
		{
			if(x & 0x0001)//if(x % 2)	//Bitwise operation is probably faster
			{
				*vidMem = (*vidMem & 0x00FF) | (color<<8);
				vidMem++;	//Because this is an odd pixel, we have to move to next address next time
				if(error>dx)
				{
					error-=dx;
					vidMem+=y_inc;
				}
			}
			else
			{
				if(error>dx)
				{
					*vidMem = (*vidMem & 0xFF00) | color;
					error-=dx;
					vidMem+=y_inc;
				}
				else	//Double!
				{
					*vidMem = color | color<<8;
#ifdef FASTDRAW
					x++;
					index++;
					error+=dy;
					vidMem++;
#endif
		// Defining FASTDRAW enables above code, speeding up drawing but producing a poorer visual result
				}
			}
			x++;
			error+=dy;
		}
		/*For the last point, we can't perform a double draw because we'd overshoot the intended end position. Therefore,
		the last point is handled separately here. Notice we are repeating code rather than carrying out a check for the last 
		pixel every time; it's faster this way!*/
		{
			if(x & 0x0001)
				*vidMem = (*vidMem & 0x00FF) | (color<<8);
			else
				*vidMem = (*vidMem & 0xFF00) | color;
		}

	}
	else
	{
		if(dy!=0)
		//If clause prevents function being used to draw single points
		{
			vidMem = &VideoBuffer[ ((y0*240) + x)>>1 ];

			for(index=0; index <=dy; index++)
			{

				if(x & 0x0001)
				{
					*vidMem = (*vidMem & 0x00FF) | (color<<8);
					error+=dx;

					if(error>0)
					{
						error -= dy;
						x++;
						vidMem++;
					}
				}
				else
				{
					*vidMem = (*vidMem & 0xFF00) | color;
					error+=dx;

					if(error>0)
					{
						error -= dy;
						x++;
					}
				}
				//Although the above code repeats, we save ourselves from having to check if X is odd twice

				vidMem+=y_inc;
			}
		}
	}

}


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

