Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Или я вообще ничего не понял про устройство NeoGeo, или я не понимаю, почему Wolf3D не был портирован на него в 90-х

Дата публикации: 17-07-2026 20:50:12

Недавняя статья открыла для меня устройство столь упоро необычное и несущее столь любимый нами трудноуловимый флёр «балдёжности», существование которого раньше проходило мимо меня — NeoGeo. Как говорится, «это не первое моё родео» — на использовании 2D‑ускорителей в качестве 3D‑ускорителей я съел изрядного бобика. Читать далее

Основное содержимое страницы с новостью.

#include <conio.h>
#include <stdlib.h>
#include <math.h>
#include <fstream.h>
#include <stdio.h>	//Debug only

#include "LevelMap.h"

#define PI 3.1415926535897932384626433832795
#define max(a,b)  (((a) > (b)) ? (a) : (b))
#define min(a,b)  (((a) < (b)) ? (a) : (b))

static unsigned char __far TestWall[256][256];	//128x256 Doom wall, vert. scaled x2
static unsigned char __far TestCaco[64][64];	//64x64 Doom mob, 1:1
static unsigned char __far Frame[200][320];

static unsigned char __far ShiftedColumns00[16][256][16];	//16 vertical stripes of the original texture, 16x256 each. Comes in five different flavors: horizontal offser = 0, 4, 8 and 12, and blurry beyond recognition.
static unsigned char __far ShiftedColumns04[16][256][16];
static unsigned char __far ShiftedColumns08[16][256][16];
static unsigned char __far ShiftedColumns12[16][256][16];
static unsigned char __far ShiftedColumnsBl[16][256][16];

static unsigned char __far MobColumns[4][64][16];	//4 vertical stripes of the original texture, 16x64 each.

char const * SetPalette256 (void __far * Pal)
{
	unsigned long pval = (unsigned long)Pal;
	unsigned short Segm = pval>>16;
	unsigned short Offs = pval;
	Segm+=Offs>>4;
	Offs&=0x000F;

//	if (Offs > 15) return "Error! Paragraph address should never be inside next paragraphs";	╒╛╔АБ╝ Б╝ё╝, ГБ╝║К ╕═╚╝╒═БЛАО, ╛К А═╛╗ МБ╝ ё═Ю═╜Б╗Ю╝╒═╚╗ ╞═Ю╝╘ АБЮ╝╙ ╒КХ╔.
	__asm
	{
		mov dx,0x3c8
		mov al,0
		out dx,al	//Starting palette address = 0
		inc dx
		mov cx,768
		mov es, Segm
		mov si, Offs
RegL:		mov al,es:[si]	//and we DO load all 256 colors.
		inc si		//That's why we requre addresses loaded in the segment part
		shr al,1	//Don't forget VGA was 6-bit RGB!
		shr al,1	//Don't forget VGA was 6-bit RGB!
		out dx,al
		loop RegL
	}

	return NULL;
}

char const * Blt256 (void __far * Body)
{
	unsigned long pval = (unsigned long)Body;
	unsigned short Segm = pval>>16;
	unsigned short Offs = pval;
	Segm+=Offs>>4;
	Offs&=0x000F;

//	if (Offs > 15) return "Error! Paragraph address should never be inside next paragraphs";	╒╛╔АБ╝ Б╝ё╝, ГБ╝║К ╕═╚╝╒═БЛАО, ╛К А═╛╗ МБ╝ ё═Ю═╜Б╗Ю╝╒═╚╗ ╞═Ю╝╘ АБЮ╝╙ ╒КХ╔.
	__asm
	{
		push ds
		mov ds, Segm
		mov si, Offs
		mov ax, 0xA000
		mov es, ax
		mov di, 0
		mov cx, 32000
SR:		mov ax,ds:[si]
		inc si		//That's why we requre addresses loaded in the segment part
		inc si
		mov es:[di], ax
		inc di
		inc di
		loop SR
		pop ds
	}

	return NULL;
}

#pragma pack(push)
#pragma pack(0)
struct BMP256HEADER
{
	short Id;
	long FSize, Ver, DataOffset, Unused, Width, Height;
	short WTF, BPP;
	long Compr; //0 for uncompressed, 1,2 for RLE
	long DataSize,   XRes, YRes, PalSize, Unused2;
	char Palette[256][4];
};
#pragma pack(pop)

char const * LoadBMP256(char *Name, unsigned char Pal[256][3], unsigned char __far * ImgBody, int BodyArrayWidth, int MaxH)
{
	fstream f;
	unsigned long Line, W,H,W4, X;
	BMP256HEADER Head;

	f.open (Name, ios::in|ios::binary);
	f.read ((char*)&Head, sizeof (Head));
	if (f.fail()) return "Can't open file!";
	if (Head.Id!=0x4D42) return "Wrong BMP id! Not a BMP file?";
	if (Head.Compr) return "Compressed files are not supported for now!";
	if (Head.BPP!=8) return "Wrong bits per pixel! Expected to read 256-color image.";
//cout<<Head.PalSize<<endl;
	for (H=0; H<256; H++) {Pal[H][0]=Head.Palette[H][2];Pal[H][1]=Head.Palette[H][1];Pal[H][2]=Head.Palette[H][0];}

	H=Head.Height;
	W=Head.Width;
	if (!W||!H||W>BodyArrayWidth||H>MaxH) return ("Wrong image size!");
	W4=(W+3)&(~3);

	for (Line=0;Line<H;Line++)
	{
		f.seekg (Head.DataOffset+Line*W4, ios::beg);
		f.read (ImgBody+(H-Line-1)*BodyArrayWidth, W);
	}
	f.close();

	return NULL;
}

float TheFamousFastRSqRt (float Val)
{
	long i;
	float f=Val;

	i = *(long *)&f;
	i = 0x5f3759df - (i>>1);
	f = *(float *)&i;
	return f * (1.5 - Val*.5*f*f);
}

short LessFamousBhaskaraI_Sine_0_to_180deg(short Arg)	//Arg 512 = 180 deg.
{
	signed long Temp,Temp2;
	Temp=Temp2=Arg;
	Temp2=511-Temp2;
	Temp*=4096*Temp2;
	Temp/=327680-((signed long)Arg)*Temp2;
	return Temp;	//Sine X 1024
}


unsigned short X, Y;	//6 MSB = map tile, 6 bits = pixel inside a tile, 4 LSB = pixel fractions.
signed short Angle=0;	//+-32768 = +-180 deg.
signed short DX=1024, DY=0;	//cos and sin of Angle X 1024.
#define GRUDAK (12<<4)

int DoClipping (signed short StepX, signed short StepY)	//Wolf3d Clone
{
	unsigned short MinX, MaxX, MinY, MaxY;

	MinX = X-GRUDAK+StepX;
	MaxX = X+GRUDAK+StepX;
	MinY = Y-GRUDAK+StepY;
	MaxY = Y+GRUDAK+StepY;

	if (Map[MinY>>10][MinX>>10]|
	    Map[MinY>>10][MaxX>>10]|
	    Map[MaxY>>10][MinX>>10]|
	    Map[MaxY>>10][MaxX>>10])
	{
		if (!StepX || !StepY) return 0;
		return DoClipping(StepX, 0) || DoClipping(0, StepY);
	}

	X+=StepX;
	Y+=StepY;
	return 1;
}

struct
{
	unsigned short X, Y;	//6 MSB = map tile, 6 bits = pixel inside a tile, 4 LSB = pixel fractions.
	signed short Rotation;	//+-32768 = +-180 deg.
} TestMob = {32<<10, 32<<10, 0};

void AnimateTestMob()
{
	short A_0_180, SpinX, SpinY;

	Map[(TestMob.Y-(32<<4))>>10][(TestMob.X-(32<<4))>>10]=0;
	Map[(TestMob.Y-(32<<4))>>10][(TestMob.X+(32<<4))>>10]=0;
	Map[(TestMob.Y+(32<<4))>>10][(TestMob.X-(32<<4))>>10]=0;
	Map[(TestMob.Y+(32<<4))>>10][(TestMob.X+(32<<4))>>10]=0;

	A_0_180 = TestMob.Rotation>>6;
	if (A_0_180>0) SpinY=LessFamousBhaskaraI_Sine_0_to_180deg(A_0_180);
	else SpinY=-LessFamousBhaskaraI_Sine_0_to_180deg(-A_0_180);
	A_0_180 = TestMob.Rotation+16384; //Native overflow to a correct angle.
	A_0_180 >>= 6;
	if (A_0_180>0) SpinX=LessFamousBhaskaraI_Sine_0_to_180deg(A_0_180);
	else SpinX=-LessFamousBhaskaraI_Sine_0_to_180deg(-A_0_180);
	TestMob.Rotation+=300;
	
	TestMob.X = (32<<10) + SpinX*4;
	TestMob.Y = (32<<10) + SpinY*4;

	Map[(TestMob.Y-(32<<4))>>10][(TestMob.X-(32<<4))>>10]=128;
	Map[(TestMob.Y-(32<<4))>>10][(TestMob.X+(32<<4))>>10]=128;
	Map[(TestMob.Y+(32<<4))>>10][(TestMob.X-(32<<4))>>10]=128;
	Map[(TestMob.Y+(32<<4))>>10][(TestMob.X+(32<<4))>>10]=128;
}

int ModeVGA =5;	//5 for literal VGA, 1 for grouped columns
int Mode2Dfx=0;	//columns: 0 for accurate VGA draw, 1 for NeoGeo restrictions, 2 for NeoGeo+position optimization, 3=2+sprite list
int Clicked=0;

int DoGameplayTick()
{
	int MouseX, MouseY, MouseB;
	short A_0_180;
	
//	printf ("Tick!\r\n");
	__asm
	{
		mov ax,0x03
		int 0x33
		mov MouseB,bx	//mouse buttons
		mov ax,0x0b
		int 0x33
		mov MouseX,cx	//mouse x
		mov MouseY,dx	//mouse y
	}
//	ClearKBBuffer();	//ToDo!
//	printf ("%i \r\n", MouseB);

	Angle-=MouseX*16;
//	printf ("%u \r\n", Angle);

	A_0_180 = Angle>>6;
	if (A_0_180>0) DY=LessFamousBhaskaraI_Sine_0_to_180deg(A_0_180);
	else DY=-LessFamousBhaskaraI_Sine_0_to_180deg(-A_0_180);
	A_0_180 = Angle+16384; //Native overflow to a correct angle.
	A_0_180 >>= 6;
	if (A_0_180>0) DX=LessFamousBhaskaraI_Sine_0_to_180deg(A_0_180);
	else DX=-LessFamousBhaskaraI_Sine_0_to_180deg(-A_0_180);
/*
	if (MouseB&2)	//RStrafe
	{
		DoClipping( - DX*((signed long)MouseY)/256 + DY/16,
			    - DY*((signed long)MouseY)/256 - DX/16);
	}
	else if (MouseB&4)//LStrafe
	{
		DoClipping( - DX*((signed long)MouseY)/256 - DY/16,
			    - DY*((signed long)MouseY)/256 + DX/16);
	} else*/ {
		DoClipping( - DX*((signed long)MouseY)/256,
			    - DY*((signed long)MouseY)/256);
	}

	if (MouseB&2)
	{
		if (!Clicked)
		{
			if (ModeVGA == 5) ModeVGA = 1;
			else if (Mode2Dfx<3) Mode2Dfx++;
			else ModeVGA=5+(Mode2Dfx=0);
		}
		Clicked=1;
	} else Clicked=0;

	if (MouseB&1)	//Fire!
	{
		return 0;	//debug: shoot anything to exit.
	}

	AnimateTestMob();
	
	return 1;	//Still alive
}

static const char OmitMap[16]=
{
/* Size	     Flags	NeoGeo size code
1	0000000010000000	0x0
2	0000100010000000        0x1
3	0000100010001000        0x2
4	0010100010001000        0x3
5	0010100010001010        0x4
6	0010101010001010        0x5
7	0010101010101010        0x6
8	1010101010101010        0x7
9	1010101011101010        0x8
10	1011101011101010        0x9
11	1011101011101011        0xA
12	1011101111101011        0xB
13	1011101111101111        0xC
14	1111101111101111        0xD
15	1111101111111111        0xE
16	1111111111111111        0xF
	"Draw if Size > ..."  */
0x07, 0x0D, 0x03, 0x09, 0x01, 0x0F, 0x05, 0x0B, 0x00, 0x08, 0x06, 0x0E, 0x02, 0x0C, 0x04, 0x0A
};

void BlitWallLikeTheNeoGeo (int XPos, int YPos, int XSize, int YSize, unsigned char __far *Column)
{	//No upscale. Downscale only. Max. 16 size X and 512 size Y. Y downscale is not accurate (written from scratch). X downscale is based on the NeoGeoDevWiki.
	if (YSize>256) YSize=256;
	for (int Y=0; Y<YSize; Y++)
	{
		int YSrc = (long)Y*256/(long)YSize;
		for (int X=0, XDst=0; X<16; X++)
		{
			if (OmitMap[X]<XSize)	//...or <= NeoGeo size code
			{
				if (XDst+XPos<320 && XDst+XPos>=0)	//AFAIK NeoGeo can auto-crop and auto-shingle sprites if they are set in the certain order
					Frame[Y+YPos][XDst+XPos] = Column[X + 16*YSrc];
				XDst++;
			}
		}
	}
}
void BlitMobLikeTheNeoGeo (int XPos, int YPos, int XSize, int YSize, unsigned char __far *Column)
{	//No upscale. Downscale only. Max. 16 size X and 512 size Y. Y downscale is not accurate (written from scratch). X downscale is based on the NeoGeoDevWiki.
	if (YSize>64) YSize=64;
	for (int Y=0; Y<YSize; Y++)
	{
		int YSrc = (long)Y*64/(long)YSize;
		for (int X=0, XDst=0; X<16; X++)
		{
			if (OmitMap[X]<XSize)	//...or <= NeoGeo size code
			{
				if (XDst+XPos<320 && XDst+XPos>=0)	//AFAIK NeoGeo can auto-crop and auto-shingle sprites if they are set in the certain order
				 if (Column[X + 16*YSrc] != 0xF7)	//Doom transparent
					Frame[Y+YPos][XDst+XPos] = Column[X + 16*YSrc];
				XDst++;
			}
		}
	}
}

struct Tile
{
	long SqDist;
	int X, Y;
	int XSize, YSize;
	unsigned char __far *ImgPtr;
} WallTileList[64], MobTileList[8][4];	//Actually, only 1st of 8 is used.
int MobCount=0;

void DebugDraw()	//Like that youtube demo, sprites are drawn over walls even if they are behind them
{
	for (int i=0; i<64;i++)
	{
		BlitWallLikeTheNeoGeo (WallTileList[i].X, WallTileList[i].Y, WallTileList[i].XSize, WallTileList[i].YSize, WallTileList[i].ImgPtr);
	}

	for (int j=0; j<MobCount;j++)
	 for (int i=0; i<4;i++)
	 {
		BlitMobLikeTheNeoGeo (MobTileList[j][i].X, MobTileList[j][i].Y, MobTileList[j][i].XSize, MobTileList[j][i].YSize, MobTileList[j][i].ImgPtr);
	 }
}

void LayeredDraw ()	//A single mob is enough for the test
{
//	return DebugDraw();
	for (int i=0; i<64;i++)
	{
		if (WallTileList[i].SqDist>MobTileList[0][0].SqDist) BlitWallLikeTheNeoGeo (WallTileList[i].X, WallTileList[i].Y, WallTileList[i].XSize, WallTileList[i].YSize, WallTileList[i].ImgPtr);
	}
	for (int i=0; i<4;i++)
	{
		BlitMobLikeTheNeoGeo (MobTileList[0][i].X, MobTileList[0][i].Y, MobTileList[0][i].XSize, MobTileList[0][i].YSize, MobTileList[0][i].ImgPtr);
	}
	for (int i=0; i<64;i++)
	{
		if (WallTileList[i].SqDist<=MobTileList[0][0].SqDist) BlitWallLikeTheNeoGeo (WallTileList[i].X, WallTileList[i].Y, WallTileList[i].XSize, WallTileList[i].YSize, WallTileList[i].ImgPtr);
	}
}

/*void LayeredDrawII ()	//A single mob is enough for the test
{
	for (int WallN=0, MobN=0; WallN<64; WallN++)
	{
		if (MobCount && MobN<4)	//The mob exists and has not ended yet
		{
			if (WallTileList[WallN].X+WallTileList[WallN].XSize > MobTileList[0][MobN].X)	//Mob begins before the wall stripe ends
			{
				if (WallTileList[WallN].SqDist > MobTileList[0][MobN].SqDist)	//The mob covers the wall
				{
					while (WallN<64 && WallTileList[WallN].SqDist > MobTileList[0][MobN].SqDist) //This case is easy to check. All mob tiles have the same SqDist.
					{	//Drawing as many wall tiles as possible
						BlitWallLikeTheNeoGeo (WallTileList[WallN].X, WallTileList[WallN].Y, WallTileList[WallN].XSize, WallTileList[WallN].YSize, WallTileList[WallN].ImgPtr);
						WallN++;
					}
					while (MobN<4 && WallTileList[WallN].X+WallTileList[WallN].XSize > MobTileList[0][MobN].X) //Now drawing mob tiles until the mob ends or that tile from the prev. loop covers it (including the first [partially] covered tile).
					{
						
					}
					WallN--;	//Retreat because we failed to draw that one (it's either non-existing 64th or just covers the mob)
				} else {	//The wall covers the mob
					
				}
			} else BlitWallLikeTheNeoGeo (WallTileList[WallN].X, WallTileList[WallN].Y, WallTileList[WallN].XSize, WallTileList[WallN].YSize, WallTileList[WallN].ImgPtr);
		} else BlitWallLikeTheNeoGeo (WallTileList[WallN].X, WallTileList[WallN].Y, WallTileList[WallN].XSize, WallTileList[WallN].YSize, WallTileList[WallN].ImgPtr);
	}
}
*/
/*
int SqDistCompare( const void *op1, const void *op2 )
{
    const Tile *W1 = (const Tile *) op1;
    const Tile *W2 = (const Tile *) op2;
    return( W2->SqDist - W1->SqDist );
}

void LayeredDrawIII ()	//A single mob is enough for the test
{
	qsort( WallTileList, 64, sizeof(Tile), SqDistCompare );

	for (int i=0; i<64;i++)
	{
		if (WallTileList[i].SqDist<MobTileList[0][0].SqDist)
		 for (int i=0; i<4;i++)
		 {
			BlitMobLikeTheNeoGeo (MobTileList[0][i].X, MobTileList[0][i].Y, MobTileList[0][i].XSize, MobTileList[0][i].YSize, MobTileList[0][i].ImgPtr);
		 }
		BlitWallLikeTheNeoGeo (WallTileList[i].X, WallTileList[i].Y, WallTileList[i].XSize, WallTileList[i].YSize, WallTileList[i].ImgPtr);
	}
}
*/
static unsigned short ScaleF[320];

int ShingledReserve=0;

int AddToZBufferAndBlacklist (int Type, unsigned short CameraX, unsigned short CameraY, unsigned short RayX, unsigned short RayY, int Column, char WhichWall, float CosA)
{
	unsigned short DistToX, DistToY;
	unsigned long SqDist;
	int TextureHeight, TexturePos, Texture;

	if (Type>127)	//We hit the mob, not the wall (not always the mob itself, but one of it's "link cells"). So we add it to the drawing list, in order to place properly later.
	{
		float x1,x2,y1,y2;	//line
		float x0,y0;	//mob center
		float d;	//distance
	
		x1=RayX;
		x2=CameraX;
		y1=RayY;
		y2=CameraY;
	
		x0=TestMob.X;
		y0=TestMob.Y;
	
		d=(x2-x1)*(y1-y0) - (x1-x0)*(y2-y1);
		d*=TheFamousFastRSqRt(  (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)  );
	
//		d+=(float)DemoSargWidth/2.0;
//		ZBuff[0].Texture = d / 16.0;
//		if (ZBuff[0].Texture>=0 && ZBuff[0].Texture<DemoSargWidth)	//ray passes inside the mob texture bounds
//		{
			if (TestMob.X>CameraX) DistToX = TestMob.X - CameraX; else DistToX = CameraX - TestMob.X;
			if (TestMob.Y>CameraY) DistToY = TestMob.Y - CameraY; else DistToY = CameraY - TestMob.Y;

			SqDist = DistToX;
			SqDist *= SqDist;
			SqDist += ((long)DistToY) * ((long)DistToY);
//			ZBuff[0].TextureHeight = 65536.0 * TheFamousFastRSqRt (SqDist/16);

//			ZBuff[0].TextureHeight = (long)(ScaleF[Column])*(long)(ZBuff[0].TextureHeight)/2048/4;
//			ZBuff[0].TexturePos = 100-ZBuff[0].TextureHeight/8;
//		}

		TextureHeight = 32768.0 * TheFamousFastRSqRt (SqDist/16);
		TextureHeight = (long)(ScaleF[Column*5+2])*(long)TextureHeight/2048/24;

		d/=4.0;	//in texture pixels
		d+=32.0;	//zero if ray hits exactly the leftmost column of the mob
		d*=TextureHeight;
		d/=64.0;	//in screen pixels

		if (MobCount<8)
		{
			for (int i=0; i<4;i++)
			{
				MobTileList[MobCount][i].SqDist = SqDist;
				MobTileList[MobCount][i].X=Column*5+2   -d  +  TextureHeight*i/4;
				MobTileList[MobCount][i].Y=100-TextureHeight/5;
				MobTileList[MobCount][i].XSize=TextureHeight*(i+1)/4 - TextureHeight*i/4;
				MobTileList[MobCount][i].YSize=TextureHeight;
				MobTileList[MobCount][i].ImgPtr=MobColumns[i][0];
			}

			MobCount++;
		} // else sacrifice the most distant one

		return Type;		//Blacklist this mob
	}

	switch (WhichWall)	//In the most generic case, the wall block can have all four textures set independently. In Wolf3D, L+R and U+D were different (some are darker, also day/night and lift switch/wall). Here, all texture are the same but must be calculated in the proper direction.
	{
		case 'L':
			Texture = 255-RayY%1024/4;
		break;
		case 'R':
			Texture = RayY%1024/4;
		break;
		case 'U':
			Texture = 255-RayX%1024/4;
		break;
		case 'D':
			Texture = RayX%1024/4;
		break;
		default: Texture = 0;
	}

	if (RayX>CameraX) DistToX = RayX - CameraX; else DistToX = CameraX - RayX;
	if (RayY>CameraY) DistToY = RayY - CameraY; else DistToY = CameraY - RayY;
	SqDist = DistToX;
	SqDist *= SqDist;
	SqDist += ((long)DistToY) * ((long)DistToY);
	TextureHeight = 65536.0 * TheFamousFastRSqRt (SqDist/16);

	if (ModeVGA == 5)	//Perfect, literal, 320 ray VGA
	{
		TextureHeight = (long)(ScaleF[Column])*(long)TextureHeight/2048/24;
		for (int y=0; y<200; y++)   //In DOSBox, this loop takes... forever.
		{
			int TexY;
			if (TextureHeight) TexY = (signed long)(y-100)*256L / (signed long)(TextureHeight) + 128;
			else TexY=256-128*(y==100);
			if (TexY<0 || TexY>255) Frame[y][Column]=0;
			else Frame[y][Column]=TestWall[TexY][Texture];
		}
	} else {
		if (Mode2Dfx>1)	//Optimal NeoGeo
		{
			TextureHeight = (long)(ScaleF[Column*5+2])*(long)TextureHeight/2048/24;
			float Min=256;
			int MinX=0, Best;
			long TextureWidth = 16 * TextureHeight * CosA / 128;
if (TextureHeight>200) TextureHeight=200;	//Debug! Should be 1..256 (up to 1:1) but it'll require cropping
			unsigned char __far *Col;
			if (TextureWidth>18 || TextureWidth<3)	//Two repeating pixels are still OK
			{
				Col = ShiftedColumnsBl[Texture/16][0];	//Nothing to optimize here -- it'll be blurry either way
				MinX = ShingledReserve;
			}
			else {
				for (int x=-2; x<=ShingledReserve; x++)
				{
					float Offset = (float)x * 128.0/(float)TextureHeight / CosA;
					float Candidate = (float)Texture+Offset;
					int Nearest4 = Candidate+2.5;	//+2 and rounding
					Nearest4&=~3;
					float Err = fabs((float)Nearest4-Candidate);
					if (Min>Err) {Min=Err;MinX=x;Best=Nearest4;}
				}
				switch (Best%16/4)
				{
					case 0: Col = ShiftedColumns00[Best/16][0]; break;
					case 1: Col = ShiftedColumns04[Best/16][0]; break;
					case 2: Col = ShiftedColumns08[Best/16][0]; break;
					case 3: Col = ShiftedColumns12[Best/16][0]; break;
				}
			}
			if (TextureWidth<5) TextureWidth=5;
			ShingledReserve = MinX+TextureWidth-5;
			if (ShingledReserve>2) ShingledReserve=2;

			if (Mode2Dfx>2)
			{
				WallTileList[Column].SqDist = SqDist;
				WallTileList[Column].X = Column*5+MinX;
				WallTileList[Column].Y = 100-TextureHeight/2;
				WallTileList[Column].XSize = TextureWidth;
				WallTileList[Column].YSize = TextureHeight;
				WallTileList[Column].ImgPtr = Col;
			}
			else BlitWallLikeTheNeoGeo (Column*5+MinX, 100-TextureHeight/2, TextureWidth, TextureHeight, Col);
		}
		else if (Mode2Dfx)	//Trivial (Naive) NeoGeo
		{
			TextureHeight = (long)(ScaleF[Column*5+2])*(long)TextureHeight/2048/24;
			long TextureWidth = 16 * TextureHeight * CosA / 128;
if (TextureHeight>200) TextureHeight=200;	//Debug! Should be 1..256 (up to 1:1) but it'll require cropping
			unsigned char __far *Col;
			switch (Texture%16/4)
			{
				case 0: Col = ShiftedColumns00[Texture/16][0]; break;
				case 1: Col = ShiftedColumns04[Texture/16][0]; break;
				case 2: Col = ShiftedColumns08[Texture/16][0]; break;
				case 3: Col = ShiftedColumns12[Texture/16][0]; break;
			}
			if (TextureWidth>18 || TextureWidth<3)	//Two repeating pixels are still OK
				Col = ShiftedColumnsBl[Texture/16][0];
			
			if (TextureWidth<5)
				BlitWallLikeTheNeoGeo (Column*5, 100-TextureHeight/2, 5           , TextureHeight, Col);
			else	BlitWallLikeTheNeoGeo (Column*5, 100-TextureHeight/2, TextureWidth, TextureHeight, Col);
		} else {		//Unrestricted sprites
			TextureHeight = (long)(ScaleF[Column*5+2])*(long)TextureHeight/2048/24;
			for (int x=-2; x<=2; x++)
			{
				float Offset = (float)x * 128.0/(float)TextureHeight / CosA;
				int TexCol = Texture+Offset;
				TexCol %= 256;
				for (int y=0; y<200; y++)
				{
					int TexY;
					if (TextureHeight) TexY = (signed long)(y-100)*256L / (signed long)(TextureHeight) + 128;
					else TexY=256-128*(y==100);
					if (TexY<0 || TexY>255) Frame[y][Column*5+2+x]=0;
					else Frame[y][Column*5+2+x]=TestWall[TexY][TexCol];
				}
			}
		}
	}

	return 0;	// 0 is for "blacklist everything" (ray hits an opaque texture and terminates).
}

static unsigned char __far Pal256[256][3];

void main (void)
{
	char const * Err;
	int MobX, MobH;
	int i, j, k, pt;
	unsigned short RayX, RayY, BrezDistToStartSteps, BrezDistToEndShifts, BrezPrev, BrezShiftPerCell, DistToX, DistToY;	//6 MSB = map tile, 6 bits = pixel inside a tile, 4 LSB = pixel fractions.
	signed short RayDX, RayDY;
	unsigned long SqDist;
	unsigned char ThisTick, PrevTick;
	signed long Temp;
	int MapCell;
	static char BlackList[256]; //When the ray meets the MObj, it can hit up to 3 (of 4 total) "link cells", pointing to the same MObj. To avoid surplus calls, we should blacklist it's pointer value after first encounter.
	
	__asm
	{
		mov ax, 0x0013  //VGA 320x200
		int 0x10
	}

	Err=LoadBMP256("Shhhhhh!.bmp",Pal256,TestCaco[0],64,64);
	if (Err)
	{
		cout<<Err<<endl;
		return;
	}
	for (int Stripe=0; Stripe<4; Stripe++)
	 for (int Y=0; Y<64; Y++)
	  for (int X=0; X<16; X++)
	  {
		int XSrc = Stripe*16+X;
		MobColumns[Stripe][Y][X] = TestCaco[Y][XSrc];
	  }	

	Err=LoadBMP256("Blurry.bmp",Pal256,TestWall[0],256,256);
	if (Err)
	{
		cout<<Err<<endl;
		return;
	}
	for (int Stripe=0; Stripe<16; Stripe++)
	 for (int Y=0; Y<256; Y++)
	  for (int X=0; X<16; X++)
	  {
		int XSrc = (Stripe*16+X) % 256;
		ShiftedColumnsBl[Stripe][Y][X] = TestWall[Y][XSrc];
	  }

	Err=LoadBMP256("TestWall.bmp",Pal256,TestWall[0],256,256);
	if (Err)
	{
		cout<<Err<<endl;
		return;
	}
	SetPalette256 (Pal256[0]);

	for (i=0; i<320; i++)
		for (j=0; j<256; j++)
			Frame[(long)j*200L/256L][i] = TestWall[j][(long)i*256L/320L];
	Blt256 (Frame[0]);

	for (int Stripe=0; Stripe<16; Stripe++)
	 for (int Y=0; Y<256; Y++)
	  for (int X=0; X<16; X++)
	  {
		int XSrc = (Stripe*16+X) % 256;
		ShiftedColumns00[Stripe][Y][X] = TestWall[Y][XSrc];
		XSrc = (Stripe*16+X+4) % 256;
		ShiftedColumns04[Stripe][Y][X] = TestWall[Y][XSrc];
		XSrc = (Stripe*16+X+8) % 256;
		ShiftedColumns08[Stripe][Y][X] = TestWall[Y][XSrc];
		XSrc = (Stripe*16+X+12) % 256;
		ShiftedColumns12[Stripe][Y][X] = TestWall[Y][XSrc];
	  }
/*	for (int RndX=0; RndX<2; RndX++)
	 for (int RndY=0; RndY<2; RndY++)
	  for (int Y=0; Y<256; Y++)
	   for (int X=0; X<16; X++)
	   {
		int SpotX=rand()%256;
		int SpotY=rand()%256;

		long AveR = Pal256[ TestWall[SpotY][SpotX] ][ 0 ]*2;
		long AveG = Pal256[ TestWall[SpotY][SpotX] ][ 1 ]*2;
		long AveB = Pal256[ TestWall[SpotY][SpotX] ][ 2 ]*2;

		AveR    += Pal256[ TestWall[SpotY][(SpotX+1  )%256] ][ 0 ];
		AveG    += Pal256[ TestWall[SpotY][(SpotX+1  )%256] ][ 1 ];
		AveB    += Pal256[ TestWall[SpotY][(SpotX+1  )%256] ][ 2 ];
                        
		AveR    += Pal256[ TestWall[SpotY][(SpotX+255)%256] ][ 0 ];
		AveG    += Pal256[ TestWall[SpotY][(SpotX+255)%256] ][ 1 ];
		AveB    += Pal256[ TestWall[SpotY][(SpotX+255)%256] ][ 2 ];
                        
		AveR    += Pal256[ TestWall[(SpotY+1  )%256][SpotX] ][ 0 ];
		AveG    += Pal256[ TestWall[(SpotY+1  )%256][SpotX] ][ 1 ];
		AveB    += Pal256[ TestWall[(SpotY+1  )%256][SpotX] ][ 2 ];
                        
		AveR    += Pal256[ TestWall[(SpotY+255)%256][SpotX] ][ 0 ];
		AveG    += Pal256[ TestWall[(SpotY+255)%256][SpotX] ][ 1 ];
		AveB    += Pal256[ TestWall[(SpotY+255)%256][SpotX] ][ 2 ];

		AveR /= 6;
		AveG /= 6;
		AveB /= 6;

		int C;
		long CM=2000000000L;
		for (int i=0; i<256; i++)
		{
			long Err = ( (long)(Pal256[i][0])-AveR )*( (long)(Pal256[i][0])-AveR );
			Err     += ( (long)(Pal256[i][1])-AveG )*( (long)(Pal256[i][1])-AveG );
			Err     += ( (long)(Pal256[i][2])-AveB )*( (long)(Pal256[i][2])-AveB );
			if (CM>Err) {C=i; CM=Err;}
		}

		Blurry[RndX][RndY][Y][X] = C;
	   }
*/
	for (i=-160;i<160;i++) ScaleF[i+160]=sqrt(i*i*10000L + 16000L*16000L);
	
	X = Y = 1.5 * 1024;	//Player start position and angle
	Angle = 0;

	__asm
	{
		mov ax,0x00
		int 0x33
		mov i,ax
	}
	if (i==-1) printf ("Mouse init completed\r\n");
	else
	{
		printf ("This demo requires a mouse.\r\n");
		return;
	}

	for(;;)
	{
		pt=0;

		if (ModeVGA == 1 && Mode2Dfx)
		{
			ShingledReserve=0;
			memset (Frame[0], 0xF7, 320*100);
			memset (Frame[100], 0xF8, 320*100);
		}

		/*memset (Blacklist, 0, 128);*/ BlackList[128] = 0;	//No blacklist (for NeoGeo Test Bench; the mob is drawn at once, unlike the VGA column-by-column drawing, so it's blacklisted per scene, not per column, too).
		MobCount = 0;

		for (i=0; i<64*ModeVGA; i++)	//320 pixels, 64 columns 5 pixels each.
		{
			MobH = 0;
			
			RayDX = DX + (long)DY*(i-32*ModeVGA)/(32*ModeVGA);
			RayDY = DY - (long)DX*(i-32*ModeVGA)/(32*ModeVGA);
			RayX=X;
			RayY=Y;
			/*memset (Blacklist, 0, 128);*/ BlackList[0] = 0;		//No blacklist (for literal VGA, which is disabled completely).
			
			float R = sqrt ((float)RayDX*(float)RayDX + (float)RayDY*(float)RayDY);	//ToDo: fast 1/sqrt, of course!

//BlackList[128]=1; //quick hack to fully remove our only test mob from the raycast engine

			if (RayDX>0)
			{
				if (RayDY>0)
				{
					if (RayDX>RayDY)	//X+ Y+ |X|>|Y|
					{
						BrezDistToStartSteps = 1024 - (RayX%1024);
						BrezShiftPerCell = 1024L*(long)RayDY/(long)RayDX;

						BrezPrev = RayY;
						RayY += (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = 1024 - (BrezPrev%1024);
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX+(long)BrezDistToEndShifts*(long)RayDX/(long)RayDY, BrezPrev+BrezDistToEndShifts, i, 'D', RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayX += BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'L', RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayY;
							RayY += BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = 1024 - (BrezPrev%1024);
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX+(long)BrezDistToEndShifts*(long)RayDX/(long)RayDY, BrezPrev+BrezDistToEndShifts, i, 'D', RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayX += 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'L', RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//HitOpaque(some horizon blabla);
						//goto RayFinished;

					} else {	//X+ Y+ |X|<|Y|

						BrezDistToStartSteps = 1024 - (RayY%1024);
						BrezShiftPerCell = 1024L*(long)RayDX/(long)RayDY;

						BrezPrev = RayX;
						RayX += (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = 1024 - (BrezPrev%1024);
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev+BrezDistToEndShifts, RayY+(long)BrezDistToEndShifts*(long)RayDY/(long)RayDX, i, 'L', RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayY += BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'D', RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayX;
							RayX += BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = 1024 - (BrezPrev%1024);
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev+BrezDistToEndShifts, RayY+(long)BrezDistToEndShifts*(long)RayDY/(long)RayDX, i, 'L', RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayY += 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'D', RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//AddToZBufferAndBlacklist(some horizon blabla);
						//goto RayFinished;
					}
				} else {
					if (RayDX>-RayDY)	//X+ Y- |X|>|Y|
					{
						BrezDistToStartSteps = 1024 - (RayX%1024);
						BrezShiftPerCell = 1024L*(long)(-RayDY)/(long)RayDX;

						BrezPrev = RayY;
						RayY -= (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = BrezPrev%1024 + 1;
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX+(long)BrezDistToEndShifts*(long)RayDX/(long)(-RayDY), BrezPrev-BrezDistToEndShifts, i, 'U', -RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayX += BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'L', RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayY;
							RayY -= BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = BrezPrev%1024 + 1;
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX+(long)BrezDistToEndShifts*(long)RayDX/(long)(-RayDY), BrezPrev-BrezDistToEndShifts, i, 'U', -RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayX += 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'L', RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//HitOpaque(some horizon blabla);
						//goto RayFinished;

					} else {	//X+ Y- |X|<|Y|

						BrezDistToStartSteps = RayY%1024 + 1;
						BrezShiftPerCell = 1024L*(long)RayDX/(long)(-RayDY);

						BrezPrev = RayX;
						RayX += (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = 1024 - (BrezPrev%1024);
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev+BrezDistToEndShifts, RayY-(long)BrezDistToEndShifts*(long)(-RayDY)/(long)RayDX, i, 'L', RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayY -= BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'U', -RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayX;
							RayX += BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = 1024 - (BrezPrev%1024);
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev+BrezDistToEndShifts, RayY-(long)BrezDistToEndShifts*(long)(-RayDY)/(long)RayDX, i, 'L', RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayY -= 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'U', -RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//AddToZBufferAndBlacklist(some horizon blabla);
						//goto RayFinished;
					}
				}
			} else {	//Big Second Half Of X<0
				if (RayDY>0)
				{
					if (-RayDX>RayDY)	//X- Y+ |X|>|Y|
					{
						BrezDistToStartSteps = RayX%1024 + 1;
						BrezShiftPerCell = 1024L*(long)RayDY/(long)(-RayDX);

						BrezPrev = RayY;
						RayY += (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = 1024 - (BrezPrev%1024);
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX-(long)BrezDistToEndShifts*(long)(-RayDX)/(long)RayDY, BrezPrev+BrezDistToEndShifts, i, 'D', RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayX -= BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'R', -RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayY;
							RayY += BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = 1024 - (BrezPrev%1024);
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX-(long)BrezDistToEndShifts*(long)(-RayDX)/(long)RayDY, BrezPrev+BrezDistToEndShifts, i, 'D', RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayX -= 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'R', -RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//HitOpaque(some horizon blabla);
						//goto RayFinished;

					} else {	//X- Y+ |X|<|Y|

						BrezDistToStartSteps = 1024 - (RayY%1024);
						BrezShiftPerCell = 1024L*(long)(-RayDX)/(long)RayDY;

						BrezPrev = RayX;
						RayX -= (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = BrezPrev%1024 + 1;
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev-BrezDistToEndShifts, RayY+(long)BrezDistToEndShifts*(long)RayDY/(long)(-RayDX), i, 'R', -RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayY += BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'D', RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayX;
							RayX -= BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = BrezPrev%1024 + 1;
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev-BrezDistToEndShifts, RayY+(long)BrezDistToEndShifts*(long)RayDY/(long)(-RayDX), i, 'R', -RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayY += 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'D', RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//AddToZBufferAndBlacklist(some horizon blabla);
						//goto RayFinished;
					}
				} else {
					if (RayDX<RayDY)	//X- Y- |X|>|Y|
					{
						BrezDistToStartSteps = RayX%1024 + 1;
						BrezShiftPerCell = 1024L*(long)RayDY/(long)RayDX;

						BrezPrev = RayY;
						RayY -= (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = BrezPrev%1024 + 1;
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX-(long)BrezDistToEndShifts*(long)RayDX/(long)RayDY, BrezPrev-BrezDistToEndShifts, i, 'U', -RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayX -= BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'R', -RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayY;
							RayY -= BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = BrezPrev%1024 + 1;
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX-(long)BrezDistToEndShifts*(long)RayDX/(long)RayDY, BrezPrev-BrezDistToEndShifts, i, 'U', -RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayX -= 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'R', -RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//HitOpaque(some horizon blabla);
						//goto RayFinished;

					} else {	//X- Y- |X|<|Y|

						BrezDistToStartSteps = RayY%1024 + 1;
						BrezShiftPerCell = 1024L*(long)RayDX/(long)RayDY;

						BrezPrev = RayX;
						RayX -= (long)BrezDistToStartSteps * (long)BrezShiftPerCell / 1024L;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BrezDistToEndShifts = BrezPrev%1024 + 1;
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev-BrezDistToEndShifts, RayY-(long)BrezDistToEndShifts*(long)RayDY/(long)RayDX, i, 'R', -RayDX/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						RayY -= BrezDistToStartSteps;
						if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
						{
							BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'U', -RayDY/R)]=1;
							if (BlackList[0]) goto RayFinished;	//Ray "i" is terminated
						}

						while(1)	//ToDo: after some reasonable distance, we need no more steps because only single-pixel horizon is visible!
						{
							BrezPrev = RayX;
							RayX -= BrezShiftPerCell;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BrezDistToEndShifts = BrezPrev%1024 + 1;
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, BrezPrev-BrezDistToEndShifts, RayY-(long)BrezDistToEndShifts*(long)RayDY/(long)RayDX, i, 'R', -RayDX/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}

							RayY -= 1024;
							if ( (MapCell=Map[RayY>>10][RayX>>10]) != 0 && !BlackList[MapCell])	//Order does matter!
							{
								BlackList[AddToZBufferAndBlacklist(MapCell, X, Y, RayX, RayY, i, 'U', -RayDY/R)]=1;
								if (BlackList[0]) goto RayFinished;	//Ray "i" stops marching
							}
						}

						//AddToZBufferAndBlacklist(some horizon blabla);
						//goto RayFinished;
					}
				}
			}

RayFinished:

			__asm
			{
				mov ax, 0
				mov es,ax
				mov al,es:[0x046c]	//system timer
				mov ThisTick,al
			}
			if (ThisTick!=PrevTick)
			{
				PrevTick=ThisTick;
				if (!DoGameplayTick() ) return;
			}
		}

		if (Mode2Dfx>2) LayeredDraw();		
		Blt256 (Frame[0]);
		
	}

}

LevelMap.H:

static unsigned char Map[64][64]=	//such a TRADITIONAL size!
{
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1[Перевод] Doom работает везде, даже на Neo Geo05.0417-07-2026
2Забытый мультиколор (часть 4)0715-06-2026
3Тайна 3dfx Voodoo: изучаем архитектуру GPU из 90-х «по винтикам»07.9608-08-2026
4Подробная история развития 2D-графики в PC06.5601-08-2026
5Как я собрал браузерный хоррор на Three.js: GLB-контракт, аномалии и PS1-рендеринг для слабых устройств014.6212-08-2026
6Погружение в создание приставки для Microsoft MakeCode Arcade05.1808-08-2026
7Легко ли сделать эмулятор ZX Spectrum для SEGA Mega Drive?2715-07-2026
8[Перевод] Linux на Atari Jaguar. Нет, серьёзно5607-07-2026
9Зажигаем погасшее. Оживляем раритетный детектор валют из девяностых07.224-07-2026
10Мне надоело писать один и тот же код. Поэтому я сделал Featuregen3609-07-2026

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 7.58. Источник: habr.com.