Raycaster
SubmittedDecember 21, 2016
blue_knight
This is a fun little script that renders the current screens in first person using raycasting and column rendering.
X, Y is on the horizontal plane.
Z is up in 3D space.
This script has no set limits, except the map limit 16x8 screens.
/////////////////////////////////////////////////////
/////// Raycasting Engine Script, version 0.2 ///////
/////// by blue_knight						  ///////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
// This is a fun little script
// that renders the current screens in first person
// using raycasting and column rendering.
// X, Y is on the horizontal plane.
// Z is up in 3D space.
// This script has no set limits, except the map limit
// 16x8 screens.
/////////////////////////////////////////////////////
const int scrWidth  = 256;
const int scrHeight = 176;
ffc script Raycaster
{
	/////////////////////////////
	// startX     : start X position in virtual space.
	// startY     : start Y position in virtual space.
	// BackgroundTiles : Tile index for the background.
	/////////////////////////////
	void run(int startX, int startY, int BackgroundTiles)
	{
		//use virtual coordinates for Link's position.
		//since he is not displayed, we don't bother keeping local coordinates, so Link's position is forced to the center.
		float camPos_X = startX;
		float camPos_Y = startY;
		float camDir_X;
		float camDir_Y;
		float camYaw = 0;
		float moveSpeed = 1.0;
		float playerRadius = 16;
		//ginormous array...
		int map_combo[1024];	//holds the 32x32 area centered on the camera.
		int map_left_x=0;
		int map_left_y=0;
		int map_cenX = 0;
		int map_cenY = 0;
		int curScreen = Game->GetCurScreen();
		int curMap = Game->GetCurMap();
		while (true)
		{
			//update the movement.
			if ( Link->InputLeft )
				camYaw-=2;
			else if ( Link->InputRight )
				camYaw+=2;
			if (camYaw = 360 )
				camYaw -= 360;
			camDir_X = Cos(camYaw);
			camDir_Y = Sin(camYaw);
			float newPosX = camPos_X;
			float newPosY = camPos_Y;
			float travelDirX;
			float travelDirY;
			bool bCollisionNeeded = false;
			if ( Link->InputUp )
			{
				newPosX = newPosX + camDir_X*moveSpeed;
				newPosY = newPosY + camDir_Y*moveSpeed;
				bCollisionNeeded = true;
				travelDirX = camDir_X;
				travelDirY = camDir_Y;
			}
			else if ( Link->InputDown )
			{
				newPosX = newPosX - camDir_X*moveSpeed;
				newPosY = newPosY - camDir_Y*moveSpeed;
				bCollisionNeeded = true;
				travelDirX = -camDir_X;
				travelDirY = -camDir_Y;
			}
			//Super basic collision for now.
			//If we're intersecting a solid combo after moving, we revert the move.
			//note that we actually check ahead in the direction of travel so we don't get too close to
			//the wall.
			if ( bCollisionNeeded )
			{
				if ( ComboSolid( Floor(newPosX+travelDirX*playerRadius), Floor(newPosY+travelDirY*playerRadius), curMap, curScreen ) )
				{
					newPosX = camPos_X;
					newPosY = camPos_Y;
				}
			}
			camPos_X = newPosX;
			camPos_Y = newPosY;
			//Read the area around the camera into an array
			//whenever we change cells.
			//This way we don't have to call GetComboFromTileIndex() or Game->GetComboData()
			//up to 16 times per ray.
			//This improves our performance by 40% or more.
			int mx = camPos_X>>4;
			int my = camPos_Y>>4;
			if ( mx != map_cenX || my != map_cenY )
			{
				map_cenX = mx;
				map_cenY = my;
				map_left_x = Max(mx-16,0);
				map_left_y = Max(my-16,0);
				for (int y=0; y<32; y++)
				{
					int yy = y+map_left_y;
					for (int x=0; xX = 128;
		Link->Y = 88;
		Link->InputLeft = false;
		Link->InputRight = false;
		Link->InputUp = false;
		Link->InputDown = false;
		Link->InputA = false;
		Link->InputB = false;
		Link->InputL = false;
		Link->InputR = false;
		Link->InputStart = false;
		Link->InputEx1 = false;
		Link->InputEx2 = false;
		Link->InputEx3 = false;
		Link->InputEx4 = false;
	}
	bool ComboSolid(int x, int y, int map, int curScr)
	{
		bool solid = false;
		int cx = (x>>4)&15;
		int cy = (y>>4)%11;
		int scr = curScr;
		scr += ( x>>8 );
		scr += ( Floor(y/176)<GetComboSolid( map, scr, (cy<>4 );
		scr += ( Floor(ty/11)<GetComboData( map, scr, (cy<DrawTile(1, 0, 0, BackgroundTiles, 1, 11, 0, 256, 176, 0, 0, 0, 0, false, 128);
		//Note that we're pixel doubling for performance, later this should be an option
		//for people with fast computers.
		for (int x=0; x<scrWidth; x+=2)
		{
			int mx = mapX;
			int my = mapY;
			//compute the screen ray from the x position and camera paramters.
			float sx = (2 * x/scrWidth) - 1;
			float rayDirX = camDirX + planeX*sx;
			float rayDirY = camDirY + planeY*sx;
			//compute the DDA parameters, will detail in some documentation later.
			float rXX = rayDirX*rayDirX;
			float rYY = rayDirY*rayDirY;
			float deltaDistX = 32767;
			float deltaDistY = 32767;
			if ( rXX != 0 ) deltaDistX = Sqrt( 1.0 + rYY/rXX );
			if ( rYY != 0 ) deltaDistY = Sqrt( 1.0 + rXX/rYY );
			int stepX;
			int stepY;
			float sideDistX;
			float sideDistY;
			if ( rayDirX < 0 )
			{ stepX = -1; sideDistX = (camX - mx) * deltaDistX; }
			else
			{ stepX = 1; sideDistX = (mx + 1.0 - camX)*deltaDistX; }
			if ( rayDirY < 0 )
			{ stepY = -1; sideDistY = (camY - my)*deltaDistY; }
			else
			{ stepY = 1; sideDistY = (my + 1.0 - camY)*deltaDistY; }
			//now that we have the DDA parameters, actually walk the ray and find the closest intersecting wall.
			int hit = 0;
			int iter = 0;
			int side;
			while (hit == 0 && iter < MAX_STEPS)
			{
				if ( sideDistX < sideDistY )
				{
					sideDistX += deltaDistX;
					mx += stepX;
					side = 0;
					if ( mx 
					















