#include <libraries/dosextens.h>
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <graphics/gfxmacros.h>

#define BITMAP

#include "maze.h"         /* This is our maze generator */

#define MENUITEMS 6

struct IntuiText item[MENUITEMS*2]=
{ 6,0,JAM2,0,0,NULL,NULL,NULL };

struct IntuiText abouttxt5=   /* Yes, this game needs lots of credits */
{ 7,0,JAM2,32,40,NULL,"keys to move",NULL};
struct IntuiText abouttxt4=
{ 7,0,JAM2,12,30,NULL,"Use keypad cursor",&abouttxt5};
struct IntuiText abouttxt3=
{ 4,0,JAM2,0,20,NULL,"Freely distributable",&abouttxt4};
struct IntuiText abouttxt2=
{ 6,0,JAM2,24,10,NULL,"Werner Gunther",&abouttxt3};
struct IntuiText abouttxt1=
{ 6,0,JAM2,40,0,NULL,"Written by",&abouttxt2};

struct MenuItem mi[MENUITEMS] =          /* main menu structures */
{
 NULL,0,0,70,11,
 ITEMTEXT|ITEMENABLED|HIGHCOMP,
 NULL,NULL,NULL,NULL,NULL,NULL
};
struct MenuItem sizemenu[MENUITEMS] =    /* sub menu for sizes */
{
 NULL,60,0,90,11,
 ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT,
 NULL,NULL,NULL,NULL,NULL,NULL
};
struct MenuItem about =                  /* about, credits & help */
{
 NULL,60,0,160,50,
 ITEMTEXT|ITEMENABLED,
 NULL,(APTR)&abouttxt1,NULL,NULL,NULL,NULL
};

struct Menu menu=
{
 NULL,0,0,70,11,
 MENUENABLED,
 "Options",
 mi
};

struct NewScreen ns=                     /* screen low-rez, 8 colors */
{
 0,0,320,200,3,
 1,0,
 NULL,
 CUSTOMSCREEN,
 NULL,NULL,NULL,NULL
};

struct NewWindow nw=                     /* window for menus & gadgets */
{
 0,1,286,199,
 1,0,
 CLOSEWINDOW|GADGETUP|MENUPICK|MENUVERIFY|VANILLAKEY,
 BORDERLESS|NOCAREREFRESH|WINDOWCLOSE|ACTIVATE,
 NULL, NULL,
 "Maze 3D Demo",
 NULL, NULL, NULL,NULL,NULL,NULL,
 CUSTOMSCREEN
};

struct Image mazeimage={0,0,0,0,1,0,4,4,0}; /* Image to display the maze */

char mtxt[MENUITEMS][9]=                    /* menu text */
{
 "Map","New Maze","Size","Demo","Quit","About"
};
char sizetxt[MENUITEMS][11]=                /* text for size menu */
{
 "Trivial","Simple","Normal","Advanced","Difficult","Impossible"
};

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Screen *screen = NULL;
struct Window *window = NULL;
struct BitMap *bm;
struct IntuiMessage *msg;
struct AreaInfo  areainfo;
struct TmpRas tmpras;
struct RastPort *rp;

ULONG  class,code;
BOOL   WorkBench=FALSE,demomode=FALSE,forward();
void   right(),left(),demo(),newmaze(),leave(),main(),show3d(),showmaze();
void   statusline();
short  i,j,xsize,ysize,mazesize=2,x,y,wayout,moves;
char   *mymaze=NULL,areapts[7*5],*raster=NULL,buffer[20];
short  dirincs[4][2]={{0,-1},{1,0},{0,1},{-1,0}};
char   heading[][7]={"North"," East ","South"," West "};

void main(argc,argv)
short argc;
char argv[];
{
 if (!argc) WorkBench=TRUE;      /* open stuff */
 if (!(IntuitionBase = (struct IntuitonBase*)
  OpenLibrary("intuition.library",0L)))
    leave("No Intuition Library");
 if (!(GfxBase = (struct GfxBase*) OpenLibrary("graphics.library",0L)))
    leave("No Graphics Library");

 for(i=0;i<MENUITEMS;i++)        /* set menu-strip */
 {                               /* could have be done on declaration */
  sizemenu[i]=sizemenu[0];
  sizemenu[i].NextItem=&sizemenu[i+1];
  sizemenu[i].ItemFill=(APTR)&item[i+MENUITEMS];
  sizemenu[i].TopEdge=i*11;
  item[i]=item[0];
  item[i+MENUITEMS]=item[0];
  item[i+MENUITEMS].IText=sizetxt[i];
  item[i+MENUITEMS].LeftEdge=11;
  item[i].IText=mtxt[i];
  mi[i]=mi[0];
  mi[i].NextItem=&mi[i+1];
  mi[i].ItemFill=(APTR)&item[i];
  mi[i].TopEdge=i*11;
 }
 mi[MENUITEMS-1].NextItem=NULL;
 sizemenu[MENUITEMS-1].NextItem=NULL;
 mi[2].SubItem=&sizemenu[0];
 mi[5].SubItem=&about;
 sizemenu[mazesize].Flags|=CHECKED;

 if (!(screen = (struct Screen*) OpenScreen(&ns))) /* open screen & window */
    leave ("Can't open Screen");
 bm = screen->ViewPort.RasInfo->BitMap;
 rp = &screen->RastPort;

 nw.Screen=screen;
 if (!(window = (struct Window*) OpenWindow(&nw))) leave("Can't open Window");
 SetMenuStrip(window,&menu);

 SetRGB4(&screen->ViewPort,0,0,0,0); /* set colors, 1-7 is a grey scale */
 for(i=1;i<7;i++) SetRGB4(&screen->ViewPort,i,2+2*i,2+2*i,2+2*i);
 SetRGB4(&screen->ViewPort,7,8,8,15);

 /* stuff needed by FloodFill, AreaFill */
 if(!(raster=(char *)AllocRaster(192,188))) leave("Can't allocate TmpRaster");
 InitTmpRas(&tmpras,raster,4512);
 rp->TmpRas=&tmpras;
 InitArea(&areainfo,areapts,7);
 rp->AreaInfo=&areainfo;
 BNDRYOFF(rp);

 for(i=0;i<3;i++) /* Draw status-display box */
 {
  SetAPen(rp,i+3);
  AreaMove(rp,190+5*i,12+5*i);
  AreaDraw(rp,319-5*i,12+5*i);
  AreaDraw(rp,319-5*i,125-5*i);
  AreaDraw(rp,319-5*i-5,125-5*i-5);
  AreaDraw(rp,319-5*i-5,12+i*5+5);
  AreaDraw(rp,190+5*i+5,12+i*5+5);
  AreaEnd(rp);
  SetAPen(rp,i+2);
  AreaMove(rp,190+5*i,12+5*i);
  AreaDraw(rp,190+5*i,125-5*i);
  AreaDraw(rp,319-5*i,125-5*i);
  AreaDraw(rp,319-5*i-5,125-5*i-5);
  AreaDraw(rp,319-5*i-5,12+i*5+5);
  AreaDraw(rp,190+5*i+5,12+i*5+5);
  AreaEnd(rp);
 }

 newmaze();

 for(;;)           /* main loop, wait for a message if not in demo-mode */
 {
  if (!demomode) WaitPort(window->UserPort);
  else demo();
  if(msg=(struct IntuiMessage*)GetMsg(window->UserPort))
  {
   demomode=FALSE;
   class=msg->Class;
   code=msg->Code;
   ReplyMsg(msg);
   switch(class)
   {
    case CLOSEWINDOW:
     leave("");
    case VANILLAKEY:
     if(y!=wayout || x!=xsize-3)
      switch(code) /* we check numbers 6/4/8, these are arrows on the */
      {            /* num pad keys, so we don't need RawKeyConvert */
       case '8':
        forward();
        break;
       case '4':
        left();
        break;
       case '6':
        right();
        break;
      }
     break;
    case MENUPICK:
      switch(ITEMNUM(code))
      {
       case 0:         /* map */
        showmaze();
        moves+=30;
        break;         /* New Maze */
       case 1:
        newmaze();
        break;
       case 2:
        if(SUBNUM(code)==mazesize) break;
        sizemenu[mazesize].Flags&=~CHECKED;
        mazesize=SUBNUM(code);
        sizemenu[mazesize].Flags|=CHECKED;
        newmaze();
        break;
       case 3:         /* Demo  */
        demomode=TRUE;
        break;
       case 4:         /* Quit  */
        leave("");
       case 5:         /* About */
	   	; /* Inserted ; to make Aztec happy -- PDS(1) -- 31-jul-88 */
      }
   }
  }
 }
}

void leave(error)
char *error;
{
 BPTR file;
                   /* Free Resouces & Exit */
 if(window)        CloseWindow(window);
 if(screen)        CloseScreen(screen);
 if(IntuitionBase) CloseLibrary(IntuitionBase);
 if(GfxBase)       CloseLibrary(GfxBase);
 if (mymaze)       FreeMem(mymaze,(xsize>>3)*ysize);
 if (raster)       FreeRaster(raster,192,188);

 if (*error && WorkBench &&
     (file=Open("con:20/70/400/60/Maze 3D Demo",MODE_OLDFILE)))
 {
  Write(file,error,strlen(error));
  Delay(200L);
  Close(file);
 }
 else if (*error) printf("%s\n",error);
 exit(0);
}

void newmaze()
{
 if (mymaze) FreeMem(mymaze,(xsize>>3)*ysize); /* Free old maze */
 xsize=32+mazesize*16;                         /* get new dimension */
 ysize=12+mazesize*12;
 if (!(mymaze=(char*) AllocMem((xsize>>3)*ysize,MEMF_CHIP|MEMF_CLEAR)))
     leave("Out of chip memory");              /* allocate new one */
 SetAPen(rp,6);                                /* clear status display */
 RectFill(rp,205,27,304,110);
 SetAPen(rp,1);
 statusline(10,"Moves:");
 statusline(30,"Looking");
 statusline(60,"Size:");
 sprintf(buffer,"%ld*%ld",xsize,ysize);
 statusline(70,buffer);

 maze(xsize,ysize,mymaze);                     /* genarate a new maze */
 seed = FastRand(seed);
 y= ((seed & 0x7ffe) % (ysize-4)+2) & 0x7ffe;  /* random start point */
 x= 2;
 seed = FastRand(seed);
 wayout = ((seed & 0x7ffe) % (ysize-4)+2) & 0x7ffe;  /* random exit */
 MAZESET(xsize-3,wayout,xsize,mymaze);               /* open exit */
 direction=1;                                    /* heading right (east) */
 statusline(40,heading[direction]);
 moves=0;
 show3d();
 showmaze();
}

void statusline(y,text)
short y;
char *text;
{                                  /* Write text, centered, into */
 Move(rp,255-strlen(text)*4,27+y); /* status-display */
 SetAPen(rp,1);
 SetBPen(rp,6);
 Text(rp,text,strlen(text));
}

void showmaze()                   /* show the maze */
{
 mazeimage.Width=xsize;           /* we define a Image-structure on our */
 mazeimage.Height=ysize;          /* bitmapped maze */
 mazeimage.ImageData=(short*)mymaze;
 DrawImage(rp,&mazeimage,255-(xsize/2),127);
                                               /* wait for a message */
 while(! window->UserPort->mp_MsgList.lh_Head->ln_Succ)
 {
  SetAPen(rp,++i&7);                        /* exit and current position */
  WritePixel(rp,255-(xsize/2)+x,127+y);     /* are blinking */
  WritePixel(rp,253-(xsize/2)+xsize,127+wayout);
  Delay(6L);
 }
 SetAPen(rp,0);
 RectFill(rp,191,127,319,199);
}

void show3d()
{
 register short k,inner,outer,wall;

 WaitBOVP(rp);          /* doesn't have too much effect in this case */
 for(i=1;i<7;i++)       /* get the wall in front */
  if(!(MAZETEST(x+i*dirincs[direction][0],y+i*dirincs[direction][1],
              xsize,mymaze))) break;
 wall=6-i;

 for(k=-1;k < 2;k+=2)   /* draw a 'sky' and a 'floor' */
 {
  SetAPen(rp,(k+8) % 8);
  AreaMove(rp,1,106+k*94);
  AreaDraw(rp,187,106+k*94);
  AreaDraw(rp,94+(2<<wall),106+k*(2<<wall)+k);
  AreaDraw(rp,94-(2<<wall),106+k*(2<<wall)+k);
  AreaEnd(rp);
 }

 if(i!=7)
 {
  if(y==wayout && (x+i*dirincs[direction][0])==xsize-1)
  {                        /* are we looking at the exit ? */
   SetAPen(rp,7);
   RectFill(rp,94-(2<<(++wall)),106-(2<<wall),94+(2<<wall),106);
   SetAPen(rp,1);
   RectFill(rp,94-(2<<wall),107,94+(2<<wall),106+(2<<wall));
  }
  else
  {
   SetAPen(rp,5);   /* draw a light grey wall */
   RectFill(rp,94-(2<<wall),106-(2<<wall),94+(2<<wall),106+(2<<wall));
  }
 }
 else        /* the front wall is too far away */
 {
  SetAPen(rp,4);
  AreaMove(rp,92,104);
  AreaDraw(rp,92,108);
  AreaDraw(rp,96,104);
  AreaDraw(rp,96,108);
  AreaEnd(rp);
 }

 for(k=-1;k < 2;k+=2)      /* draw walls on the left and on the right */
  for(i=5-wall;i>-1;i--)   /* starting from the front wall ending at */
  {                        /* our current position */
   int xx, yy;		/* Patch to make Aztec happy -- PDS(1) -- 31-jul-88 */
   inner=(2<<(5-i));       /* inner edges of a wall */
   outer=(inner<<1);       /* outer edges */
   if(outer > 94) outer=94;  /* 'Clip' if too large */
                           /* check if a wall or a passage */
   xx=x+dirincs[(direction+k)&3][0]+i*dirincs[direction][0]; /* PDS(1) */
   yy=y+dirincs[(direction+k)&3][1]+i*dirincs[direction][1]; /* PDS(1) */
   if(!(MAZETEST(xx,yy, xsize,mymaze)))
   {                             /* it's a wall */
    SetAPen(rp,4);
    AreaMove(rp,94+k*inner,106-inner);
    AreaDraw(rp,94+k*inner,106+inner);
    AreaDraw(rp,94+k*outer,106+outer);
    AreaDraw(rp,94+k*outer,106-outer);
    AreaEnd(rp);
   }
   else                         /* it's a passage */
   {
    SetAPen(rp,7);              /* clear upper and lower corners */
    AreaMove(rp,94+k*outer,106-outer);
    AreaDraw(rp,94+k*inner,106-inner);
    AreaDraw(rp,94+k*outer,106-inner);
    AreaEnd(rp);
    SetAPen(rp,1);
    AreaMove(rp,94+k*outer,106+outer);
    AreaDraw(rp,94+k*inner,106+inner);
    AreaDraw(rp,94+k*outer,106+inner);
    AreaEnd(rp);
    SetAPen(rp,5);              /* light grey wall */
    if (k==1) RectFill(rp,94+inner,106-inner,94+outer,106+inner);
    else      RectFill(rp,94-outer,106-inner,94-inner,106+inner);
   }
  }
}

void demo()    /* demo mode: follow the left-hand wall until we get to */
{              /* the exit */
 long delay;
               /* be fast if the exit is far away, slow if it gets nearer */
 delay=19-18*(xsize-3-x+((wayout-y)<0?-(wayout-y):(wayout-y)))/
          ((wayout<ysize/2? ysize-wayout:wayout)+xsize-3);

 if(y==wayout && x==xsize-3) /* did we reach the exit ?*/
 {
  demomode=FALSE;            /* stop if we did */
  return;
 }                           /* is it possible to turn left ? */
 if(MAZETEST(x+dirincs[(direction-1)&3][0],y+dirincs[(direction-1)&3][1],
             xsize,mymaze))
 {
  left();                    /* yes, turn left */
  Delay(delay);
  forward();                 /* and one step forward */
 }
 else if(!forward()) right(); /* if not try to move forward, if everything */
                              /* fails, turn right */
 Delay(delay);
}

BOOL forward()                /* move one step forward if possible */
{
 if(!(MAZETEST(x+dirincs[direction][0],
             y+dirincs[direction][1],xsize,mymaze))) return(FALSE);

 x+=dirincs[direction][0];
 y+=dirincs[direction][1];
 moves++;
 sprintf(buffer," %ld/%ld ",
         moves,xsize-3-x+((wayout-y)<0?-(wayout-y):(wayout-y)));
 statusline(20,buffer);
 show3d();

 if(y==wayout && x==xsize-3)  /* has the exit been reached ?*/
 {
  SetAPen(rp,1);              /* yes, write some stupid text */
  SetBPen(rp,7);
  Move(rp,58,104);
  Text(rp,"Solved in",9);
  SetAPen(rp,6);
  SetBPen(rp,1);
  sprintf(buffer,"%ld moves.",moves);
  Move(rp,94-strlen(buffer)*4,116);
  Text(rp,buffer,strlen(buffer));
 }
 return(TRUE);
}

void left()                 /* turn left */
{
 direction=(--direction) & 3;
 show3d();
 statusline(40,heading[direction]);
}

void right()                /* turn right */
{
 direction=(++direction) & 3;
 show3d();
 statusline(40,heading[direction]);
}
