/*  SCAVENGER by David Ashley  dash@linuxmotors.com */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>

#include "scav.h"
#include "x.h"
#include "names.h"
#include "sound.h"
#include "anim.h"
#include "edit.h"

/*void mode0(),mode1(),mode2(),mode3(),mode4(),mode5(),mode6();
 */

unsigned long slowdown;
int slowdownval;

int slowdowns[]={0xffffffff,0xfefefefe,0xeeeeeeee,0xaaaaaaaa,0x44444444};

char demolevel0[]={
0x04,0x44,0x44,0x07,0x00,0x06,0x60,0x00,0x70,0x44,0x44,0x40,
0x30,0x00,0x00,0x1a,0x00,0x06,0x60,0x00,0x11,0x00,0x00,0x03,
0x30,0x44,0x40,0x00,0x09,0x06,0x60,0x80,0x80,0x84,0x44,0x03,
0x31,0x00,0x01,0x11,0x11,0x12,0x21,0x11,0x11,0x10,0x00,0x13,
0x30,0x00,0x00,0x01,0x11,0x11,0x11,0x11,0x10,0x00,0x00,0x03,
0x31,0x00,0x00,0x0b,0x11,0x11,0x11,0x11,0x70,0x00,0x00,0x13,
0x30,0x00,0x70,0x00,0xb1,0x11,0x11,0x1b,0x00,0x07,0x00,0x03,
0x31,0x11,0xa0,0x00,0x00,0x11,0x11,0x00,0x00,0x0a,0x11,0x13,
0x30,0x00,0x00,0x00,0x00,0x01,0x10,0x00,0x00,0x00,0x00,0x03,
0x30,0x70,0x00,0x04,0x44,0x00,0x00,0x44,0x40,0x00,0x70,0x03,
0x31,0x11,0x11,0x10,0x00,0x31,0x13,0x00,0x01,0x11,0x11,0x13,
0x31,0x11,0x11,0x10,0x03,0x11,0x11,0x30,0x01,0x11,0x11,0x13,
0x30,0x00,0x00,0x00,0x31,0x11,0x11,0x13,0x00,0x00,0x00,0x03,
0x30,0x00,0x00,0x03,0x11,0x11,0x11,0x11,0x30,0x00,0x00,0x03,
0x30,0x00,0x70,0x31,0x11,0x11,0x11,0x11,0x13,0x07,0x00,0x03
};

char demomovie0[]={
0xba,0xa9,0x00,0x00,0x6c,0x6f,0x72,0x64,0x20,0x64,0x61,0x76,
0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x66,0x03,0x10,
0x02,0x01,0x00,0x05,0x03,0x2c,0x02,0x52,0x00,0x2e,0x01,0x69,
0x04,0x08,0x05,0x19,0x04,0x07,0x05,0x19,0x04,0x05,0x05,0x05,
0x00,0x2e,0x03,0x04,0x00,0x06,0x05,0x03,0x00,0x18,0x04,0x07,
0x05,0x06,0x00,0x18,0x03,0x06,0x00,0x09,0x05,0x08,0x00,0x20,
0x03,0x0e,0x00,0xcd,0x04,0x1d,0x01,0x0d,0x03,0x06,0x01,0x33,
0x03,0x0d,0x04,0x08,0x05,0x2e,0x04,0x45,0x01,0x55,0x03,0x03,
0x00,0x06,0x04,0x1f,0x00,0x21,0x03,0x10,0x06,0x18,0x03,0x07,
0x06,0x03,0x03,0x05,0x00,0x15,0x04,0x0f,0x06,0x25,0x04,0x25,
0x00,0x31,0x02,0x38,0x04,0x13,0x03,0x07,0x01,0x07,0x03,0x07,
0x01,0x0d,0x03,0x06,0x01,0x10,0x03,0x06,0x01,0x0d,0x03,0x08,
0x01,0x0b,0x03,0x09,0x01,0x0d,0x03,0x0c,0x01,0x78,0x04,0x0c,
0x01,0x05,0x00,0x04,0x01,0x28,0x00,0x42,0x01,0xbe,0x03,0x08,
0x06,0x1a,0x03,0x06,0x06,0x01,0x03,0x04,0x00,0x27,0x04,0x08,
0x05,0x07,0x00,0x29,0x03,0x2e,0x00,0x28,0x03,0x01,0x00,0x26,
0x04,0x1b,0x02,0x11,0x03,0x05,0x02,0x50,0x03,0x66,0x01,0x16,
0x00,0x05,0x02,0x01,0x00,0x26,0x04,0x07,0x06,0x05,0x00,0x19,
0x03,0x07,0x06,0x27,0x03,0x31,0x01,0x23,0x04,0x10,0x00,0x07,
0x02,0x2d,0x00,0x22,0x03,0x44,0x01,0x91,0x04,0x17,0x00,0x8b,
0x03,0x8e,0x00,0x2a,0x02,0x22,0x00,0x29,0x01,0xb1,0x04,0x06,
0x05,0x05,0x04,0x08,0x00,0x0d,0x04,0x07,0x05,0x1a,0x04,0x07,
0x05,0x06,0x00,0x2e,0x03,0x05,0x00,0x06,0x05,0x03,0x00,0x18,
0x04,0x07,0x05,0x06,0x00,0x27,0x03,0x08,0x06,0x02,0x03,0x03,
0x00,0xa8,0x04,0x52,0x01,0x83,0x03,0x19,0x01
};

uchar rotbytes[]={
0x00,0x01,0x03,0x03,0x07,0x0f,0x00,0x00,
0x01,0x02,0x06,0x0d,0x19,0x33,0x00,0x00,
0x01,0x03,0x05,0x0e,0x1e,0x3c,0x00,0x00
};

int keylist[]=
{
XK_a,'a',XK_b,'b',XK_c,'c',XK_d,'d',XK_e,'e',XK_f,'f',XK_g,'g',XK_h,'h',
XK_i,'i',XK_j,'j',XK_k,'k',XK_l,'l',XK_m,'m',XK_n,'n',XK_o,'o',XK_p,'p',
XK_q,'q',XK_r,'r',XK_s,'s',XK_t,'t',XK_u,'u',XK_v,'v',XK_w,'w',XK_x,'x',
XK_y,'y',XK_z,'z',XK_space,' ',XK_Return,10,XK_BackSpace,8,XK_Escape,27,
XK_0,'0',XK_1,'1',XK_2,'2',XK_3,'3',XK_4,'4',
XK_5,'5',XK_6,'6',XK_7,'7',XK_8,'8',XK_9,'9',
0xff
};
 
int marks[MARKMAX],*markpoint;

char libname[256];
char localname[256];
char localdirname[256];
char resourcename[256];
char rcname[256];

uchar playername[20];
uchar bestname[20];
int fallsound;
int digsound;
uchar needwhole=0,needtoptext=1,needbottomtext=1;

int xpos,ypos;
int hc=0;

int upkey,downkey,leftkey,rightkey,digleftkey,digrightkey;

int hiddens[3],hiddennum;
int ilevel;
int file;
int pty;
char gfxname0[256];
char gfxname1[256];
char gfxname2[256];
char gfxname3[256];
char *gfxnames[4]={gfxname0,gfxname1,gfxname2,gfxname3};

uchar movie[MOVIEMAX+2+20];
uchar *mpoint;
uchar *mend=movie+MOVIEMAX+20;
long score,bestscore;
int time1,time2;
int playflags;

gfxset gfxsets[NUMGFX];

uchar toptext[72];
uchar bottomtext[72];
uchar topwant[73];
uchar bottomwant[73];
uchar mtflag;

int oldmode;
int wnum;
int rowbytes;
int gran,gran64;
long pbase;
int cwin;
long cwinlow,cwinhigh;
int seed=1;
int figseg;
uchar colormap[768];
int mousex,mousey;
int numenemies;
int numgold;
int cframe;
int clevel;
int bright;
uchar temp[1024];
void (*mode)();
uchar playing;
uchar recording;
long testval=-1;
uchar wonflag=0;
uchar lostflag=0;
uchar freezing=0;
uchar randcount,randcount2;
uchar smap[16];
int mbuttons,mbuttons2;
int curx,cury;

int rotnum;

/*void mode0(),mode1(),mode2(),mode3(),mode4();
*/

uchar levelbuff[LEVELSIZE];
uchar levelback[LEVELSIZE];
uchar movings[LEVELSIZE];
uchar randoms[LEVELSIZE];
int xcomp[640];
int ycomp[480];
int ycomp2[480];
int centerx[640];
int centery[480];

int cantascend[LX];
int cantdescend[LX];
int enterableo[LX+2];
int *enterable=enterableo+1;
int stable[LX];
int upgoal[LX];
int downgoal[LX];
int bits[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
uchar calced;

struct sprite spritelist[MAXSPRITES];

uchar *flashat;
int flashx,flashy;
int flashtimer;
struct sprite *flashsprite;

struct entity entities[8];

struct dig diggings[MAXDIG];


int skips[192];

int mickeyx,mickeyy;
int mx,my;

int offscreen;
int vp=0;
int tileseg;

uchar *wantiff="Picture file must be in IFF format.\n";

uchar *picloc=0,*picput;
int bmw,bmh,byteswide,depth;
int ihand,ileft;
uchar *itake;
uchar ibuff[IBUFFLEN];

int bestdir,bestdist,aboveenemy,belowenemy,mask,thisdir,aboveplayer,belowplayer;

void *configtab[]=
{
"upkey",&upkey,setint,
"downkey",&downkey,setint,
"leftkey",&leftkey,setint,
"rightkey",&rightkey,setint,
"digleftkey",&digleftkey,setint,
"digrightkey",&digrightkey,setint,
"background",gfxname0,setstring,
"hero",gfxname1,setstring,
"enemy",gfxname2,setstring,
"hidden",gfxname3,setstring,
"slowdown",&slowdownval,setint,
0,0,0
};







long readlong()
{
long val1=0;

	val1=myci()<<24L;
	val1|=myci()<<16L;
	val1|=myci()<<8;
	val1|=myci();

	return val1;
}
int rbs()
{
	int ch;
	ch=myci();
	if(ch>127) ch-=256;
	return ch;
}

int myci()
{
	if(!ileft)
	{
		ileft=read(ihand,ibuff,IBUFFLEN);

		if(!ileft) return -1;
		itake=ibuff;
	}
	ileft--;
	return *itake++;
}

uchar *doiff(uchar *name)
{
long type,size;
uchar comp;
int i,j;
/*
   int k,d,d1;
   uchar *p1;
 */
uchar lines[8][MAXBYTES];
uchar temparea[256];

	if(!picloc)
	{
		picloc=malloc(64000);
		if(!picloc)
			return "Could not allocate memory for picture.\n";
	}
	picput=picloc;
	sprintf(temparea,"%s/%s/%s",localname,localdirname,name);
	ihand=open(temparea,O_RDONLY);
	if(ihand==-1)
	{
		sprintf(temparea,"%s/%s",libname,name);
		ihand=open(temparea,O_RDONLY);
		if(ihand==-1)
			return "Cannot open picture file.\n";
	}
	ileft=0;
	type=readlong();
	if(type!=FORM) {close(ihand);ihand=0;return wantiff;}
	readlong();
	type=readlong();
	if(type!=ILBM) {close(ihand);ihand=0;return wantiff;}
	bmw=bmh=byteswide=0;
	while( (type=readlong()) )
	{
		if((type&0xffffffff)==0xffffffffL) break;
		size=readlong();
		if(size&1) size++;
		if(type==BMHD)
		{
			for(i=0;i<size;i++) temparea[i]=myci();
			bmw=temparea[0]*256+temparea[1];
			byteswide=(bmw+7)>>3;
			bmh=temparea[2]*256+temparea[3];
			depth=temparea[8];
			comp=temparea[10];
/*printf("(%d,%d) %d bpp\n",bmw,bmh,depth);*/
		} else
		if(type==BODY)
		{
			for(i=0;i<bmh;i++)
			{
				for(j=0;j<depth;j++)
				{
					unpack(byteswide,lines[j]);
				}
				debit ((uchar *)lines, byteswide, depth);
			}
		} else
		if(type==CMAP)
		{
			for(i=0;i<size;i++)
				colormap[i]=myci();
		} else
		{
			while(size--) myci();
		}
	}
	close(ihand);ihand=0;

	return 0;
}

void debit(uchar *lines, int numb, int deep)
{
int i,j;
	for(i=0;i<numb;i++)
	{
		for(j=7;j>=0;j--)
			*picput++ = fixbyte(lines+i,1<<j,deep);
	}
}

int fixbyte(char *take,int bit,int deep)
{
int res=0,mask=1;
	while(deep--)
	{
		if(*take & bit)
			res|=mask;
		mask+=mask;
		take+=MAXBYTES;
	}
	return res;
}

void unpack(int len, uchar *where)
{
	int ch,ch2;
	while(len>0)
	{
		ch=rbs();
		if(ch>=0)
		{
			ch++;
			len-=ch;
			while(ch--) *where++=readbyte();
		} else
		{
			ch=1-ch;
			len-=ch;
			ch2=readbyte();
			while(ch--) *where++=ch2;
		}
	}
}

int getresource(int num,char *put,int len)
{
int input;
int got;
uchar buff[8];
long offset,length;
long max;

	input=open(resourcename,O_RDONLY);
	if(input<0) return -1;
	got=read(input,buff,8);
	if(got!=8) return -2;
	if (strncmp(buff,"SCAV",4)) return -3;
	max=(buff[4]<<24) | (buff[5]<<16) | (buff[6]<<8) | buff[7];
	if(num>=max) return 0;
	lseek(input,(num+1)<<3,SEEK_SET);
	got=read(input,buff,8);
	offset=(buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3];
	length=(buff[4]<<24) | (buff[5]<<16) | (buff[6]<<8) | buff[7];
	if(len>length) len=length;
	if(!offset || !len) return 0;
	lseek(input,offset,SEEK_SET);
	got=read(input,put,len);
	close(input);
	return got;
}

int rlout(int handle, long value)
{
char buff[4];
	buff[0]=value>>24;
	buff[1]=value>>16;
	buff[2]=value>>8;
	buff[3]=value;
	return write(handle,buff,4) == 4;
}

int putresource(int num,uchar *take,int len)
{
int input,output;
uchar buff[8];
long offset,length;
char bakname[128];
int err,got;
long headers[MAXRESOURCES*2];
long max,oldnum,count;
/* int j,k;
   long cutoff;
 */
int i, delta;
char copybuff[1024];

	if(num>=MAXRESOURCES) return -5;
	sprintf(bakname,"%s.bak",resourcename);
	input=open(resourcename,O_RDONLY);
	if(input==-1) return -4;
	got=read(input,buff,8);
	if(got!=8) return -2;
	if(strncmp(buff,"SCAV",4)) return -3;
	output=creat(bakname,00600);
	if(output==-1) {close(input);return -6;}

	oldnum=(buff[4]<<24) | (buff[5]<<16) | (buff[6]<<8) | buff[7];
	for(i=0;i<oldnum+oldnum;i++)
	{
		got=read(input,buff,4);
		headers[i]=(buff[0]<<24) | (buff[1]<<16) | (buff[2]<<8) | buff[3];
	}
	while(i<=num+num+1)
		headers[i++]=0;
	max=i>>1;
	offset=headers[num+num];
	length=headers[num+num+1];
	delta=(max-oldnum)<<3;
	if(offset)
		for(i=0;i<max;i++)
			if(headers[i+i]>offset)
				headers[i+i]-=length;
	if(delta)
		for(i=0;i<max;i++)
			if(headers[i+i])
				headers[i+i]+=delta;
	if (write(output,"SCAV",4) != 4) return -200;
	if (!rlout(output,max))	return -200;
	headers[num+num]=0;
	headers[num+num+1]=len;
	for(i=0;i<max+max;i++)
		if (!rlout(output,headers[i])) return -200;

	if(offset)
	{
		offset-=(oldnum+1)<<3;
		count=offset;
		while(count)
		{
			got=count>1024 ? 1024 : count;
			if (read(input,copybuff,got) != got) return -200;
			if (write(output,copybuff,got) != got) return -200;
			count-=got;
		}
		lseek(input,length,SEEK_CUR);
	}
	offset += (max+1) << 3;
	for(;;)
	{
		got=read(input, copybuff, 1024);
		if (!got) break;
		if (write (output, copybuff, got) != got) return -200;
		offset += got;
	}
	if(len)
	{
		if (write (output, take, len) != len) return -200;
		lseek (output, (num+1) << 3, SEEK_SET);
		if (!rlout (output, offset)) return -200;
	}
	close (input);
	if (fsync(output)) return -200;
	if (close(output)) return -200;
	err=rename(bakname,resourcename);
	if(err) return -1;
	return (0);
}


int loadlevel(int num)
{
int err;
uchar tlevel[180];
uchar *p1,*p2;
int i,j;

	memset(levelbuff,BORDER,LEVELSIZE);
	if(num)
	{
		err = getresource ((num-1) << 1, tlevel, 180);
		if (err != 180) return (0);
	} else
		bcopy(demolevel0,tlevel,180);

	p2=levelbuff+30;
	p1=tlevel;
	for(j=0;j<15;j++)
	{
		for(i=0;i<12;i++)
		{
			*p2++ = *p1>>4;
			*p2++ = *p1++ & 0x0f;
		}
		p2+=4;
	}
	return (1);
}

int myrand()
{
int ttt;
	ttt=seed;
	seed>>=1;
	if(ttt&1) seed^=0xb400;
	return seed;
}

void paintlevel()
{
int i,j;
uchar *p;
int w;
uchar v;
/* uchar *map;
 */
uchar ttt[64];

	p=levelbuff+30;
	for(j=0;j<LY;j++)
	{
		for(i=0;i<LX;i++)
		{
			w=*p;
			if(w!=BRICK && w!=CEMENT && w!=HIDDEN && w!=FAKE)
			{
				w=*((p-levelbuff)+randoms)*12;
				puttile(i*24+XOFF,j*24+YOFF,w);
				v=smap[*(p-28)];
				v|=smap[*(p-29)]<<1;
				v|=smap[*(p-1)]<<2;
				if(v) puttile(i*24+XOFF,j*24+YOFF,SHADOWFIGS+v);
			}
			p++;
		}
		p+=4;
	}
	p=levelbuff+30;
	for(j=0;j<LY;j++)
	{
		for(i=0;i<LX;i++)
		{
			w=*p;
			if(w) puttile(i*24+XOFF,j*24+YOFF,w + 12*randoms[p-levelbuff]);
			p++;
		}
		p+=4;
	}
	for(i=0;i<24;i++)
	{
		j=i*24+XOFF;
		puttile(j,YOFF-24,BORDERFIGS+1);
		puttile(j,YOFF+24*15,BORDERFIGS+7);
	}
	for(i=0;i<15;i++)
	{
		j=i*24+YOFF;
		puttile(XOFF-24,j,BORDERFIGS+3);
		puttile(XOFF+24*24,j,BORDERFIGS+5);
	}
	puttile(XOFF-24,YOFF-24,BORDERFIGS);
	puttile(XOFF+24*24,YOFF-24,BORDERFIGS+2);
	puttile(XOFF+24*24,YOFF+15*24,BORDERFIGS+8);
	puttile(XOFF-24,YOFF+15*24,BORDERFIGS+6);
	if(playing)
	{
		sprintf(ttt,"PLAYER:%s",playername);
		pty=YOFF+16*TY;
		centertext(ttt);
	}
}

void dectime()
{
/* int i;
   uchar *p1,*p2;
 */
 
	if(!playing || !time1 || freezing) return;
	time2++;
	if(time2<60) return;
	time2=0;
	time1--;
	mtflag++;
}

void fixtext()
{
int i,x,y;
uchar ch,*p1,*p2;

	if(!playing) return;
	p1=toptext;
	p2=topwant;
	i=72;
	y=YOFF-36;
	x=XOFF;
	while(i--)
	{
		if(*p1++!=*p2++)
		{
			ch=p1[-1]=p2[-1];
			writechar(x,y,ch /*,TCOLOR*/);
			needtoptext=1;
		}
		x+=8;
	}
	p1=bottomtext;
	p2=bottomwant;
	i=72;
	y=YOFF+15*24+24;
	x=XOFF;
	while(i--)
	{
		if(*p1++!=*p2++)
		{
			ch=p1[-1]=p2[-1];
			writechar(x,y,ch /*,TCOLOR*/);
			needbottomtext=1;
		}
		x+=8;
	}
}

void writestring(str,x,y,color)
int x,y,color;
uchar *str;
{
	while(*str)
	{
		writechar(x,y,*str++ /*,color*/);
		x+=8;
	}
}

void storesprites()
{
int i;
struct sprite *p;
	p=spritelist;
	for(i=0;i<MAXSPRITES;i++)
	{
		if(p->spriteflags & SPRITEACTIVE)
		{
			store(p->spritex,p->spritey,i);
			p->spriteflags |= SPRITESTORING;
		}
		p++;
	}
}

void restoresprites()
{
int i;
struct sprite *p;
	p=spritelist;
	for(i=0;i<MAXSPRITES;i++)
	{
		if(p->spriteflags & SPRITESTORING)
		{
				restore(p->spritex,p->spritey,i);
				mark(p->spritex,p->spritey);
				p->spriteflags ^= SPRITESTORING;
		}
		p++;
	}
}
void drawsprites()
{
int i;
struct sprite *p;
	p=spritelist+MAXSPRITES;
	for(i=0;i<MAXSPRITES;i++)
	{
		--p;
		if(p->spriteflags & SPRITEACTIVE)
		{
			mark(p->spritex,p->spritey);
			puttile(p->spritex,p->spritey,p->spritefig);
		}
	}
}

void chainlevel(int num)
{
	clevel=num;
	fadeout();
	loadlevel(clevel);
	restoresprites();
	paintlevel();
	storesprites();
	drawsprites();
	copyup();
	fadein();
}

struct sprite *newsprite()
{
int i;
struct sprite *sp;

	sp=spritelist;
	for(i=0;i<MAXSPRITES;i++)
		if(!(sp->spriteflags & (SPRITEACTIVE | SPRITESTORING)))
			break;
		else	sp++;
	sp->spriteflags=SPRITEACTIVE;
	sp->spritex=0;
	sp->spritey=0;
	sp->spritefig=0;
	return sp;
}

void initplay()
{
int i,lr;
uchar *p1,*p2,ch;
int flashes[3];
int flashnum;
struct entity *plent,*enent;
int err;

	slowdown=slowdowns[slowdownval%5];
	if(recording) randcount=randcount2%24;
	score=0;
	time1=100;
	time2=0;
	mtflag=1;
	mpoint=movie+20;
	bestname[0]=0;
	bestscore=0;
	if(clevel)
		err = getresource (1 + ((clevel-1) << 1), movie, MOVIEMAX+20);
	else
		bcopy(demomovie0,movie,err=sizeof(demomovie0));
	if(err>0)
	{
		bestscore=movie[0] | (movie[1]<<8) | (movie[2]<<16) | (movie[3]<<24);
	}
	if(recording)
	{
		*mpoint++=randcount;
		*mpoint=0;
	} else
	{
		if(err>0)
		{
			bcopy(movie+4,bestname,16);
			mpoint=movie+20;
			randcount=*mpoint++;
		}
	}
	for(i=0;i<72;i++)
		toptext[i]=bottomtext[i]=topwant[i]=bottomwant[i]=0;
	lostflag=wonflag=0;
	playflags=0;
	freezing=1;
	for(i=0;i<MAXDIG;i++)
		diggings[i].digtimer=0;
	numenemies=0;
	numgold=0;
	p1=levelbuff;
	p2=levelback;
	flashnum=0;
	hiddennum=0;
	enent=plent=entities;
	for(i=0;i<LEVELSIZE;i++)
	{
		if(i%28 < 14) lr=4; else lr=3;
		movings[i]=0;
		*p2++=ch=*p1;
		if(ch==HIDDEN)
		{
			ch=BRICK;
			if(hiddennum<3)
				hiddens[hiddennum++]=i;
		}
		if(ch==ESCAPE) ch=EMPTY;
		if(ch==FLASHING)
		{
			ch=GOLD;
			if(flashnum<3)
				flashes[flashnum++]=i;
		}
		if(ch==HERO)
		{
			ch=EMPTY;
			plent->enx=XCONV(i);
			plent->eny=YCONV(i);
			plent->enat=p1;
			plent->enflags=PLAYER;
			plent->endir=-1;
			plent->enwhat=lr;
			plent->enacount=0;
		movings[i]|=plent->enmask=0x80;

		} else if(ch==ENEMY)
		{
			ch=EMPTY;
			numenemies++;
			enent++;
			enent->enx=XCONV(i);
			enent->eny=YCONV(i);
			enent->ensprite=newsprite();
			enent->enat=p1;
			enent->enflags=0;
			enent->enwhat=lr;
			enent->enacount=0;
			movings[i]|=enent->enmask=0x80>>numenemies;
		}
		*p1++=ch;
		if(ch==GOLD) numgold++;
	}
	entities[0].ensprite=newsprite();
	rotnum=numenemies-1;
	rethink();
	if(flashnum)
	{
		i=flashes[randcount%flashnum];
		flashat=levelbuff+i;
		flashsprite=newsprite();
		flashx=XCONV(i);
		flashy=YCONV(i);
		flashtimer=0;
	} else flashat=0;
}

void maketext()
{
	sprintf(topwant,"LEVEL %04d       BEST %05ld  %-18sBONUS %03d00   SCORE %05ld",clevel,bestscore,bestname,time1,score);
	mtflag=0;
}

void initdemo()
{
int err;

	fadeout();
	mode=mode0;
	playing=1;
	recording=0;

	if(!loadlevel(clevel))
		clevel=0;
	if(clevel)
	{
		err = getresource (1 + ((clevel-1) << 1), movie, 20);
		if(err<=0) clevel=0;
	}

	loadlevel(clevel);
	restoresprites();
	initplay();
	paintlevel();
	maketext();
	fixtext();
	changesprites();
	storesprites();
	drawsprites();
	fadein();
	copyup();
}

void startgame()
{
/* int i;
 */
	fadeout();
	playing=1;
	recording=1;

	restoresprites();
	initplay();
	paintlevel();
	maketext();
	fixtext();
	changesprites();
	storesprites();
	drawsprites();
	copyup();
	fadein();
}

void spritesoff()
{
struct entity *en;
struct sprite *sp;
struct dig *adig;
int i;

	restoresprites();
	en=entities;
	for(i=0;i<numenemies+1;i++)
		SPRITEOFF((en++)->ensprite);
	adig=diggings;
	for(i=0;i<MAXDIG;i++)
	{
		if(adig->digtimer && (sp=adig->digsprite))
			SPRITEOFF(sp);
		adig++;
	}
	if(flashat) {SPRITEOFF(flashsprite);flashat=0;}
}

void lefttext(uchar *txt)
{
int ptx;
uchar ch;

	ptx=100;
	while ((ch=*txt++))
	{
		writechar(ptx,pty,ch /*,TCOLOR*/);
		ptx+=8;
	}
	pty+=16;
}

void centertext (uchar *txt)
{
int	ptx;
uchar ch;

	ptx=320-8*(strlen(txt)/2);
	while((ch=*txt++))
	{
		writechar(ptx,pty,ch /*,TCOLOR*/);
		ptx+=8;
	}
	pty+=16;
}

void nextlevel (int type)
{
uchar ttt[64];

	fadeout();
	spritesoff();

	if(type)
	{
		clear();
		pty=150;
		centertext("YOU PASSED THE LEVEL!");
		sprintf(ttt,"SCAVENGED      %05ld\n",score);
		centertext(ttt);
		sprintf(ttt,"TIME BONUS     %03d00\n",time1);
		centertext(ttt);
		score+=time1*100;
		if(!(playflags&HASTRAPPED))
		{
			centertext("NO TRAPPING    20000\n");
			score+=20000;
		} else if(!(playflags&HASKILLED))
		{
			centertext("NO KILLING     10000\n");
			score+=10000;
		}
		centertext("-----------");
		sprintf(ttt,      "\nFINAL SCORE    %05ld\n",score);
		centertext(ttt);
		if(score>bestscore)
		{
			centertext("");
			sprintf(ttt,"YOU JUST GOT THE HIGH SCORE FOR LEVEL %04d!!!",clevel);
			centertext(ttt);

			movie[0]=score;
			movie[1]=score>>8;
			movie[2]=score>>16;
			movie[3]=score>>24;
			bcopy(playername,movie+4,16);
/*
{
int tf;
	tf=creat("tmovie",00600);
	if(tf!=-1)
	{
		write(tf,movie,mpoint-movie+2);
		close(tf);
	}
}
*/
			putresource(((clevel-1)<<1)+1,movie,mpoint-movie+2);
		}
		fadein();
		copyup();
		for(;;)
		{
			pause();
			scaninput();
			if(checkdown(XK_space) || checkdown(XK_Escape)) /* space or ESC */
				break;
		}
		fadeout();
		clear();
	}

	clevel++;
	if(!loadlevel(clevel))
	{
		clevel=1;
		loadlevel(clevel);
	}
	initplay();
	paintlevel();
	maketext();
	fixtext();
	changesprites();
	storesprites();
	drawsprites();
	copyup();
	fadein();
}

void samelevel()
{
int i;
	fadeout();
	spritesoff();
	for(i=0;i<LEVELSIZE;i++)
		levelbuff[i]=levelback[i];
	initplay();
	paintlevel();
	changesprites();
	storesprites();
	drawsprites();
	copyup();
	fadein();
}

void endplay()
{
int i;

	quiet();
	restoresprites();
	spritesoff();
	playing=0;
	for(i=0;i<LEVELSIZE;i++) levelbuff[i]=levelback[i];
}

int whichfig (struct entity *en)
{
int a,what;
uchar ch;

	ch=*COMPXY(en->enx,en->eny);
	a=en->enacount>>1;
	what=en->enwhat;
	switch(what)
	{
	case 0:
		if(ch==LADDER)
			a+=72;
		else a+=12;
		break;
	case 1:
		a+=72;				/* climbing up */
		break;
	case 2:
		if (en->enflags&FALLING || en->enflags&UNSTABLE)
			a+=60;			/* falling */
		else
			a+=72;			/* climbing down */
		break;
	case 3:
		if(ch==RAIL)
			a+=36;			/* climbing left on rail */
		else
			a+=12;			/* running left */
		break;
	case 4:
		if(ch==RAIL)
			a+=48;			/* climbing right on rail */
		else
			a+=24;			/* running right */
		break;
	case 5:
		a+=84;				/* dig left */
		break;
	case 6:
		a+=90;				/* dig right */
		break;
	}
	return a;
}

void changesprites()
{
struct sprite *sp;
struct entity *en;
struct dig *adig;
int time;
int i;

	if(playing) {

	en=entities;
	sp=en->ensprite;
	sp->spritex=en->enx;
	sp->spritey=en->eny;
	i=sp->spritefig=whichfig(en)+96;
/*	if(i==12+96+3 || i==24+96+3 || i==12+96+9 || i==24+96+9) makesound(4);*/
	for(i=0;i<numenemies;i++)
	{
		++en;
		sp=en->ensprite;
		sp->spritex=en->enx;
		sp->spritey=en->eny;
		sp->spritefig=whichfig(en)+192;
		if(en->enflags & HIDDENTYPE) sp->spritefig+=96;
	}
	adig=diggings;
	for(i=0;i<MAXDIG;i++)
	{
		time=adig->digtimer;
		sp=adig->digsprite;
		if(time>=1 && time<=DIGTIME)
		{
			sp->spritex=adig->digx;
			sp->spritey=adig->digy;
			sp->spritefig=DIGFIGS+1+((time-1)>>2);
		} else if(time>=DIGLIFE-DIGTIME)
		{
			sp->spritex=adig->digx;
			sp->spritey=adig->digy;
			sp->spritefig=DIGFIGS+((DIGLIFE-time-1)>>2);
		}
		adig++;
	}
	if(flashat)
	{
		flashsprite->spritex=flashx;
		flashsprite->spritey=flashy;
		if(flashtimer<8) flashsprite->spritefig=FLASHFIGS+(flashtimer>>1);
		else if(flashtimer<16) flashsprite->spritefig=FLASHFIGS+7-(flashtimer>>1);
		else	flashsprite->spritefig=DIGFIGS; /* empty fig */
		flashtimer++;
		flashtimer&=63;
	}
	}
}

void fadein()
{
}
void fadeout()
{
}

int iterate()
{
uchar pausing;
	pause();
	randcount2++;
	pausing=0;
	do
	{
		if(pausing) pause();
		scaninput();
		if((checkpressed(XK_Alt_L) || checkpressed(XK_Meta_L)) && 
			checkdown(XK_x)) return 1; /* alt-x */
		if(checkdown(XK_Pause) && playing) pausing^=1;
	} while(pausing);

	mbuttons=(checkbutton(1) ? 1 : 0) | (checkbutton(3) ? 2 : 0);
	mbuttons2=(checkbuttondown(1) ? 1 : 0) | (checkbuttondown(3) ? 2 : 0);

	xpos=getmousex();
	ypos=getmousey();
	if(xpos<0) xpos=0;
	if(xpos>639) xpos=639;
	if(ypos<0) ypos=0;
	if(ypos>479) ypos=479;
	curx=xpos;
	cury=ypos;

	restoresprites();
	animprocess();
	editprocess();
	slowdown=(slowdown>>1) | ((slowdown&1)<<31);
	if(slowdown&1)
	{
		movement();
		dodiggings();
		dectime();
	}
	if(playing && mtflag) maketext();
	fixtext();
	if(slowdown&1)
		changesprites();
	storesprites();
	drawsprites();
	if(needwhole)
	{
		markpoint=marks;
		needwhole=0;
		copyup();
	} else
	{
		domarks();
		if(needtoptext)
		{
			needtoptext=0;
			copyupxysize(TEXTX,TOPTEXTY,12*72,12);
		}
		if(needbottomtext)
		{
			needbottomtext=0;
			copyupxysize(TEXTX,BOTTOMTEXTY,12*72,12);
		}
	}
/*	xflush();*/
	xsync();
	mode();
	if(checkdown(XK_Sys_Req))
		capturescreen();
	return 0;
}


uchar *getgfx(int num)
{
uchar *p;
gfxset *gs;

	p=doiff(gfxnames[num]);
	if(p)
	{
		sprintf(temp,"%s:%s",gfxnames[num],p);
		return temp;
	}
	gs=gfxsets+num;
	if(!gs->gs_pic)
		gs->gs_pic=malloc(64000);
	if(!gs->gs_pic)
		return "Could not allocate memory for gfx set.\n";
	bcopy(picloc,gs->gs_pic,64000);
	bcopy(colormap,gs->gs_colormap,768);
	return 0;
}


void setint(char *ip,int *op)
{
	sscanf(ip,"%x",op);
}

void setstring(char *ip,char *op)
{
	strcpy(op,ip);
}


void getconfig()
{
int i,j,md;
uchar *p,ch;
uchar name[16];
void **pnt;
/* uchar ahex;
 */
char name2[256];
void (*func)();
char *op;

	sprintf(name2,"%s/%s/%s",localname,localdirname,rcname);
	file=open(name2,O_RDONLY);
	if(file==-1) return;
	i=read(file,temp,1023);
	temp[i]=0;
	p=temp;
	md=0;
	for(;;)
	{
		ch=tolower(*p++);
		if(!ch) break;
		if(ch==13) continue;
		switch(md)
		{
		case 0:
			if(ch==10) break;
			if(ch=='#' || ch<'a' || ch>'z')
			{
				md=1;
				break;
			}
			i=0;
			md=2;
		case 2:
			if(ch<'a' || ch>'z')
			{
				if(ch=='=')
				{
					name[i]=0;
					i=0;
					pnt=configtab;
					j=0;
					while(*pnt)
					{
						if(!strcmp(*pnt++,name))
							break;
						j++;
						pnt+=2;
					}
					op=name2;
					func=pnt[1];
					md=3;
				} else
					md=0;
				break;
			}
			if(i<15)
			{
				name[i++]=ch;
			} else
				md=0;
			break;
		case 1:
			if(ch==10)
				md=0;
			break;
		case 3:
			if(ch && ch!=10)
				*op++=ch;
			else
			{
				*op=0;
				if(func)
					func(name2,*pnt);
				md=0;
			}
			break;
		}
	}
}

void remslash(char *str)
{
int i;
	i=0;
	while(*str) {i++;str++;}
	while(i-- && *--str=='/') *str=0;
}

int dupfile(char *src, char *dest)
{
int input,output,len,res;
char buff[1024];

	input=open(src,O_RDONLY);
	if(input==-1) return -1;
	output=creat(dest,00600);
	if(output==-1)
	{
		close(input);
		return -2;
	}
	do
	{
		len=read(input,buff,1024);
		res=write(output,buff,len);
		if(len!=res)
		{
			close(input);
			close(output);
			return -3;
		}
	} while(len);
	return 0;
}

void finddata()
{
char *p;
int file,err;
/* char temp2[256];
 */
	strcpy(libname,LIBNAME);
	strcpy(localdirname,LOCALDIRNAME);
	strcpy(rcname,RCNAME);

	p=getenv(ENVHOME);
	if(!p)
	{
		printf("Cannot find %s environment variable!\n",ENVHOME);
		exit(1);
	}
	strcpy(localname,p);
	remslash(localname);
	sprintf(temp,"%s/%s",localname,localdirname);
	file=open(temp,O_RDONLY);
	if(file==-1)
	{
		printf("No %s/!!! Setting one up...\n",temp);
		err=mkdir(temp,00700);
		if(err==-1)
		{
			printf("Failed to create directory %s, exiting\n",temp);
			exit(1);
		}
	}

	sprintf(resourcename,"%s/%s/%s",localname,localdirname,LEVELSNAME);
	file=open(resourcename,O_RDONLY);
	if(file==-1)
	{
		printf("No %s, setting one up...\n",resourcename);
		sprintf(temp,"%s/%s",libname,LEVELSNAME);
		printf("Trying to copy %s...",temp);
		err=dupfile(temp,resourcename);
		if(err)
		{
			printf("failed to copy\n");
			exit(1);
		}
		printf("...copied.\n");
	} else close(file);
	makercname(temp);
	file=open(temp,O_RDONLY);
	if(file==-1)
	{
		printf("No %s, setting one up...\n",temp);
		strcpy(gfxname0,"redbrick.lbm");
		strcpy(gfxname1,"regularguy.lbm");
		strcpy(gfxname2,"badguy.lbm");
		strcpy(gfxname3,"spiralthing.lbm");
		upkey=XK_KP_Up;
		downkey=XK_KP_Begin;
		leftkey=XK_KP_Left;
		rightkey=XK_KP_Right;
		digleftkey=XK_KP_Home;
		digrightkey=XK_KP_Page_Up;
		if(!makercfile(temp))
		{
			printf("Couldn't create %s\n",temp);
			exit(1);
		}
	}
}

void makercname(char *str)
{
	sprintf(str,"%s/%s/%s",localname,localdirname,rcname);
}

int makercfile(char *str)
{
FILE *file;
	file=fopen(str,"w");
	if(!file) return 0;
	fprintf(file,"background=%s\n",gfxname0);
	fprintf(file,"hero=%s\n",gfxname1);
	fprintf(file,"enemy=%s\n",gfxname2);
	fprintf(file,"hidden=%s\n",gfxname3);
	fprintf(file,"upkey=%x\n",upkey);
	fprintf(file,"downkey=%x\n",downkey);
	fprintf(file,"leftkey=%x\n",leftkey);
	fprintf(file,"rightkey=%x\n",rightkey);
	fprintf(file,"digleftkey=%x\n",digleftkey);
	fprintf(file,"digrightkey=%x\n",digrightkey);
	fprintf(file,"slowdown=%d\n",slowdownval);
	fclose(file);
	return 1;
}

void thandler(int val)
{
	signal(SIGALRM,thandler);
	hc++;
}

int main (int argc, char** argv)
{
struct itimerval itval;
int i,j;
/* long time1;*/
uchar *err;

	finddata();
	if(fork()) return 0;

	openx(argc,argv);
	soundinit();
	for(i=0;i<NUMGFX;i++)
		gfxsets[i].gs_pic=0;
	getconfig();
	if(argc>1)
	{
		ilevel=atoi(argv[1]);
	}
	else ilevel=1;
	animinit();
	editinit();
	fontinit();
	err=getenv(ENVUSER);
	if(err)
		strncpy(playername,err,16);
	else
		strcpy(playername,"anonymous");
	sprintf(temp,"%s/%s/%s",localname,localdirname,PLAYERNAME);
	file=open(temp,O_RDONLY);
	if(file!=-1)
	{
		read(file,playername,16);
		close(file);
	}
	for(i=0;i<16;i++) smap[i]=0;
	smap[BRICK]=1;
	smap[CEMENT]=1;
	smap[HIDDEN]=1;
	smap[FAKE]=1;
	smap[DUGBRICK]=1;
	for(i=0;i<LEVELSIZE;i++) randoms[i]=myrand() & 3;

	for(i=0;i<MAXSPRITES;i++) spritelist[i].spriteflags=0;
	markpoint=marks;

	dumpgfx();
	for(i=0;i<NUMGFX;i++)
	{
		err=getgfx(i);
		if(err)
		{
			puts(err);
			return;
		}
		createinout(i);
	}
	getcolors();
	getfigures();

	for(i=0;i<640;i++)
		xcomp[i]=(i-XOFF+2*TX+(TX/2))/TX;
	for(i=0;i<480;i++)
	{
		ycomp[i]=(i-YOFF+TY+(TY/2))/TY;
		ycomp2[i]=ycomp[i]*28;
	}
	for(i=0;i<640;i++)
	{
		j=(i+TX-(XOFF%TX)) % TX;
		if(j==0) centerx[i]=0;
		else if(j<TX/2) centerx[i]=-2;
		else centerx[i]=2;
	}
	for(i=0;i<480;i++)
	{
		j=(i+TY-(YOFF%TY)) % TY;
		if(j==0) centery[i]=0;
		else if(j<TY/2) centery[i]=-2;
		else centery[i]=2;
	}

	changesprites();

	clevel=0;
	initdemo();

	itval.it_interval.tv_sec=itval.it_value.tv_sec=0;
	itval.it_interval.tv_usec=itval.it_value.tv_usec=20000;
	thandler(0);
	setitimer(ITIMER_REAL,&itval,NULL);

	while(!iterate());

	endsound();
	closex();
	if(testval>=0) printf("Testval=%lx\n",testval);
	return 0;
}

void createhidden(int at)
{
struct entity *enent;

	rotnum++;
	numenemies++;
	enent=entities+numenemies;
	enent->enx=XCONV(at);
	enent->eny=YCONV(at);
	enent->ensprite=newsprite();
	enent->enat=levelbuff+at;
	enent->enflags=HIDDENTYPE | HOLDING | TRAPPED;
	enent->enwhat=3;
	enent->enacount=0;
	enent->entimer=0;
	enent->enat=enent->enloc1=at+levelbuff;
	movings[at]|=enent->enmask=0x80>>numenemies;
}


void newdig(uchar *at)
{
int i,j;
struct dig *adig;
int x,y;

	i=at-levelbuff;

	digsound=makesound(1);

	for(j=0;j<hiddennum;j++)
	{
		if(i==hiddens[j])
		{
			hiddennum--;
			hiddens[j]=hiddens[hiddennum];
			if(!hiddennum || (randcount&1))
			{
				hiddennum=0;
				createhidden(i);
			}
		}
	}
	x=XCONV(i);
	y=YCONV(i);
	adig=diggings;
	for(i=0;i<MAXDIG;i++)
		if(adig->digtimer==0) break;
		else adig++;
	if(i==MAXDIG) return;
	adig->digx=x;
	adig->digy=y;
	adig->digat=at;
	adig->digtimer=-1;
	adig->digsprite=0;
}

int onenemy(struct entity *pl, int what)
{
struct entity *en;
int i;
int px,py;
int dx,dy;

	en=entities+1;
	px=pl->enx;
	py=pl->eny;

	i=numenemies;
	while(i--)
	{
		dx=en->enx-px;
		dy=en->eny-py;
		if(dx<0) dx=-dx;
		if(dy<=28 && dy>=16 && dx<18)
		{
			if(!centery[pl->eny] && (what==3 || what==4))
			{
				pl->entimer=0;
				pl->enflags|=HELDSTABLE;
				if (en->enflags&FALLING) pl->enflags|=UNSTABLE;
				return 1;
			}
			if(dy<=24) {
				if (en->enflags&FALLING) pl->enflags|=UNSTABLE;
				return 1;
			}
		}
		en++;
	}
	i=movings[28+COMPXY(px,py)-levelbuff]&0x7f;
	if(i)
	{
		en=entities+1;
		while(!(i&en->enmask)) en++;
		if(py+24==en->eny) return 1;
	}

	return 0;	
}

void fix(uchar *where)
{
uchar v;
uchar ch;
	ch=*where;
	if(ch==BORDER) return;
	addmodify(where-levelbuff,12*randoms[where-levelbuff]);
	v=smap[*(where-28)];
	v|=smap[*(where-29)]<<1;
	v|=smap[*(where-1)]<<2;
	if(v) addmodifyq(where-levelbuff,SHADOWFIGS+v);
	if(*where!=EMPTY) addmodifyq(where-levelbuff,
		ch!=HAT ? ch+12*randoms[where-levelbuff] : HATFIGS);
}

void doplayer(struct entity *pl, int what)
{
uchar *at;
uchar ch;
uchar *oldat;
uchar stillheld=0;
int cy;
int flags;

	if(pl->enflags & FALLING)
		pl->enflags |= OLDFALLING;
	else
		pl->enflags &= 0xffff-OLDFALLING;
	if(recording && mpoint<mend)
	{
		if(*mpoint)
		{
			if(what==*(mpoint+1) && *mpoint!=255)
				++*mpoint;
			else
			{
				mpoint+=2;
				*mpoint=1;
				mpoint[1]=what;
			}
		} else
		{
			*mpoint=1;
			mpoint[1]=what;
		}
	}
	oldat=COMPXY(pl->enx,pl->eny);
	cy=centery[pl->eny];
	if(pl->enflags & DIGGING)	/* suspend player while digging */
	{
		pl->enx+=centerx[pl->enx];
		pl->eny+=cy;
		pl->entimer++;
		if(pl->entimer==DIGTIME)
		{
			pl->enflags&=0xffff-DIGGING;
		} else
		{
			what=0;
			goto dpdone;
		}
	}

	if((pl->enflags & HELDSTABLE) && pl->entimer++ <HELDLIMIT && what==pl->endir) stillheld++;
	pl->enflags &= 0xffff-UNSTABLE;	/* player is stable unless we find him
					 * standing on enemy */
	if(!ISSTABLE(*COMPXY(pl->enx,pl->eny+TY/2)) &&
		*COMPXY(pl->enx,pl->eny)!=3 &&
		!(*oldat==RAIL && cy==0 && what!=2) &&
		!(pl->enflags&DIGGING) && !onenemy(pl,what) && !stillheld)
	{
		what=0;
		pl->enflags|=FALLING;
		pl->enwhat=2;
	} else pl->enflags&=0xffff-FALLING;
	switch(what)
	{
	case 0:
		if(pl->enflags & FALLING)
		{
			pl->eny+=2;
			pl->enx+=centerx[pl->enx];
		}
		break;
	case 1:
		if(!numgold && pl->eny==YOFF && *COMPXY(pl->enx,pl->eny)==3)
		{
			wonflag++;
			return;
		}
		if(ISENTERABLE(*COMPXY(pl->enx,pl->eny-TY/2-2)) &&
			(*COMPXY(pl->enx,pl->eny)==3 || *COMPXY(pl->enx,pl->eny+TY/2-2)==3) )
		{
			pl->eny-=2;
			pl->enx+=centerx[pl->enx];
		} else what=0;
		break;
	case 2:
		ch=*COMPXY(pl->enx,pl->eny+TY/2);
		if(ISENTERABLE(ch) || ch==5)
		{
			pl->eny+=2;
			pl->enx+=centerx[pl->enx];
		} else what=0;
		break;
	case 3:
		if(pl->enflags & HELDSTABLE && pl->entimer==0)
			pl->endir=3;
		if(ISENTERABLE(*COMPXY(pl->enx-TX/2-2,pl->eny)))
		{
			pl->enx-=2;
			pl->eny+=cy;
		} else what=0;
		break;
	case 4:
		if(pl->enflags & HELDSTABLE && pl->entimer==0)
			pl->endir=4;
		if(ISENTERABLE(*COMPXY(pl->enx+TX/2,pl->eny)))
		{
			pl->enx+=2;
			pl->eny+=cy;
		} else what=0;
		break;
	case 5:
	case 6:
		at=COMPXY(pl->enx,pl->eny);
		at+= what==5 ? -1 : 1;
		if(*(at+28)==1 && (*at==0 || *at==DUGBRICK) && !(pl->enflags & FALLING) && !(at[(movings-levelbuff)]&0x7f))
		{
			newdig(at+28);
			pl->enflags|=DIGGING;
			pl->entimer=0;
			pl->enwhat=what;	/* start digging animation */
			pl->enacount=0;
			break;
		} else what=0;
		what=0;
		break;
	}
dpdone:
	at=COMPXY(pl->enx,pl->eny);
	if(*at==BRICK) lostflag++;
	if((*at==GOLD || *at==HAT) && centerx[pl->enx]==0 && centery[pl->eny]==0)
	{
		makesound(2);
		if(*at==GOLD)
		{
			*at=EMPTY;
			if(flashat!=at)
				score+=250;
			else
				score+=8000;
			mtflag++;
			fix(at);
			decgold();
			if(flashat)
			{
				flashat=0;
				SPRITEOFF(flashsprite);
			}
		} else
		{
			*at=EMPTY;
			score+=10000;
			mtflag++;
			fix(at);
		}
	}
	if(at!=oldat)
	{
		calced=0;
		movings[oldat-levelbuff]&=0xff-pl->enmask;
		movings[at-levelbuff]|=pl->enmask;
	}
	if(movings[at-levelbuff]&0x7f) lostflag++;
	if((pl->enflags & DIGGING) && pl->enwhat>=5) {
		if (++pl->enacount >= 12) {	/* digging animation */
			pl->enwhat-=2;		/* digging animation done */
			pl->enacount=0;
			goto dpdone2;
		}
	} else if (what || (pl->enflags&FALLING) ||
			(pl->enflags&UNSTABLE && !(pl->enflags&DIGGING)))
		pl->enacount++;
	else pl->enacount=0;
	if(pl->enacount>=24) pl->enacount=0;
	if(what) pl->enwhat=what;
dpdone2:
	flags=pl->enflags;
	if((flags&FALLING) && !(flags&OLDFALLING)) fallsound=makesound(0);
	if(!(flags&FALLING) && (flags&OLDFALLING)) stopsound(fallsound);
}

void addmodify (int where, int what)
{
int x,y;
	x=XCONV(where);
	y=YCONV(where);
	mark(x,y);
	puttile(x,y,what);
}

void addmodifyq (int where, int what)
{
	puttile(XCONV(where),YCONV(where),what);
}

void decgold()
{
int i;
	numgold--;
	if(numgold) return;
	for(i=0;i<LEVELSIZE;i++)
	{
		if(levelback[i]==ESCAPE)
		{
			levelbuff[i]=LADDER;
			addmodify(i,LADDER + 12*randoms[i]);
		}
	}
	rethink();
}

void dodiggings()
{
int i;
struct dig *adig;
int time;
struct entity *en;
uchar ch;

	if(!playing) return;
	adig=diggings;
	for(i=0;i<MAXDIG;i++,adig++)
	{
		if(adig->digtimer)
		{
			time= ++adig->digtimer;
			if(!time) time=++adig->digtimer;
			if(time<DIGTIME/2)
			{
				if(*(adig->digat-28)==BRICK || movings[adig->digat-levelbuff-28])
				{
					stopsound(digsound);

					*adig->digat=BRICK;
					if(adig->digsprite)
						SPRITEOFF(adig->digsprite);
					adig->digsprite=0;
					entities[0].enflags &=0xffff-DIGGING;
					adig->digtimer=0;
					continue;
				}
			}
			if(time==1 || time==DIGLIFE-DIGTIME)
			{
				if(time==1) *adig->digat=CEMENT;
				adig->digsprite=newsprite();
				if(time!=1) addmodify(adig->digat-levelbuff,BRICK + 12*randoms[adig->digat-levelbuff]);
			} else if(time==DIGTIME+1 || time==DIGLIFE)
			{
				SPRITEOFF(adig->digsprite);
				adig->digsprite=0;
				if(time==DIGLIFE)
				{
					adig->digtimer=0;
					*adig->digat=BRICK;
					closebrick(adig->digat);
					if((ch=(movings[adig->digat-levelbuff] & 0x7f)))
					{
						en=entities+1;
						while(!(ch & en->enmask)) en++;
						killenemy(en);
					}
				} else
				{
					if(movings[adig->digat-levelbuff] & 0x7f)
					{
						*adig->digat=CEMENT;
					} else
					{
						*adig->digat=DUGBRICK;
						openbrick(adig->digat);
					}
					addmodify(adig->digat-levelbuff,DIGFIGS+6);
				}
			}
		}
	}
}

void rethink()
{
int i,j,b1,b2,sb;
uchar ch1,ch2;
uchar *p,*p2;

	p=levelbuff+30;
	for(i=0;i<LX;i++)
	{
		cantascend[i]=1;
		cantdescend[i]=enterable[i]=stable[i]=0;
		p2=p;
		for(j=1;j<=LY;j++)
		{
			sb=bits[j];
			ch1=*p2;
			b1=bits[ch1];
			ch2=*(p2+28);
			b2=bits[ch2];
			if(b1 & (FEMPTY | FLADDER | FRAIL | FESCAPE | FGOLD | FFLASHING | FHAT | FDUGBRICK)) enterable[i]|=sb;
			if(ch1!=3) cantascend[i]|=sb;
			if(b2 & (FBRICK | FCEMENT | FFAKE | FDUGBRICK | FBORDER)) cantdescend[i]|=sb;
			if((b1 & (FRAIL | FLADDER)) || (b2 & (FBRICK | FCEMENT | FLADDER | FFAKE | FHIDDEN | FDUGBRICK | FBORDER)))
					stable[i]|=sb;
			p2+=28;
		}
		p++;
	}
	enterable[-1]=0;
	enterable[LX]=0;
	calced=0;
}

void openbrick(uchar *where)
{
int i;
	i=where-levelbuff-2;
	enterable[i%28]|=bits[i/28];
	calced=0;
}

void closebrick(uchar *where)
{
int i;
	i=where-levelbuff-2;
	enterable[i%28]&=0xffff-bits[i/28];
	calced=0;
}

void recalc()
{
int px,py;
int i,j,t;

	calced=1;
	px=xcomp[entities[0].enx]-2;
	py=ycomp[entities[0].eny];
	belowplayer=-bits[py];
	aboveplayer=(bits[py]<<1)-1;
	for(i=0;i<LX;i++)
	{
		j=i;
		if(i<px) j++;
		if(i>px) j--;
		t=stable[i] & stable[j] & enterable[j];
		upgoal[i]=(t & aboveplayer) | cantascend[i];
		downgoal[i]=(t & belowplayer) | cantdescend[i];
	}
}

int findbest(int expos, int eypos)
{
int px,py;
int ex,ey;
int i;
int b1;

	px=xcomp[entities[0].enx]-2;
	py=ycomp[entities[0].eny];
	ex=xcomp[expos]-2;
	ey=ycomp[eypos];
	b1=bits[ey];
	if(py==ey)
	{
		if(px==ex) return 0;
		i=ex;
		if(i<px)
		{
			i++;
			while(enterable[i] & stable[i] & b1)
			{
				if(i==px) return 4;
				i++;
			}
		} else
		{
			i--;
			while(enterable[i] & stable[i] & b1)
			{
				if(i==px) return 3;
				i--;
			}
		}
	}
	belowenemy=(-b1)<<1;
	aboveenemy=b1-1;
	bestdist=100;
	mask=0xffff;
	bestdir=0;
	if(!(cantascend[ex]&b1))
	{
		thisdir=1;
		if(!uprate(upgoal[ex] & aboveenemy,0)) goto foundbest;
	}
	if(!(cantdescend[ex]&b1))
	{
		thisdir=2;
		if(!downrate(downgoal[ex] & belowenemy & mask,0)) goto foundbest;
	}
	thisdir=3;
	i=ex;
	while(enterable[--i]&b1)
	{
		if(!(cantdescend[i]&b1))
			if(!downrate(downgoal[i] & belowenemy,ex-i)) break;
		if(!(stable[i]&b1)) break;
		if(!(cantascend[i]&b1))
			if(!uprate(upgoal[i] & aboveenemy,ex-i)) break;
	}
	thisdir=4;
	i=ex;
	while(enterable[++i]&b1)
	{
		if(!(cantdescend[i]&b1))
			if(!downrate(downgoal[i] & belowenemy,i-ex)) break;
		if(!(stable[i]&b1)) break;
		if(!(cantascend[i]&b1))
			if(!uprate(upgoal[i] & aboveenemy,i-ex)) break;
	}
foundbest:
	return bestdir;
}

int downrate(unsigned int d0, unsigned int delta)
{
unsigned int ax;
	if(!d0) return 1;
	ax=((d0-1) ^ d0) & d0;

	ax&=mask;
	if(!ax) return 1;
	if(ax & aboveplayer)
	{
		if((mask=-(ax+ax) & aboveplayer))
			return bestdir=thisdir;
		mask=(aboveplayer>>1)+1;
		if(delta<bestdist)
		{
			bestdist=delta;
			bestdir=thisdir;
		}
		return 0;
	} else
	{
		mask=ax-1;
		return bestdir=thisdir;
	}
}

int uprate(unsigned int d0, unsigned int delta)
{
unsigned int ax;
	if(!d0) return 1;
	ax=0x8000;
	while(!(ax & d0)) ax>>=1;

	ax&=mask;
	if(!ax) return 1;
	if(ax & aboveplayer)
	{
		if((mask=-(ax+ax) & aboveplayer))
			return bestdir=thisdir;
		mask=(aboveplayer>>1)+1;
		if(delta<bestdist)
		{
			bestdist=delta;
			bestdir=thisdir;
		}
		return 0;
	} else
	{
		mask=ax-1;
		return bestdir=thisdir;
	}
}

void doenemies()
{
struct entity *en;
int what,want;
int i,j,k;
uchar rotbyte;
uchar *oldat,*at;
uchar ch,ch2;
uchar moved;

	if(!calced) recalc();
	if(!numenemies) return;
	rotbyte=rotbytes[rotnum];
	rotnum-=8;
	if(rotnum<0) rotnum+=24;
	en=entities+1;
	for(i=0;i<numenemies;(rotbyte>>=1),(en++),i++)
	{
#define TRAPTIME 130
#define LIFTTIME1 12
#define LIFTTIME2 7
		if(rotbyte&1)
		{
			moved=0;
			what=0;
			oldat=COMPXY(en->enx,en->eny);
			want=findbest(en->enx,en->eny);
			en->enflags &=0xffff-FALLING;
			if(en->enflags & TRAPPED)
			{
				k=en->enflags;
				if(((k&HIDDENTYPE) && (k&HOLDING)) || !(k&HIDDENTYPE))
					en->entimer++;
				j=en->entimer;
				if(j<TRAPTIME-LIFTTIME1-LIFTTIME2) goto testdead;
				if(j==TRAPTIME)
				{
					en->enflags &= 0xffff-TRAPPED;
					if(*en->enloc1==CEMENT)
					{
						*en->enloc1=DUGBRICK;
						openbrick(en->enloc1);
					}
				}
				else
					if(j<TRAPTIME-LIFTTIME2)
					{
						j=(en->enloc1-levelbuff)-28;
						if(levelbuff[j]==1 || (movings[j]&(0x7f-en->enmask)) ||
								(!findbest(XCONV(j),YCONV(j)) && !(movings[j]&0x80)))
						{
							en->entimer--;
							what=0;
						} else what=1;
						goto doit;
					} else
					{
						what=want;
						goto doit;
					}
			}
			if(en->enflags & DEAD)
			{
				what=0;

				if(!en->entimer)
				{
					if(en->enflags & HIDDENTYPE)
					{
						rotnum--;
						numenemies--;
						movings[oldat-levelbuff]&=0xffff-en->enmask;
						if(*(oldat-28)==HAT)
						{
							*(oldat-28)=EMPTY;
							fix(oldat-28);
						}
						SPRITEOFF(en->ensprite);
						continue;
					}
					if(en->enflags & HOLDING)
					{
						en->enflags &=0xffff-HOLDING;
						decgold();
					}
					k=randcount;
					at=levelbuff+30+k;
					for(;;)
					{
						if(!*at) break;
						k++;at++;
						if(k==LX) {k=0;at-=LX;}
					}
					en->enx=XCONV(at-levelbuff);
					en->eny=YCONV(at-levelbuff);
				}
				j=en->entimer++;
				if(j==DEADTIME) en->enflags &=0xffff-DEAD;
				else goto doit;
			}
			if(!ISSTABLE(*COMPXY(en->enx,en->eny+TY/2)) && *oldat!=3 &&
						!(*COMPXY(en->enx,en->eny+TY/2-1)==4 && centery[en->eny]==0))
			{
				what=0;
				en->enflags |=FALLING;
				en->enwhat=2;	/* switch to falling anim */
			}
			else
				what=want;

doit:
			switch(what)
			{
			case 1:
				if(ISENTERABLE(*COMPXY(en->enx,en->eny-TY/2-2)))
				{
					en->enx+=centerx[en->enx];
					en->eny-=2;
					moved++;
				} else what=0;
				break;
			case 0:
				if(!(en->enflags & FALLING))
					break;
			case 2:
				en->enx+=centerx[en->enx];
				en->eny+=2;
				moved++;
				if(*oldat==DUGBRICK && centerx[en->enx]==0 && centery[en->eny]==0)
				{
					en->enflags|=TRAPPED;
					if(!(en->enflags & HIDDENTYPE))
						playflags|=HASTRAPPED;
					en->entimer=0;
					*oldat=CEMENT;
					en->enloc1=oldat;
					closebrick(oldat);
					if(en->enflags & HOLDING)
					{
						en->enflags &=0xffff-HOLDING;
						if(*(oldat-28)==EMPTY)
						{
							*(oldat-28)=(en->enflags & HIDDENTYPE) ? HAT : GOLD;
							fix(oldat-28);
						} else decgold();
					}
				}
				break;
			case 3:
				ch2=movings[oldat-levelbuff];
				ch=(ch2&(en->enmask-1)) | (movings[COMPXY(en->enx-TX/2,en->eny)-levelbuff] & (0xff-ch2));
				if(!(ch & (0x7f-en->enmask)))
				{
					en->enx-=2;
					moved++;
					if(!(en->enflags & TRAPPED))
						en->eny+=centery[en->eny];
				} else what=0;
				break;
			case 4:
				ch2=movings[oldat-levelbuff];
				ch=(ch2&(en->enmask-1)) | (movings[COMPXY(en->enx+TX/2,en->eny)-levelbuff] & (0xff-ch2));
				if(!(ch & (0x7f-en->enmask)))
				{
					en->enx+=2;
					moved++;
					if(!(en->enflags & TRAPPED))
						en->eny+=centery[en->eny];
				} else what=0;
				break;
			}
testdead:
			if(what) en->enwhat=what;
			at=COMPXY(en->enx,en->eny);
/*
			if(*at==1)
			{
				killenemy(en);
			}
*/
			if(oldat!=at)
			{
				movings[oldat-levelbuff]&=0xff-en->enmask;
				movings[at-levelbuff]|=en->enmask;
				if(*oldat==DUGBRICK && at==oldat+28 && (en->enflags & HOLDING))
				{
					en->enflags &=0xffff-HOLDING;
					if(!(en->enflags&HIDDENTYPE)) decgold();
				}
			}
			if(moved && !centerx[en->enx] && !centery[en->eny])
			{
				if((en->enflags & (HOLDING|HIDDENTYPE))==HOLDING)
				{
					if(en->enholdcount) en->enholdcount--;
					if(*at==EMPTY && !en->enholdcount)
					{
						ch=*(at+28);
						if(ch==BRICK || ch==DUGBRICK || ch==LADDER || ch==CEMENT || ch==BORDER)
						{
							en->enflags&=0xffff-HOLDING;
							*at=GOLD;
							fix(at);
						}
					}
				} else
				{
					if(*at==GOLD && at!=flashat && !(en->enflags&HIDDENTYPE))
					{
						*at=EMPTY;
						en->enflags|=HOLDING;
						fix(at);
						en->enholdcount=randcount;
					}
				}
			}
		if(what || (en->enflags&FALLING)) en->enacount++;
		else en->enacount=0;
		if(en->enacount>=24) en->enacount=0;
		}
	}
}

void killenemy(struct entity *en)
{
	if(en->enflags & DEAD) return;
	en->enflags &=0xffff-TRAPPED;
	en->enflags |=DEAD;
	if(!(en->enflags&HIDDENTYPE))
		playflags|=HASKILLED;
	en->entimer=0;
}

void movement()
{
int what;

	if(!playing) return;

	if(!recording)
	{
		what=*(mpoint+1);
		--*mpoint;
		if(!*mpoint) mpoint+=2;
	} else
	{
		what=0;
		if(checkpressed(digleftkey)) what=5;
		else if(checkpressed(digrightkey)) what=6;
		else if(checkpressed(upkey)) what=1;
		else if(checkpressed(downkey)) what=2;
		else if(checkpressed(leftkey)) what=3;
		else if(checkpressed(rightkey)) what=4;
	}
	if(freezing & !what) return;
	freezing=0;
	randcount++;
	if(randcount>=24) randcount=0;
	doplayer(entities,what);
	doenemies();

}

void mark(int x,int y)
{
	if(markpoint==marks+MARKMAX)
		needwhole=1;
	else
	{
		*markpoint++=x;
		*markpoint++=y;
	}
}

void domarks()
{
int *ip,x,y;
	ip=marks;
	while(ip<markpoint)
	{
		x=*ip++;
		y=*ip++;
		copyupxy(x,y);
	}
	markpoint=marks;
}

void paintmode3()
{
uchar ttt[64];

	restoresprites();
	clear();
	pty=150;
	sprintf(ttt,"PLAYER:%s",playername);
	lefttext(ttt);
	lefttext("");
	lefttext("F1,SPC    START GAME AT FIRST UNSOLVED LEVEL");
	lefttext("F2        GRAPHICS EDITOR");
	lefttext("F3        LEVEL EDITOR");
	lefttext("F4        START LEVEL 1");
	lefttext("F5        CHANGE PLAYER NAME");
	lefttext("F7        DEMO PREVIOUS LEVEL");
	lefttext("F8        DEMO NEXT LEVEL");
	lefttext("F10       REMAP MOVEMENT KEYS");
	lefttext("ESC       RETURN TO DEMO");
	lefttext("ALT-X     EXIT GAME");
}

void tomode3()
{
	fadeout();
	paintmode3();
	copyup();
	fadein();
}

void query(uchar *txt, int maxlen, uchar *str)
{
uchar ttt[80];
uchar ttt2[80];
int j,k;
int *p,ch,ch2;
char changed;

	strcpy(ttt,str);
	k=strlen(ttt);
	strcat(ttt,txt);
	j=strlen(txt);
	changed=1;
	for(;;)
	{
		if(changed)
		{
			sprintf(ttt2,"%s# ",ttt);
			writestring(ttt2,0,468,TCOLOR);
			copyup();
			changed=0;
		}
		scaninput();
		pause();
		p=keylist;
		for(;;)
		{
			ch=0;
			ch2=*p++;
			if(ch2==255) break;
			ch=*p++;
			if(checkdown(ch2)) break;
		}
		if(!ch) continue;
		if(ch==0x1b || ch==10) break;
		if(ch==8)
		{
			if(j)
			{
				j--;
				ttt[k+j]=0;
				changed=1;
			}
		} else if(j<maxlen)
		{
			ttt[k+j]=ch;
			j++;
			ttt[k+j]=0;
			changed=1;
		}
	}
	if(ch==10) strcpy(txt,ttt+k);
	sprintf(ttt2,"%-72s","");
	writestring(ttt2,0,468,TCOLOR);
}

void remapkey(char *str,int *key,int line)
{
char ttt[128];
int code;
	sprintf(ttt,"Press %s:",str);
	line*=12;
	line+=64;
	writestring(ttt,0,line,TCOLOR);
	copyup();
	for(;;)
	{
		pause();
		scaninput();
		if(!anydown()) continue;
		code=firstdown();
		break;
	}
	if(code==XK_Escape) code=*key;
	*key=code;
	sprintf(ttt+strlen(ttt),"%x",code);
	writestring(ttt,0,line,TCOLOR);
	copyup();
}

void remapkeys()
{
	clear();
	remapkey("key for moving up",&upkey,0);
	remapkey("key for moving down",&downkey,1);
	remapkey("key for moving left",&leftkey,2);
	remapkey("key for moving right",&rightkey,3);
	remapkey("key for digging left",&digleftkey,4);
	remapkey("key for digging right",&digrightkey,5);
	makercname(temp);
	makercfile(temp);
}

void getplayername()
{
	query(playername,16,"Enter name:");
}

void findlevel()
{
int err;

	clevel=1;
	for(;;)
	{
		err=getresource(1+((clevel-1)<<1),movie,20);
		if(err<=0) break;
		clevel++;
	}
	if(!loadlevel(clevel))
	{
		clevel=1;
		loadlevel(clevel);
	}
}

void mode3() /* menu */
{

	if(checkdown(XK_F1) || checkdown(XK_space)) /* F1 or space*/
	{
		fadeout();
		findlevel();
		mode=mode1;
		startgame();
	} else
	if(checkdown(XK_F4)) /* F4 */
	{
		mode=mode1;
		clevel=ilevel;
		loadlevel(clevel);
		startgame();
	} else
	if(checkdown(XK_Escape)) /* ESC */
	{
		initdemo();
	} else
	if(checkdown(XK_F2)) /* F2 */
	{
		endplay();
		mode=mode2;
		toanim();
	} else
	if(checkdown(XK_F5)) /* F5 */
	{
		getplayername();
		sprintf(temp,"%s/%s/%s",localname,localdirname,PLAYERNAME);
		file=creat(temp,00600);
		if(file!=-1)
		{
			write(file,playername,16);
			close(file);
		}
		tomode3();
	} else
	if(checkdown(XK_F10)) /* F10 */
	{
		remapkeys();
		tomode3();
	} else
	if(checkdown(XK_F3)) /* f3 */
	{
		endplay();
		toedit();
	}
}

void mode0() /* demo mode */
{
	if(checkdown(XK_space)) /* spacebar */
	{
		fadeout();
		endplay();
		tomode3();
		mode=mode3;
	} else
	if(checkdown(XK_F1)) /* F1 */
	{
		fadeout();
		endplay();
		mode=mode1;
		findlevel();
		startgame();
	} else
	if(checkdown(XK_F2)) /* f2 */
	{
		fadeout();
		endplay();
		mode=mode2;
		toanim();
	} else
	if(checkdown(XK_F3)) /* f3 */
	{
		fadeout();
		endplay();
		toedit();
	} else
	if(checkdown(XK_F4)) /* F4 */
	{
		fadeout();
		endplay();
		mode=mode1;
		clevel=ilevel;
		loadlevel(clevel);
		startgame();
	} else
	if(clevel > 0 && checkdown(XK_F7)) /* f7 */
	{
		quiet();
		makesound(3);
		waitsound();
		fadeout();
		endplay();
		clevel--;
		initdemo();
	} else
	if(wonflag || checkdown(XK_F8)) /* f8 */
	{
		quiet();
		makesound(3);
		waitsound();
		fadeout();
		endplay();
		clevel++;
		initdemo();
	}
}

void mode1() /* playing game */
{
	if(checkdown(XK_q) || checkdown(XK_Escape)) /* q or escape */
	{
		fadeout();
		endplay();
		initdemo();
	}
	if (clevel > 0 && checkdown(XK_F7)) /* f7 */
	{
		quiet();
		makesound(3);
		waitsound();
		clevel -= 2;
		nextlevel(wonflag);
	}
	else if(wonflag || checkdown(XK_F8)) /* f8 */
	{
		quiet();
		makesound(3);
		waitsound();
		nextlevel(wonflag);
	}
	else if(lostflag || checkdown(XK_k)) /* k */
	{
		quiet();
		makesound(4);
		waitsound();
		samelevel();
	}
}

void csline(uchar *put, int line)
{
}

void capturescreen()
{
uchar name[16];
int i;

	i=0;
	for(;;)
	{
		sprintf(name,"CAP%03d.lbm",i++);
		file=open(name,O_RDONLY);
		if(file!=-1) close(file);
		else break;
	}

	writeiff(name,640,480,csline,colormap);
}

int makesound(int num)
{
	playsound(num);
	return num;
}

void stopsound(int num)
{
	playsound(num+64);
}

void quiet()
{
	/*play_sound(-1);  needs waitsound() */
}

void waitsound() {}
