/*
	TP - 3D Engine  in C--
	Marko Kleine Berkenbusch
*/

?resize           TRUE
?parsecommandline TRUE
?stack           10000
?use80386
?codesize

?include "VGA.H--"
?include "TRIG.H--"
?include "KEYCODES.H--"
?include "FILE.H--"
?include "VIDEO.H--"
?include "SYSTEM.H--"
?include "MATH.H--"
?include "DOS.H--"
?include "PORTS.H--"
?include "STRING.H--"
?include "WRITE.H--"
?include "RANDOM.H--"

?define maxAnzPolys  500
?define maxAnzPunkte 600
?define AnzBMaps      10
?define StepSize     200
?define AnzMonsters    4
?define MonsterX   -2000
?define MonsterZ    8000
?define MonsterSX    100
?define MonsterSY    135
?define MonsterSpeed 200
byte LightTab[4096];
byte leftdown,rightdown,updown,downdown,altdown,sgreater,ssmaller,Karte,KAktiv,
     lookup,lookdown,fire,quit,Shoot,FloorActive;
byte pal[768],hpal[768],ColReady[320],MColR[320],MonsterHit;
byte CurrentLoopflag[5];
word Polygone_p1[maxAnzPolys],Polygone_p2[maxAnzPolys],
     Polygone_p3[maxAnzPolys],Polygone_p4[maxAnzPolys];
word AnzPolys,AnzPunkte;
word VMem,SWinkel,Hits;
word BMaps[AnzBMaps];           /* Segmentadressen der Graphikdaten */
word PBMaps[maxAnzPolys];       /* Texturzuordnungstabelle */
word Monsters[AnzMonsters];
word oldkeyboardhandle[2];
word MonsterDrawPos,MonsterFrame;
int Winkel,Versch,FVersch,Dreh,MonstWi;
word CurrentSPointer[5];
long BPos_x,BPos_y,BPos_z;  /* Betrachterposition */
long Punkte_x[maxAnzPunkte],Punkte_y[maxAnzPunkte],Punkte_z[maxAnzPunkte],
     GPunkte_x[maxAnzPunkte],GPunkte_y[maxAnzPunkte],GPunkte_z[maxAnzPunkte];
long HScale,VScale,XStart,XEnd,YStart,YEnd;
dword ShootTimerStart;
long FStep,SStep;
byte BMapNames = "WALL1.DAT\000"
 	         "WALL2.DAT\000"
	         "WALL3.DAT\000"
	         "WALL4.DAT\000"
	         "WALL5.DAT\000"
	         "WALL6.DAT\000"
	         "FLOOR.DAT\000"
	         "CEIL.DAT\000"
 		 "GUN11.DAT\000"
		 "GUN22.DAT" ;
byte DataFile    = "NIXF.WAD" ;
byte PalName     = "WALL.PAL" ;
byte LightFile   = "LIGHTTAB.DAT" ;
byte MonsterFile = "TMONSTER.DAT" ;

void InitC4(byte size)
{
	AX=0x13;
	$INT 0x10
	DX=0x3C4;
	AL=4;
	$OUT DX,AL
	DX++;
	$IN AL,DX
	AL&=0xF7;
	$OUT DX,AL
	DL=0xD4;
	AL=0x13;
	$OUT DX,AL
	DX++;
	AL=size;
	$OUT DX,AL
	DX--;
	AX=0xE317;
	$OUT DX,AX
	AX=0x014;
	$OUT DX,AX
}

interrupt IntZeroHandler()
{
	$POP AX
	AX++;
	$PUSH AX
}

interrupt keyboardhandle()
{
	$PUSH DS
	$PUSH AX
	$PUSH BX
	DS=CS;
	$IN AL,0x60
	BL=TRUE;
	IF(AL>=128){
		AL-=128;
    BL=FALSE;
	}
	IF(AL==s_left)leftdown=BL;
	ELSE IF(AL==s_right)rightdown=BL;
	ELSE IF(AL==s_up)updown=BL;
	ELSE IF(AL==s_down)downdown=BL;
	ELSE IF(AL==s_alt)altdown=BL;
	ELSE IF(AL==s_f1)ssmaller=BL;
	ELSE IF(AL==s_f2)sgreater=BL;
	ELSE IF(AL==s_tab)Karte=BL;
	ELSE IF(AL==s_esc)quit=TRUE;
	ELSE IF(AL==s_pageup)lookup=BL;
	ELSE IF(AL==s_pagedown)lookdown=BL;
	ELSE IF(AL==s_ctrl)fire=BL;
	ELSE IF(AL==s_f3)!FloorActive;
	@EATKEY();
	@EOI();
	$POP BX
	$POP AX
	$POP DS
}

int readint(int mapfile)
byte b,str[6];
{
	FOR(;;){
		read(mapfile,#b,1);
		IF(b=='-')||(b>='0')&&(b<='9')BREAK;
	}
	SI=0;
	FOR(;;){
		str[SI]=b;
		read(mapfile,#b,1);
		SI++;
		IF(b>'9')||(b<'0')BREAK;
	}
	str[SI]=0;
	STRTOINT(#str);
}

void ReadBitmap(word F,BSeg)
{
	SI=64;
	DI=0;
	loop(SI){
		readfar(F,BSeg,DI,64);
		DI+=256;
	}
	SI=64;
	FS=BSeg;
	loop(SI){
		DI=SI-1<<8;
		CX=64;
		loop (CX){
			FSBYTE[DI+64]=FSBYTE[DI];
			FSBYTE[DI+128]=AL;
			FSBYTE[DI+192]=AL;
			DI++;
		}
	}
}

void Lies_Daten()
word F,FileCount,i,j;
byte fnamebuf[36];
{
	i=FALSE;	//считать и загрузить карту
	IF(@PARAMCOUNT()>1){
		STR_UP(PARAMSTR(0));
		IF(byte strcmp("-FILE",PARAMSTR(0))==0){
			F=open(PARAMSTR(1),F_READ);
		}
		ELSE i++;
	}ELSE i++;
	IF(i!=FALSE){
		F=open(#DataFile,F_READ);
	}
	AnzPunkte=readint(F);	//число пунктов
	FOR(i=1;i<=AnzPunkte;i++){
		Punkte_x[i]=readint(F)*100;
		Punkte_y[i]=readint(F)*100;
		Punkte_z[i]=readint(F)*100;
	}
	AnzPolys=readint(F);
	FOR(i=0;i<AnzPolys;i++){
		Polygone_p1[i]=readint(F);
		Polygone_p2[i]=readint(F);
		Polygone_p3[i]=readint(F);
		Polygone_p4[i]=readint(F);
		PBMaps[i]=readint(F);
	}
	close(F);
	F=open(#PalName,F_READ);
	readfar(F,DS,#pal,768);
	close(F);
	j=AnzBMaps;
	FileCount=0;
	loop(j){
		FOR(SI=0,BX=FileCount;SI<35;SI++){	//получить имя файла
			fnamebuf[SI]=BMapNames[BX];
			BX++;
			IF(AL==0)BREAK;
		}
		FileCount=BX;
		F=open(#fnamebuf,F_READ);
		SI=AnzBMaps-j<<1;
		IF(j==4)||(j==3){
			BMaps[SI]=GETMEM(1024);
			ReadBitmap(F,BMaps[SI]);
			close(F);
		}
		ELSE{
			BMaps[SI]=GETMEM(256);
			readfar(F,BMaps[SI],0,4096);
			close(F);
		}
	}
	F=open(#MonsterFile,F_READ);
	FileCount=AnzMonsters;
	loop(FileCount){
		SI=FileCount-1<<1;
		Monsters[SI]=GETMEM(MonsterSX*MonsterSY/16+1);
		readfar(F,Monsters[SI],0,MonsterSX*MonsterSY);
	}
	close(F);
	F=open(#LightFile,F_READ);
	readfar(F,DS,#LightTab,4096);
	close(F);
}

void DrehePunkte()
int sinus,cosinus;
long h_x,h_z;
{
	DI=AnzPunkte;
	BX=Winkel&0b1111111110;
	sinus=sin[BX];
	cosinus=cos[BX];
	loop(DI){
		SI=DI<<2;
		h_x=Punkte_x[SI]-BPos_x;
		h_z=Punkte_z[SI]-BPos_z;
		GPunkte_x[SI]=-sinus*h_z;
		GPunkte_x[SI]+=cosinus*h_x;
		GPunkte_z[SI]=cosinus*h_z;
		GPunkte_z[SI]+=sinus*h_x;
		GPunkte_x[SI]=GPunkte_x[SI]>>7;
		GPunkte_z[SI]=GPunkte_z[SI]>>7;
		GPunkte_x[SI]+=BPos_x;
		GPunkte_z[SI]+=BPos_z;
		GPunkte_y[SI]=Punkte_y[SI];
	}
}

void InitAll()
{
	SETINTVECT(,0,CS,#IntZeroHandler);	//обработчик переполнения
	BPos_x=8000;
	Winkel=360;
	EAX=0;
	BPos_y=EAX;
	BPos_z=EAX;
	FStep=EAX;
	SStep=EAX;
	Dreh=AX;
	quit=AL;
	KAktiv=AL;
	leftdown=AL;
	rightdown=AL;
	updown=AL;
	downdown=AL;
	altdown=AL;
	sgreater=AL;
	ssmaller=AL;
	Karte=AL;
	lookup=AL;
	lookdown=AL;
	fire=AL;
	SWinkel=1;
	InitC4(40);
	Clear();
	RANDOMIZE();
	MonstWi=540;
	MonsterFrame=1;
	SI=AnzPunkte+1<<2;
	AnzPunkte++;
	Hits=0;
	Punkte_x[SI]=MonsterX;
	Punkte_y[SI]=-1000;
	Punkte_z[SI]=MonsterZ;
	HScale=300;
	VScale=-200;
	XStart=0;
	XEnd=320;
	YStart=0;
	YEnd=200;
	VMem=0;
	Versch=0;
	FVersch=0;
	FloorActive=TRUE;
	CurrentLoopflag[4]=TRUE;
}

void SwapPolys(word x,y)
{
	DI=x<<1;
	SI=y<<1;
	Polygone_p1[SI]><Polygone_p1[DI];
	Polygone_p2[SI]><Polygone_p2[DI];
	Polygone_p3[SI]><Polygone_p3[DI];
	Polygone_p4[SI]><Polygone_p4[DI];
	PBMaps[SI]><PBMaps[DI];
}

void SortierePolygone()
long PMD[maxAnzPolys],mpz;
word i,j,k1,k2;
{
	CX=AnzPolys;
	loop(CX){
		DI=CX-1<<1;
		SI=DI;
		DI<<=1;
		BX=Polygone_p1[SI]<<2;
		PMD[DI]=GPunkte_z[BX];
		BX=Polygone_p2[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p3[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p4[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		PMD[DI]=PMD[DI]>>2;
	}
	i=AnzPolys;
	loop(i){
		j=i;
		loop(j){
			k2=AnzPolys-i;
			AX<<=2;
			SI><AX;
			k1=AnzPolys-j;
			AX<<=2;
			DI><AX;
			IF(PMD[DI]>PMD[SI])SwapPolys(k1,k2);
		}
	}
	CX=AnzPolys;
	loop(CX){
		DI=CX-1<<1;
		SI=DI;
		DI<<=1;
		BX=Polygone_p1[SI]<<2;
		PMD[DI]=GPunkte_z[BX];
		BX=Polygone_p2[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p3[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		BX=Polygone_p4[SI]<<2;
		PMD[DI]+=GPunkte_z[BX];
		PMD[DI]=PMD[DI]>>2;
	}
	SI=AnzPunkte<<2;
	mpz=GPunkte_z[SI];
	CX=AnzPolys;
	loop(CX){
		SI=CX-1<<2;
		IF(long mpz>=PMD[SI])MonsterDrawPos=CX;
	}
}

void MapWall(long x1,y1,z1,x2,y2,z2,x4,y4,z4;word Map)
long blx[4],bly[4],m,dz1,dz2,dz4;
long a,e,o,u,x,y,ty,dy,itx,ts;
long bmx,w_x,w_z,K_x,K_z,hx,hz,ls;
word lightofs;
byte ClipCheck;
{
	dz1=BPos_z+150;
	ClipCheck=FALSE;
	K_x=BPos_x-x1;
	K_z=BPos_z-z1;
	w_x=x2-x1;
	w_z=z2-z1;
	IF(long z1>dz1)&&(long z2<dz1)&&(long w_z<>0){
		x2=dz1-z1+1*w_x/w_z+x1;
		z2=dz1;
		ClipCheck=TRUE;
	}
	IF(long z1<dz1)&&(long z2>dz1)&&(long z1<>z2){
		x1=dz1-z2+1*w_x/w_z+x2;
		z1=dz1;
		z4=z1;
		x4=x1;
		ClipCheck=TRUE;
	}
	IF(z1>dz1)&&(z2>dz1)ClipCheck=TRUE;
	if(ClipCheck==TRUE){
		dz1=BPos_z-z1;
		dz2=BPos_z-z2;
		dz4=BPos_z-z4;
		blx[0]=BPos_x-x1*HScale/dz1+160;
		bly[0]=BPos_y-y1*VScale/dz1+100;
		blx[4]=BPos_x-x2*HScale/dz2+160;
		bly[4]=BPos_y-y2*VScale/dz2+100;
		blx[8]=BPos_x-x4*HScale/dz4+160;
		bly[8]=BPos_y-y4*VScale/dz4+100;
		hx=blx[0]+blx[4]>>1-160;
		hz=HScale;
		bmx=hz*w_x;
		bmx-=hx*w_z;
		IF(long bmx<=0)goto RAUS;
		if(blx[0]<>blx[4]){
			IF(blx[0]<blx[4]){
				a=blx[0];
				e=blx[4];
			}
			ELSE{
				a=blx[4];
				e=blx[0];
			}
			if(a<XEnd){
				if(e>XStart){
					dz1=blx[4]-blx[0];
					m=bly[4]-bly[0]<<8/dz1;
					IF(a<XStart)a=XStart;
					IF(e>=XEnd)e=XEnd+1;
					x=e-a;
					loop(x){
						BX=x+a-1;
						if(ColReady[BX]==FALSE){
							ColReady[BX]=TRUE ;
							bmx=x+a-160;
							dz1=K_z*bmx/HScale-K_x;
							dz2=w_z*bmx/HScale-w_x;
							ls=63*dz1/dz2;
							itx=ls;
							ls=w_z*dz1/dz2+z1-BPos_z/1100;
							IF(ls>15)ls=15;
							IF(Shoot==TRUE)ls=0;
							ls=ls<<8;
							lightofs=#LightTab+ls;
							dz1=x-blx[0]+a*m>>8+bly[0];
							o=dz1;
							dz2=bly[0]-dz1+bly[8];
							u=dz2;
							ty=u-o;
							dy=63*65536/ty;
							u+=Versch;
							o+=Versch;
							IF(o<YStart){
								AX=0x1234;
								ts=ABS(o-YStart)*dy;
								o=YStart;
							}
							ELSE ts=0;
							IF(u>YEnd)u=YEnd;
							y=u-o;
							ES=0xA000;
							ty=x+a-1&3;
							ty=1<<ty;
							DX=0x3c4;
							AL=2;
							$out DX,AL
							DX++;
							AL=ty;
							$out DX,AL
							ty=x+a-1>>2;
							DI=o*80+ty+VMem;
							CX=y;
							BX=Map-1<<1;
							FS=BMaps[BX];
							SI=itx&63<<6;
							EDX=ts;
							GS=lightofs;
							loop(CX){
							 	EDX+=dy;
							 	EBX=EDX;
							 	$ROR EBX,16
							 	AL=FSBYTE[SI+BX];
							 	BX=GS;
							 	$XLAT
							 	$STOSB
							 	DI+=79;
							}
						}
					}
				}
			}
		}
	}
RAUS:
}

void DrawPolys()
word i;
long x1,y1,z1,x2,y2,z2,x4,y4,z4;
{
	i=AnzPolys;
	loop(i){
		IF(i==MonsterDrawPos){
			BX=320;
			loop(BX)MColR[BX]=ColReady[BX];
		}
		DI=i-1<<1;
		$push DI
		BX=Polygone_p1[DI]<<2;
		x1=GPunkte_x[BX];
		y1=GPunkte_y[BX];
		z1=GPunkte_z[BX];
		BX=Polygone_p2[DI]<<2;
		x2=GPunkte_x[BX];
		y2=GPunkte_y[BX];
		z2=GPunkte_z[BX];
		BX=Polygone_p4[DI]<<2;
		x4=GPunkte_x[BX];
		y4=GPunkte_y[BX];
		z4=GPunkte_z[BX];
		$pop BX
		MapWall(long x1,long y1,long z1,long x2,long y2,long z2,long x4,long y4,long z4,PBMaps[BX]);
	}
}

void _HLine(word y,c)
{
	DI=y*80+VMem;
	AX=XStart>>2;
	DI+=AX;
	CX=XEnd-XStart>>3+1&0xFFFE;
	AX=c;
	$CLD
	$REPZ
	$STOSW
}

void Cls()
int i,h,oh;
{
	ES=0xA000;
	@OUTPORTW(0x0F02,,,0x3C4);
	i=101-YStart-Versch;
	loop(i){
		h=i/7;
		h=223-h;
		IF(h<>oh)_HLine(98+i+Versch,h-1<<8+h);
		ELSE _HLine(98+i+Versch,h<<8+h);
		oh=h;
	}
	i=99-YStart+Versch;
	loop(i){
		h=100-i+Versch/12;
		h=31-h;
		IF(h<>oh)_HLine(i-1+YStart,h+1<<8+h);
		ELSE _HLine(i-1+YStart,h<<8+h);
		oh=h;
	}
}

void Clear()
{
	ES=0xA000;
	@OUTPORTW(0x0F02,,,0x3C4);
	CX=16000;
	DI=0;
	AX=0;
	$CLD
	$REPZ
	$STOSW
}

void MoveScr_C4(word x,y;byte size)
{
	AX=y;
	BX=0;
	BL=size;
	$SHL BX,1
	$MUL BX
	BX><AX;
	BX+=x;
	DX=0x3D4;
	AL=0xC;
	$OUT DX,AL
	DX++;
	AL=BH;
	$OUT DX,AL
	DX--;
	AL=0xD;
	$OUT DX,AL
	DX++;
	AL=BL;
	$OUT DX,AL
}

void FlipPages()
{
	IF(VMem==0){
		MoveScr_C4(0,0,40);
		VMem=16000;
	}
	ELSE{
		MoveScr_C4(0,200,40);
		VMem=0;
	}
}

void setpixel(word x,y,c)
{
	AX=x>>2;
	DI=80*y+VMem+AX;
	ES=0xA000;
	DX=0x3C4;
	AL=2;
	$OUT DX,AL
	CL=x&3;
	AX=1<<CL;
	DX++;
	$OUT DX,AL
	AX=c;
	$STOSB
}

void KLine(int x1,y1,x2,y2;byte c)
int xs,ys,xd,yd,d;
{
	IF(x1>x2)xs=-1;
	ELSE xs=1;
	IF(y1>y2)ys=-1;
	ELSE ys=1;
	xd=ABS(x1-x2);
	yd=ABS(y1-y2);
	IF(xd==0)d=-1;
	ELSE d=0;
	FOR(;;){
		IF(x1>=XStart)&&(x1<=XEnd)&&(y1>=YStart)&&(y1<YEnd)setpixel(x1,y1,c);
		IF(d<0){
			y1+=ys;
			d+=xd;
		}
		ELSE{
			x1+=xs;
			d-=yd;
		}
		IF(x1==x2)&&(y1==y2)BREAK;
	}
}

void DrawMap()
long x1,y1,x2,y2;
word i;
{
	i=AnzPolys;
	loop(i){
		DI=i-1<<1;
		$PUSH DI
		SI=Polygone_p1[DI]<<2;
		x1=GPunkte_x[SI]-BPos_x*4/500+160;
		y1=-GPunkte_z[SI]+BPos_z*3/500+100;
		SI=Polygone_p2[DI]<<2;
		x2=GPunkte_x[SI]-BPos_x*4/500+160;
		y2=-GPunkte_z[SI]+BPos_z*3/500+100;
		$POP DI
		KLine(int x1,int y1,int x2,int y2,PBMaps[DI]);
	}
	setpixel(160,100,15);
	setpixel(161,101,15);
	setpixel(159,101,15);
	setpixel(162,102,15);
	setpixel(158,102,15);
}

void ShowFloor()
long Sinus,Cosinus;
int y,ddx,dy,a,aah,h1,htx,hty,tx,ty,h3;
int light;
long FLD1,FLD2,h2,sx,sy,drx,dry;
{
	BX=719-Winkel&0b1111111110;
	Sinus=sin[BX];
	Cosinus=cos[BX];
	aah=101+Versch*80+VMem;
	a=XStart>>2;
	aah+=a;
	y=YEnd-101-Versch;
	ES=0xA000;
	FS=BMaps[12];
	loop(y){
		h1=YEnd-100-y-Versch;
		light=y+Versch/7;
		IF(light>15)light=15;
		IF(light<0)||(Shoot==TRUE)light=0;
		light=light<<8+#LightTab;
		IF(h1<>0)FLD2=-VScale<<14/h1;
		sx=FLD2/HScale;
		FLD1=XStart-XEnd>>1*sx;
		sy=sx*Cosinus>>5;
		ddx=sy;
		sy=sx*Sinus>>5;
		dy=sy;
		drx=-Sinus*FLD2;
		h2=Cosinus*FLD1;
		drx=h2+drx;
		drx=drx>>7;
		dry=Cosinus*FLD2;
		h2=Sinus*FLD1;
		dry=h2+dry;
		dry=dry>>7;
		htx=drx;
		hty=dry;
		h3=2;
		htx+=BPos_x<<4;
		hty+=BPos_z<<4;
		loop(h3){
			h1=2-h3<<1;
			AX=3<<h1;
			AX<<=8;
			AX+=2;
			@OUTPORTW(,,,0x3C4);
			tx=htx;
			ty=hty;
			a=aah;
			SI=XEnd-XStart+1>>2;
			DI=a;
			DX=tx;
			CX=ty;
			loop(SI){
				BH=DH;
				BL=CH;
				BX&=0x3F3F;
				AL=FSBYTE[BX];
				BX=light;
				$XLAT
				$STOSB
				DX+=ddx;
				CX+=dy;
			}
			htx+=ddx>>2;
			hty+=dy>>2;
		}
		a=DI;
		a+=XStart>>1;
		aah=a;
	}
}

void ShowCeiling()
long Sinus,Cosinus;
int y,ddx,dy,a,aah,h1,htx,hty,tx,ty,h3;
int light;
long FLD1,FLD2,h2,sx,sy,drx,dry;
{
	BX=719-Winkel&0b1111111110;
	Sinus=sin[BX];
	Cosinus=cos[BX];
	aah=100+Versch*80+VMem;
	a=XStart>>2;
	aah+=a;
	y=YEnd-99+Versch;
	ES=0xA000;
	FS=BMaps[14];
	loop(y){
		h1=YEnd-100-y+Versch;
		light=y-Versch/7;
		IF(light>15)light=15;
		IF(light<0)||(Shoot==TRUE)light=0;
		light=light<<8+#LightTab;
		IF(h1<>0)FLD2=-VScale<<14/h1;
		sx=FLD2/HScale;
		FLD1=XStart-XEnd>>1*sx;
		sy=sx*Cosinus>>5;
		ddx=sy;
		sy=sx*Sinus>>5;
		dy=sy;
		drx=-Sinus*FLD2;
		h2=Cosinus*FLD1;
		drx=h2+drx;
		drx=drx>>7;
		dry=Cosinus*FLD2;
		h2=Sinus*FLD1;
		dry=h2+dry;
		dry=dry>>7;
		htx=drx;
		hty=dry;
		h3=2;
		htx+=BPos_x<<4;
		hty+=BPos_z<<4;
		loop(h3){
			h1=2-h3<<1;
			AX=3<<h1<<8+2;
			@OUTPORTW(,,,0x3C4);
			tx=htx;
			ty=hty;
			a=aah;
			SI=XEnd-XStart+1>>2;
			DI=a;
			DX=tx;
			CX=ty;
			loop(SI){
				BH=DH;
				BL=CH;
				BX&=0x3F3F;
				AL=FSBYTE[BX];
				BX=light;
				$XLAT
				$STOSB
				DX+=ddx;
				CX+=dy;
			}
			htx+=ddx>>2;
			hty+=dy>>2;
		}
		a=DI;
		a+=XStart>>1;
		aah=a-160;
	}
}

void OutTextXY(word Str,xb,yb;byte c)
byte h;
word x,y;
{
	DI=Str;
	FS=0xF000;	//адрес знакогенератора
	SI=0xFA6E;
	do{
		y=8;
		loop(y){
			AL=DSBYTE[DI];
			AH=0;
			BX=AX<<3+y-1;
			h=FSBYTE[SI+BX];
			x=8;
			$PUSH DI
			loop(x){
				AL=h&1;
				IF(NOTZEROFLAG)setpixel(xb+x-1,yb+y,byte c);
				h>>=1;
			}
			$POP DI
		}
		xb+=8;
		DI++;
	}while(DSBYTE[DI]<>0);
}

void DisplayHits()
byte buf[6];
int start;
{
	OutTextXY("Hits : ",word XStart+1,word YStart+1,byte 15);
	WORDTODIGITS(Hits,#buf);
	start=0;
	IF(Hits>0){
		do{
			start++;
			BX=#buf+start;
		}while(DSBYTE[BX]=='0');
	}
	ELSE start=4;
	OutTextXY(#buf+start,word XStart+57,word YStart+1,15);
}

void PutResizedSprite(int x,y,ox,oy,nx,ny,SprAdr)
int bbx,ddx,dy,tx,ty,tp,a,ha,h,sofs;
{
	FS=SprAdr;
	GS=0xA000;
	sofs=0;
	ddx=ox<<7/nx;
	dy=oy<<7/ny;
	h=y+ny;
	IF(h>YEnd)ny=YEnd-y;
	h=x+nx;
	IF(h>XEnd)nx=XEnd-x-1;
	IF(x<XStart){
		nx=nx-XStart+x;
		sofs=XStart-x*ddx>>7*oy;
		x=XStart;
	}
	tx=0;
	ha=80*y+VMem;
	bbx=nx;
	loop(bbx){
		tp=tx>>7*oy+sofs;
		ty=0;
		a=nx-bbx+x>>2+ha;
		DX=0x3c4;
		AL=2;
		$out DX,AL
		CL=nx-bbx+x&3;
		AL=1<<CL;
		DX++;
		$out DX,AL
		CX=ny;
		DI=a;
		loop(CX){
			h=ty>>7+tp;
			SI=h;
			AL=FSBYTE[SI];
			IF(AL<>0)GSBYTE[DI]=AL;
			ty+=dy;
			DI+=80;
		}
		tx+=ddx;
	}
}

void PutGun()
int px,py,s,u,v;
dword dt;
{
	IF(SWinkel>359)SWinkel=0;
	SI=SWinkel<<1;
	u=sin[SI]/10*HScale/300;
	SI=SWinkel<<1;
	v=ABS(cos[SI])/20*HScale/300;
	IF(Shoot==TRUE){
		u=0;
		v=0;
	}
	px=-HScale*32/300+160+u;
	py=VScale*64/200+YEnd+v;
	s=HScale*64/300;
	GS=0x0040;
	dt=GSDWORD[0x006C]-ShootTimerStart;
	IF(dt>5){
		PutResizedSprite(px,py,64,64,s,s,BMaps[16]);
		Shoot=FALSE;
	}
	ELSE{
		PutResizedSprite(px,py,64,64,s,s,BMaps[18]);
		Shoot=TRUE;
	}
}

void BlackPal()
{
	DX=0x3C8;
	AL=0;
	$OUT DX,AL
	DX++;
	CX=768;
	loop(CX){
		$OUT DX,AL
	}
}

void FadeUp()
byte h;
{
	FOR(SI=0;SI<768;SI++){hpal[SI]=0;}
	h=64;
	loop(h){
		FOR(SI=0;SI<768;SI++){
			IF(hpal[SI]<pal[SI])hpal[SI]++;
		}
		WAITVSYNC() ;
		SI=#hpal;
		CX=768;
		DX=0x3C8;
		AL=0;
		$OUT DX,AL
		DX++;
		$CLD
		$REPZ
		$OUTSB
	}
}

void FadeDown()
byte h;
{
	h=64;
	loop(h){
		SI=768;
		loop(SI)IF(pal[SI-01]<>0)pal[SI-01]--;
		WAITVSYNC() ;
		SI=#pal;
		CX=768;
		DX=0x3C8;
		AL=0;
		$OUT DX,AL
		DX++;
		$CLD
		$REPZ
		$OUTSB
	}
}

void PutResizedMSprite(int x,y,ox,oy,nx,ny,SprAdr)
int bbx,ddx,dy,tx,ty,tp,a,ha,h,sofs,addofs;
{
	FS=SprAdr;
	GS=0xA000;
	sofs=0;
	addofs=0;
	ddx=ox<<7/nx;
	dy=oy<<7/ny;
	h=y+ny;
	IF(h>YEnd)ny=YEnd-y;
	h=x+nx;
	IF(h>=XEnd)nx=XEnd-x;
	IF(x<XStart){
		nx=nx-XStart+x;
		sofs=XStart-x*ddx>>7*oy;
		x=XStart;
	}
	IF(int y<YStart){
		ny=ny-YStart+y;
		addofs=YStart-y*dy>>7;
		y=YStart;
	}
	tx=0;
	ha=80*y+VMem;
	bbx=nx;
	loop(bbx){
		BX=nx-bbx+x;
		IF(MColR[BX]==FALSE){
			IF(BX==160)MonsterHit=TRUE;
			tp=tx>>7*oy+sofs+addofs;
			ty=0;
			a=nx-bbx+x>>2+ha;
			AL=2;
			DX=0x3C4;
			$out DX,AL
			CL=nx-bbx+x&3;
			AL=1<<CL;
			DX++;
			$out DX,AL
			CX=ny;
			DI=a;
			loop(CX){
				h=ty>>7+tp;
				SI=h;
				AL=FSBYTE[SI];
				IF(AL<>0)GSBYTE[DI]=AL;
				ty+=dy;
				DI+=80;
			}
		}
		tx+=ddx;
	}
}

byte CheckStep(long sx,sz,ddx,dz,R)
word i;
long m_x,m_z,p_x,p_z,u_x,u_z,t1,t2,t3;
byte hilf;
{
	m_x=sx+ddx;
	m_z=sz+dz;
	hilf=TRUE;
	i=AnzPolys;
	R=R*R;
	loop(i){
		DI=i-1<<1;
		SI=Polygone_p1[DI]<<2;
		DI=Polygone_p2[DI]<<2;
		u_x=Punkte_x[SI]+Punkte_x[DI];
		u_z=Punkte_z[SI]+Punkte_z[DI];
		u_x>>=1;
		u_z>>=1;
		p_x=u_x-m_x;
		p_z=u_z-m_z;
		t1=p_x*p_x;
		t1+=p_z*p_z;
		p_x=Punkte_x[SI]-m_x;
		p_z=Punkte_z[SI]-m_z;
		t2=p_x*p_x;
		t2+=p_z*p_z;
		p_x=Punkte_x[DI]-m_x;
		p_z=Punkte_z[DI]-m_z;
		t3=p_x*p_x;
		t3+=p_z*p_z;
		IF(dword t1<=R)||(dword t2<=R)||(dword t3<=R){
			hilf=FALSE;
			i=1;
		}
	}
	AL=hilf;
}

void MoveMonster()
long deltax,deltaz;
int Wi;
{
	IF(word RAND()<8000)MonstWi=RAND()/50<<1;
	if(MonstWi<720){
		SI=MonstWi;
		Wi=sin[SI]*MonsterSpeed>>7;
		deltax=Wi;
		Wi=cos[SI]*MonsterSpeed>>7;
		deltaz=Wi;
		SI=AnzPunkte<<2;
		IF(CheckStep(long Punkte_x[SI],long Punkte_z[SI],long deltax,long deltaz,long 1000)==TRUE){
			SI=AnzPunkte<<2;
			Punkte_x[SI]+=deltax;
			Punkte_z[SI]+=deltaz;
			MonsterFrame++;IF(MonsterFrame==AnzMonsters)MonsterFrame=1;
		}
	}
}

void DrawMonster()
int xp,yp,xs,ys;
long dz,mpx,mpy,mpz;
{
	SI=AnzPunkte<<2;
	mpx=GPunkte_x[SI];
	mpy=GPunkte_y[SI];
	mpz=GPunkte_z[SI];
	dz=BPos_z-mpz;
	if(long dz<=-150){
		xp=BPos_x-mpx*HScale/dz+160;
		yp=BPos_y-mpy*VScale/dz+100+Versch;
		xs=-700*HScale/dz;
		ys=1400*VScale/dz;
		xp=xp-xs;
		yp=yp-ys;
		MonsterHit=FALSE;
		SI=MonsterFrame<<1;
		dz=yp+ys;
		EAX=xp+xs;
		IF(long EAX>XStart)&&(xp<XEnd)&&(dz>YStart)&&(yp<YEnd)PutResizedMSprite(xp,yp,MonsterSX,MonsterSY,xs,ys,Monsters[SI]);
	}
	MoveMonster();
}

void HandleHits()
word i;
{
	IF(MonsterHit==TRUE)&&(CurrentLoopflag[4]==TRUE)&&(fire==TRUE){
		CurrentLoopflag[4]=FALSE;
		CurrentSPointer[4]=0;
		MonsterFrame=0;
		Shoot=TRUE;
		BX=320;
		loop(BX)ColReady[BX-0x01]=FALSE;
		DrehePunkte();
		SortierePolygone();
		IF(FloorActive==TRUE){
			ShowFloor();
			ShowCeiling();
		}
		ELSE Cls();
		DrawPolys();
		DrawMonster();
		PutGun();
		FlipPages();
		MonsterFrame=1;
		CurrentLoopflag[4]=FALSE;
		CurrentSPointer[4]=0;
		Hits++;
		i=500;
		loop(i)MoveMonster();
	}
	CurrentLoopflag[4]=TRUE;
}

void KeyControls()
long deltax,deltaz;
{
	IF(leftdown==TRUE){
		IF(altdown==TRUE)SStep=-StepSize;
		ELSE Dreh=-6;
	}
	IF(rightdown==TRUE){
		IF(altdown==TRUE)SStep=StepSize;
		ELSE Dreh=6;
	}
	Winkel+=Dreh;
	IF(Winkel>719)Winkel=0;
	IF(Winkel<0)Winkel=719;
	IF(Dreh>0)Dreh--;
	IF(Dreh<0)Dreh++;
	IF(updown==TRUE)FStep=-StepSize;
	IF(downdown==TRUE)FStep=StepSize;
	BX=Winkel&0b1111111110;
	deltaz=sin[BX]*SStep>>7;
	deltax=cos[BX]*SStep>>7;
	-deltaz;
	IF(CheckStep(long BPos_x,long BPos_z,long deltax,long deltaz,long 800)==TRUE){
		BPos_x+=deltax;
		BPos_z+=deltaz;
	}
	deltax=sin[BX]*FStep>>7;
	deltaz=cos[BX]*FStep>>7;
	-deltax;
	-deltaz;
	IF(CheckStep(long BPos_x,long BPos_z,long deltax,long deltaz,long 800)==TRUE){
		BPos_x+=deltax;
		BPos_z+=deltaz;
	}
	IF(FStep<0){
		FStep+=20;
		SWinkel+=15;
	}
	IF(FStep>0){
		FStep-=20;
		SWinkel+=15;
	}
	IF(SStep>0){
		SStep-=20;
		SWinkel+=15;
	}
	IF(SStep<0){
		SStep+=20;
		SWinkel+=15;
	}
	IF(fire==TRUE)&&(CurrentLoopflag[4]==TRUE){
		GS=0x0040;
		ShootTimerStart=GSDWORD[0x006C];
		CurrentLoopflag[4]=FALSE;
		CurrentSPointer[4]=0;
	}
	IF(Karte==TRUE)KAktiv=1-KAktiv;
	if(ssmaller==TRUE)&&(HScale>45){
		HScale-=15;
		VScale+=10;
		XStart=-160*HScale/300+160&508;
		XEnd=160*HScale/300+160&508-1;
		YStart=100*VScale/200+100;
		YEnd=-100*VScale/200+100;
		Versch=FVersch*HScale/300;
		Clear();
	}
	if(sgreater==TRUE)&&(HScale<300){
		HScale+=15;
		VScale-=10;
	 	XStart=-160*HScale/300+160&508;
		XEnd=160*HScale/300+160&508-1;
		YStart=100*VScale/200+100;
		YEnd=-100*VScale/200+100;
		Versch=FVersch*HScale/300;
		Clear();
	}
	IF(lookup==TRUE)&&(FVersch<91){
		FVersch+=4;
		Versch=FVersch*HScale/300;
	}
	IF(lookdown==TRUE)&&(FVersch>-93){
		FVersch-=4;
		Versch=FVersch*HScale/300;
	}
}

main()
{
	Lies_Daten();
	InitAll();
	GETINTVECT(#oldkeyboardhandle,KEYBOARD_INT);
	SETINTVECT(,KEYBOARD_INT,CS,#keyboardhandle);
	BX=320;
	loop(BX)ColReady[BX-0x01]=FALSE;
	BlackPal();
	DrehePunkte();
	SortierePolygone();
	ShowFloor();
	ShowCeiling();
	DrawPolys();
	PutGun();
//	OutTextXY("Push fire to start",88,96,14);
	FlipPages();
	FadeUp();	//увеличить яркость
//	do{}while(fire==FALSE);	//ждем нажатия
//	do{}while(fire==TRUE);	//а теперь отпускания
	do{
		BX=320;
		loop(BX)ColReady[BX-0x01]=FALSE;
		KeyControls();
		DrehePunkte();
		SortierePolygone();
		IF(FloorActive==TRUE){
			ShowFloor();
			ShowCeiling();
		}
    ELSE Cls();
		DrawPolys();
		DrawMonster();
		PutGun();
		DisplayHits();
		IF(KAktiv==1)DrawMap();
		FlipPages();
		WAITVSYNC();
		HandleHits() ;
	}while(quit==FALSE);
	SETINTVECT(,KEYBOARD_INT,oldkeyboardhandle[2],oldkeyboardhandle[0]);
	FadeDown();
	SETVIDEOMODE(0x03);
}
