/*==================================================================
**
** MINEFIELD.C  watch out where you step...
**
** This file contains the code used to play minefield.
**
** This game is played on a 640x200 screen / window.
**
** The main routines in this file are:
**
** Each location
**
**
** Revision history (most recent first):
**
** 19Aug91  CC  1.  Started work on game.
** 04Jul91  CC  1.  Started work on game.
** 
**
**================================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <devices/narrator.h>
#include <libraries/translator.h>
#include <functions.h>


#pragma page "Private data"
/*===================================================================
**
** Private data
**
**=================================================================*/

#define FPE fprintf(stderr,


struct Library *TranslatorBase = NULL;

struct MsgPort write_port;

int  NarratorOpen = FALSE;

struct narrator_rb voice_io;

struct MsgPort read_port;
struct mouth_rb mouth_io;

BYTE audio_chan[] = {3,5,10,12};


struct Screen *mfScreen = NULL;
struct Window *mfWindow = NULL;
struct ViewPort *mfVP = NULL;
struct RastPort *mfRP = NULL;

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
#define GRAPHICS_REV 33L
#define INTUITION_REV 33L


USHORT ColorLut[32] = {
	0x000, 0xfff, 0xf00, 0x0f0,
	0x00f, 0xff0, 0xaaa, 0x555,
	0x0aa, 0xaaa, 0xa00, 0x0a0,
	0x00a, 0xaa0, 0x500, 0x005,
	0x0aa, 0xaaa, 0xa00, 0x0a0,
	0x00a, 0xaa0, 0x500, 0x005,
	0x0aa, 0xaaa, 0xa00, 0x0a0,
	0x00a, 0xaa0, 0x500, 0x005
};

#define BLACK 0L
#define WHITE 1L
#define RED 2L
#define GREEN 3L
#define BLUE 4L
#define YELLOW 5L
#define LTGRAY 6L
#define DKGRAY 7L
#define CYAN 8L


struct NewScreen mfNS = {
  0,0,		/* screen XY origin relative to View */
  640,200,	/* screen width and height */
  4,		/* screen depth (number of bitplanes) */
  WHITE,DKGRAY,	/* detail and block pens */
  HIRES,	/* display modes for this screen */
  CUSTOMSCREEN,	/* screen type */
  NULL,		/* pointer to default screen font */
  NULL,		/* screen title */
  NULL,		/* first in list of custom screen gadgets */
  NULL		/* pointer to custom BitMap structure */
};

struct NewWindow mfNW = {
  0,0,		/* window XY origin relative to TopLeft of screen */
  640,200,	/* window width and height */
  WHITE,CYAN,	/* detail and block pens */
  MOUSEBUTTONS|MENUPICK|CLOSEWINDOW|VANILLAKEY,	/* IDCMP flags */
  SMART_REFRESH|ACTIVATE|WINDOWCLOSE, /* Window flags */
  NULL,		/* first gadget in gadget list */
  NULL,		/* custom CHECKMARK imagery */
  NULL,		/* window title */
  NULL,         /* custom screen pointer */
  NULL,		/* custom bitmap */
  640,200,	/* minimum width and height */
  640,200,	/* maximum width and height */
  CUSTOMSCREEN  /* destination screen type */
};


#define GAMEMENU	0
#define GAMEMENUX	10
#define GAMEMENUWD	90
#define GAMEWD		140
#define GAMEHT		15
#define mn_NEWGAME	0
#define mn_QUIT		1

#define SIZEMENU	1
#define SIZEMENUX	(GAMEMENUX+GAMEMENUWD)
#define SIZEMENUWD	90
#define SIZEWD		140
#define SIZEHT		15
#define mn_20		0
#define mn_30		1
#define mn_40		2
#define mn_50		3
#define mn_60		4
#define mn_70		5
#define mn_80		6
#define mn_90		7
#define mn_100		8


struct TextAttr  Font80 = {
  (STRPTR)"topaz.font", TOPAZ_EIGHTY, FS_NORMAL, FPF_ROMFONT, };

struct IntuiText tx_New = {
   CYAN, DKGRAY, JAM2, 5, 3, &Font80,
   (UBYTE *) "  New   ", NULL,
};
struct IntuiText tx_Quit = {
   CYAN, DKGRAY, JAM2, 5, 3, &Font80,
   (UBYTE *) "  Quit  ", NULL,
};
struct IntuiText tx_20 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 20 mines ", NULL,
};
struct IntuiText tx_30 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 30 mines ", NULL,
};
struct IntuiText tx_40 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 40 mines ", NULL,
};
struct IntuiText tx_50 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 50 mines ", NULL,
};
struct IntuiText tx_60 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 60 mines ", NULL,
};
struct IntuiText tx_70 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 70 mines ", NULL,
};
struct IntuiText tx_80 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 80 mines ", NULL,
};
struct IntuiText tx_90 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) " 90 mines ", NULL,
};
struct IntuiText tx_100 = {
   CYAN, DKGRAY, JAM2, 25, 3, &Font80,
   (UBYTE *) "100 mines ", NULL,
};




struct MenuItem it_100 = {
   NULL,
   0, 120, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x0ffL,
   (APTR) &tx_100, NULL,
   0, NULL, NULL,
};
struct MenuItem it_90 = {
   &it_100,
   0, 105, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x17fL,
   (APTR) &tx_90, NULL,
   0, NULL, NULL,
};
struct MenuItem it_80 = {
   &it_90,
   0, 90, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1bfL,
   (APTR) &tx_80, NULL,
   0, NULL, NULL,
};
struct MenuItem it_70 = {
   &it_80,
   0, 75, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1dfL,
   (APTR) &tx_70, NULL,
   0, NULL, NULL,
};
struct MenuItem it_60 = {
   &it_70,
   0, 60, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1efL,
   (APTR) &tx_60, NULL,
   0, NULL, NULL,
};
struct MenuItem it_50 = {
   &it_60,
   0, 45, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKED,
   0x1f7L,
   (APTR) &tx_50, NULL,
   0, NULL, NULL,
};
struct MenuItem it_40 = {
   &it_50,
   0, 30, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1fbL,
   (APTR) &tx_40, NULL,
   0, NULL, NULL,
};
struct MenuItem it_30 = {
   &it_40,
   0, 15, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1fdL,
   (APTR) &tx_30, NULL,
   0, NULL, NULL,
};
struct MenuItem it_20 = {
   &it_30,
   0, 0, SIZEWD, SIZEHT,
   CHECKIT | ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0x1feL,
   (APTR) &tx_20, NULL,
   0, NULL, NULL,
};

struct Menu SizeMenu = {
   NULL,
   SIZEMENUX, 0, SIZEMENUWD, 0,
   MENUENABLED,
   (BYTE *) "Mines",
   &it_20,
};


struct MenuItem it_Quit = {
   NULL,
   0, 15, GAMEWD, GAMEHT,
   ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
   0L,
   (APTR) &tx_Quit, NULL,
   'Q', NULL, NULL,
};
struct MenuItem it_New = {
   &it_Quit,
   0, 0, GAMEWD, GAMEHT,
   ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
   0L,
   (APTR) &tx_New, NULL,
   'N', NULL, NULL,
};

struct Menu GameMenu = {
   &SizeMenu,
   GAMEMENUX, 0, GAMEMENUWD, 0,
   MENUENABLED,
   (BYTE *) "Game",
   &it_New,
};


#define COLUMNS   16
#define ROWS      16

#define SQUARE_WD 22
#define SQUARE_HT 10

#define X_OFFSET  ((640-(COLUMNS*SQUARE_WD))/2)
#define Y_OFFSET  ((190-(ROWS*SQUARE_HT))/2)+10

UBYTE   mfArray[ROWS*COLUMNS];

int     NumMines = 50;


char    TitleString[80] = { "MineField" };

#define mf_UNKNOWN 0
#define mf_KNOWN   1
#define mf_MINE    2
#define mf_MARKED  4

int     GameReady = FALSE;

char   *Intro[] = {
  "Welcome to mine field.",
  "Try to cross the mine field from top left to lower right.",
  "If you can make it without stepping on a mine you win!",
  "Mine field squares can be marked by pointing at them and pressing a key.",
  "Each square stepped on will show the number of mines in adjacent squares.",
  "Are you ready?  Lets start.",
  NULL
};


#pragma page "mfCleanup()"
/*===================================================================
**
** mfCleanup() closes the mf screen and Amiga graphics
** libraries.
**
**=================================================================*/
void mfCleanup( void )
{
  if (mfWindow) {
    ClearMenuStrip( mfWindow );
    CloseWindow( mfWindow );
    mfWindow = NULL;
  }
  if (mfScreen) {
    CloseScreen( mfScreen );
    mfScreen = NULL;
  }
  if (write_port.mp_SigBit != -1)
    FreeSignal( (long)write_port.mp_SigBit );
  if (NarratorOpen)
    CloseDevice( (struct IORequest *) &voice_io );
  
  if (TranslatorBase) {
    CloseLibrary( TranslatorBase );
    TranslatorBase = NULL;
  }
  if (GfxBase) {
    CloseLibrary( GfxBase );
    GfxBase = NULL;
  }
  if (IntuitionBase) {
    CloseLibrary( IntuitionBase );
    IntuitionBase = NULL;
  }

  exit(0);
}


#pragma page "mfOpen()"
/*===================================================================
**
** mfOpen() open up the mf screen and window.  If a mf
** screen already exists close it first.
**
**=================================================================*/
void mfOpen( void )
{

/*
** Open the Amiga libraries
*/
  GfxBase = (struct GfxBase *)
    OpenLibrary( "graphics.library", GRAPHICS_REV );
  if (!GfxBase) {
    FPE "OpenLibrary( GfxBase ) failed!\n");
    mfCleanup();
  }

  IntuitionBase = (struct IntuitionBase *)
    OpenLibrary( "intuition.library", INTUITION_REV );
  if (!IntuitionBase) {
    FPE "OpenLibrary( IntuitionBase ) failed!\n");
    mfCleanup();
  }


/*
** Open the mf screen
*/
  mfScreen = OpenScreen( &mfNS );
  if (!mfScreen) {  
    FPE "mfInit() - Open Screen failed\n");
    mfCleanup();
  }

  mfVP = &mfScreen->ViewPort;
  LoadRGB4(mfVP, ColorLut, 32L );

/*
** Open the mf window
*/
  mfNW.Screen = mfScreen;
  mfNW.Title = (UBYTE *)TitleString;
  mfWindow = OpenWindow( &mfNW );
  if (!mfWindow) {
    FPE "mfInit() - OpenWindow failed\n");
    mfCleanup();
  }
  mfRP = mfWindow->RPort;
  SetMenuStrip( mfWindow, &GameMenu );

  SetDrMd( mfRP, (long)JAM1 );
}


#pragma page "SayInit()"
/*===================================================================
**
** SayInit() initializes the stuff we'll need to make the Amiga talk.
**
**=================================================================*/
void SayInit( void )
{
  write_port.mp_SigBit = -1;

  TranslatorBase = OpenLibrary( "translator.library", 0L );
  if (!TranslatorBase) {
    FPE "OpenLibrary( TranslatorBase ) failed!\n");
    mfCleanup();
  }

  if (OpenDevice( "narrator.device", 0L, (struct IORequest *)&voice_io, 0L )) {
    FPE "Can't open narrator.device\n");
    mfCleanup();
  }
  NarratorOpen = TRUE;

  write_port.mp_SigBit = AllocSignal(-1L);
  if (write_port.mp_SigBit == -1) {
    FPE "Can't allocate signal bit\n");
    mfCleanup();
  }

  write_port.mp_Node.ln_Name = "speech_write";
  write_port.mp_Node.ln_Type = NT_MSGPORT;
  write_port.mp_Flags = PA_SIGNAL;
  write_port.mp_SigTask = FindTask( NULL );

  NewList( &write_port.mp_MsgList );

/*------------
#define	DEFPITCH	110L
#define	DEFRATE	150L
#define	DEFVOL	64L
#define	DEFFREQ	22200L
#define	MALE	0L
#define	FEMALE	1L
#define	NATURALF0	0L
#define	ROBOTICF0	1L
#define	DEFSEX	MALE
#define	DEFMODE	NATURALF0
#define	MINRATE	40L
#define	MAXRATE	400L
#define	MINPITCH	65L
#define	MAXPITCH	320L
#define	MINFREQ	5000L
#define	MAXFREQ	28000L
#define	MINVOL	0L
#define	MAXVOL	64L
------------*/
  voice_io.rate = 150;
  voice_io.pitch = 100;
  voice_io.mode = NATURALF0;
  voice_io.sex = MALE;
  voice_io.volume = 64;
  voice_io.sampfreq = 20000;
  voice_io.mouths = 0;
  voice_io.ch_masks = (UBYTE *)audio_chan;
  voice_io.nm_masks = 4;
}


#pragma page "SayT()"
/*===================================================================
**
** SayT() passes the english text specified through the translator
** to convert it to phonemes then passes the phonemes to the narrator
** device...
**
**=================================================================*/
void SayT( char *text )
{
  UBYTE PhonBuf[1024];
  LONG  textLen;
  long  rtncode;

  textLen = strlen( text );
  rtncode = Translate( text, textLen, (char *)PhonBuf, 1024L );

  if (rtncode) {
    DisplayBeep(NULL);
    FPE "Translate() failed!\n");
    return;
  }

  voice_io.message.io_Command = CMD_WRITE;
  voice_io.message.io_Offset = 0;
  voice_io.message.io_Data = (APTR)PhonBuf;
  voice_io.message.io_Length = strlen( (char *)PhonBuf );

  voice_io.message.io_Message.mn_Node.ln_Type = NT_MESSAGE;
  voice_io.message.io_Message.mn_Length = sizeof(voice_io);
  voice_io.message.io_Message.mn_ReplyPort = &write_port;

  SendIO( (struct IORequest *)&voice_io );
  WaitIO( (struct IORequest *)&voice_io );

}


#pragma page "MineNeighbors()"
/*===================================================================
**
** MineNeighbors() returns the number of adjoining squares containing
** mines...
**
**=================================================================*/
int MineNeighbors( int row, int col )
{
  int     Ncnt;
  UBYTE  *mp;

  Ncnt = 0;
  mp = &mfArray[col+row*COLUMNS];


  if (row > 0) {
    if (col > 0) {
      if (*(mp-(COLUMNS+1)) & mf_MINE)
        Ncnt += 1;
    }

    if (*(mp-COLUMNS) & mf_MINE)
      Ncnt += 1;

    if (col < COLUMNS-1) {
      if (*(mp-(COLUMNS-1)) & mf_MINE)
        Ncnt += 1;
    }
  }

  if (col > 0) {
    if (*(mp-1) & mf_MINE)
      Ncnt += 1;
  }

  if (col < COLUMNS-1) {
    if (*(mp+1) & mf_MINE)
      Ncnt += 1;
  }

  if (row < ROWS-1) {
    if (col > 0) {
      if (*(mp+(COLUMNS-1)) & mf_MINE)
        Ncnt += 1;
    }

    if (*(mp+COLUMNS) & mf_MINE)
      Ncnt += 1;

    if (col < COLUMNS-1) {
      if (*(mp+(COLUMNS+1)) & mf_MINE)
        Ncnt += 1;
    }
  }

  return(Ncnt);
}


#pragma page "KnownNeighbors()"
/*===================================================================
**
** KnownNeighbors() returns the number of adjoining squares whose
** contents are known...
**
**=================================================================*/
int KnownNeighbors( int row, int col )
{
  int     Ncnt;
  UBYTE  *mp;

  Ncnt = 0;
  mp = &mfArray[col+row*COLUMNS];


  if (row > 0) {
    if (col > 0) {
      if (*(mp-(COLUMNS+1)) & mf_KNOWN)
        Ncnt += 1;
    }

    if (*(mp-COLUMNS) & mf_KNOWN)
      Ncnt += 1;

    if (col < COLUMNS-1) {
      if (*(mp-(COLUMNS-1)) & mf_KNOWN)
        Ncnt += 1;
    }
  }

  if (col > 0) {
    if (*(mp-1) & mf_KNOWN)
      Ncnt += 1;
  }

  if (*(mp) & mf_KNOWN)
    Ncnt += 1;

  if (col < COLUMNS-1) {
    if (*(mp+1) & mf_KNOWN)
      Ncnt += 1;
  }

  if (row < ROWS-1) {
    if (col > 0) {
      if (*(mp+(COLUMNS-1)) & mf_KNOWN)
        Ncnt += 1;
    }

    if (*(mp+COLUMNS) & mf_KNOWN)
      Ncnt += 1;

    if (col < COLUMNS-1) {
      if (*(mp+(COLUMNS+1)) & mf_KNOWN)
        Ncnt += 1;
    }
  }

  return(Ncnt);
}



#pragma page "DrawBoxCover()"
/*===================================================================
**
** DrawBoxCover() draws a cover for the box at position row & col.
**
**=================================================================*/
void DrawBoxCover( int Row, int Col, char *Symbol )
{
  long   x1,y1, x2,y2;


  x1 = X_OFFSET + Col*SQUARE_WD;
  y1 = Y_OFFSET + Row*SQUARE_HT;

  x2 = x1 + SQUARE_WD-1;
  y2 = y1 + SQUARE_HT-1;

  SetAPen( mfRP, WHITE );
  RectFill( mfRP, x1,y1, x2,y2 );
  SetAPen( mfRP, DKGRAY );
  RectFill( mfRP, x1+2,y1+1, x2,y2 );
  SetAPen( mfRP, LTGRAY );
  RectFill( mfRP, x1+2,y1+1, x2-2,y2-1 );

  if (Symbol) {

    Move( mfRP, x1+7,y1+8 );
    SetAPen( mfRP, RED );
    Text( mfRP, Symbol, 1L );

  }
}


#pragma page "DrawBox()"
/*===================================================================
**
** DrawBox() draws a cover for the box at position row & col.
**
**=================================================================*/
void DrawBox( int Row, int Col, char *Symbol )
{
  long   x1,y1, x2,y2;


  x1 = X_OFFSET + Col*SQUARE_WD;
  y1 = Y_OFFSET + Row*SQUARE_HT;

  x2 = x1 + SQUARE_WD-1;
  y2 = y1 + SQUARE_HT-1;

  SetAPen( mfRP, WHITE );
  RectFill( mfRP, x1,y1, x2,y2 );
  SetAPen( mfRP, DKGRAY );
  RectFill( mfRP, x1,y1, x2-2,y2-1 );
  SetAPen( mfRP, LTGRAY );
  RectFill( mfRP, x1+2,y1+1, x2-2,y2-1 );

  if (Symbol) {

    Move( mfRP, x1+7,y1+8 );
    SetAPen( mfRP, BLUE );
    Text( mfRP, Symbol, 1L );

  }
}


#pragma page "ShowSafe()"
/*===================================================================
**
** ShowSafe() shows this square and all the other squares guarranteed
** to be safe...
**
**=================================================================*/
void ShowSafe( int row, int col )
{
  int   Ncnt;
  char  mstring[16];

  if (row < 0 || col < 0 || row >= ROWS || col >= COLUMNS) {
    return;
  }

  if (mfArray[col+row*COLUMNS] & mf_KNOWN)
    return;

  Ncnt = MineNeighbors( row, col );

  mfArray[col+row*COLUMNS] |= mf_KNOWN;

  if (Ncnt) {
    sprintf( mstring, "%d\n", Ncnt );
    DrawBox( row, col, mstring );
  }
  else {
    DrawBox( row, col, NULL );
    ShowSafe( row-1, col-1 );
    ShowSafe( row-1, col );
    ShowSafe( row-1, col+1 );
    ShowSafe( row, col-1 );
    ShowSafe( row, col+1 );
    ShowSafe( row+1, col-1 );
    ShowSafe( row+1, col );
    ShowSafe( row+1, col+1 );
  }
}



#pragma page "DoSetup()"
/*===================================================================
**
** DoSetup() clears our window then draws the dots on it.
**
**=================================================================*/
void DoSetup( void )
{
  int  x,y,k;

  SetAPen( mfRP, LTGRAY );
  RectFill( mfRP, 2L,10L, 638L,197L );

/*
** Draw the initialized play field
*/

  for (x=0; x<COLUMNS; ++x) {
    for (y=0; y<ROWS; ++y) {

      DrawBoxCover( y, x, NULL );

    }
  }

/*
** Clear the mine field
*/
  for (x=0; x<ROWS*COLUMNS; ++x)
    mfArray[x] = mf_UNKNOWN;

/*
** Determine which squares have mines
*/
  for (k=0; k<NumMines; ++k) {

    x = (rand() * (long)COLUMNS) / RAND_MAX;
    y = (rand() * (long)ROWS) / RAND_MAX;

    if (mfArray[x+y*COLUMNS] == mf_MINE || (x <= 1 && y <= 1) ||
      (x == COLUMNS-1 && y == ROWS-1)) {
      k = k - 1;
      continue;
    }
    else {
      mfArray[x+y*COLUMNS] |= mf_MINE;
    }
  }

/*
** Display the safe squares in the upper left corner...
*/
  ShowSafe( 0, 0 );

  GameReady = TRUE;

}


#pragma page "WhichBox()"
/*===================================================================
**
** WhichBox() determines to which box the cursor is pointing.
**
** Returns TRUE if successful
**         FALSE if not pointing at a box
**
**=================================================================*/
int WhichBox( int x, int y, int *row, int *col )
{


  if (x < X_OFFSET || y < Y_OFFSET)
    return(FALSE);

  if (x >= X_OFFSET+COLUMNS*SQUARE_WD || y >= Y_OFFSET+ROWS*SQUARE_HT)
    return(FALSE);

  x -= X_OFFSET;
  y -= Y_OFFSET;

  *row = y / SQUARE_HT;
  *col = x / SQUARE_WD;
  return(TRUE);
}


#pragma page "DrawMines()"
/*===================================================================
**
** DrawMines() draws the positions of all mines in the mine field.
**
**=================================================================*/
void DrawMines( void )
{
  int  row,col;
  char mstring[16];

  strcpy( mstring, "*" );

  for (row=0; row<ROWS; ++row) {
    for (col=0; col<COLUMNS; ++col) {
      if (mfArray[col+row*COLUMNS] & mf_MINE) {
        DrawBoxCover( row, col, mstring );
      }
    }
  }
}


#pragma page "MakeMove()"
/*===================================================================
**
** MakeMove() checks to see if the x & y coords are on the playing
** field.  If they are then KNOWN neighbors are looked for.  If none
** then this is an invalid move (DisplayBeep()).  If ok then if there
** is a mine underfoot the gamediscontinues the old game and setsup a new one...
**
** Returns TRUE if we're still alive.
**         FALSE if we stepped on a mine.
**
**=================================================================*/
int MakeMove( long x, long y )
{
  int   row, col;


  if (!WhichBox( (int)x, (int)y, &row, &col))
    return(TRUE);

/*
** Can't step on a marked square...
*/
  if (mfArray[col+row*COLUMNS] & mf_MARKED) {
    SayT( "Better not step there.");
    return(TRUE);
  }

/*
** Can only step next to know territory
*/
  if (!KnownNeighbors( row, col )) {
    DisplayBeep(NULL);
    SayT( "You may not step there yet.");
    return(TRUE);
  }

/*
** Did we step on a mine?
*/
  if (mfArray[col+row*COLUMNS] & mf_MINE) {
    return(FALSE);
  }

/*
** Display the square we stepped on...
*/
  ShowSafe( row, col );

  return(TRUE);
}
 

#pragma page "MarkSquare()"
/*===================================================================
**
** MarkSquare() checks to see if the x & y coords are on the playing
** field.  If they are the square is UNKNOWN then the square is marked
** with the specified character.
**
**=================================================================*/
void MarkSquare( long x, long y, short code )
{
  int   row, col;


  if (!WhichBox( (int)x, (int)y, &row, &col))
    return;

  if (mfArray[col+row*COLUMNS] & mf_KNOWN) {
    DisplayBeep( NULL );
    return;
  }

  if (mfArray[col+row*COLUMNS] & mf_MARKED) {
    DrawBoxCover( row, col, NULL );
    mfArray[col+row*COLUMNS] &= (~mf_MARKED);
  }
  else {
    DrawBoxCover( row, col, "o" );
    mfArray[col+row*COLUMNS] |= mf_MARKED;
  }

}


#pragma page "NewGame()"
/*===================================================================
**
** NewGame() discontinues the old game and setsup a new one...
**
**=================================================================*/
void NewGame( void )
{
  char   tstring[80];

  sprintf( tstring, "%s - Playing with %d mines...\0", TitleString, NumMines );
  SetWindowTitles( mfWindow, tstring, NULL );
  DoSetup();
  GameReady = TRUE;

}


/*===================================================================
**
** WindowService() provides gadget and menu service for the
** defaults window.
**
** Check for render abort request...
**
** Return TRUE if render abort request
**        FALSE if not render abort
**
**=================================================================*/
int WindowService( void )
{
  struct IntuiMessage *message;
  ULONG		 class;
  USHORT         code;
  long           mx,my;
  int            YouMadeMove = FALSE;


  for (;;) {

    Wait( 1L << mfWindow->UserPort->mp_SigBit );

    while (message = (struct IntuiMessage *)
      GetMsg( (struct MsgPort *)mfWindow->UserPort)) {

      class = message->Class;
      code  = message->Code;
      mx = message->MouseX;
      my = message->MouseY;
      ReplyMsg( (struct Message *)message );

      switch ( class ) {

        case MOUSEBUTTONS:
          if (code == SELECTUP && GameReady) {
            if (!MakeMove( mx, my )) {
              SayT( "BOOM!  You stepped on a mine!");
              DrawMines();
              GameReady = FALSE;
            }
            else {
              if (mfArray[ROWS*COLUMNS-1] & mf_KNOWN) {
                SayT( "You made it threw the mine field!" );
                DrawMines();
                GameReady = FALSE;
              }
            }
          }
          break;

        case VANILLAKEY:
          if (GameReady) {
            MarkSquare( mx, my, code);
          }
          break;

        case MENUPICK:
          switch (MENUNUM(code)) {

            case GAMEMENU:
              if ( ITEMNUM(code) == mn_NEWGAME )
                NewGame();
              else if ( ITEMNUM(code) == mn_QUIT )
                mfCleanup();
	      break;

            case SIZEMENU:
              switch (ITEMNUM(code)) {
                case mn_20:
                  NumMines = 20;
                  break;
                case mn_30:
                  NumMines = 30;
                  break;
                case mn_40:
                  NumMines = 40;
                  break;
                case mn_50:
                  NumMines = 50;
                  break;
                case mn_60:
                  NumMines = 60;
                  break;
                case mn_70:
                  NumMines = 70;
                  break;
                case mn_80:
                  NumMines = 80;
                  break;
                case mn_90:
                  NumMines = 90;
                  break;
                case mn_100:
                  NumMines = 100;
                  break;
                default:
                  FPE "Unknown number of mines!\n");
                  mfCleanup();
              }
	      break;

            default:
              break;
          }
          break;

        case CLOSEWINDOW:
          mfCleanup();
          break;

        default:
	  printf("WindowService() - Bad msg class $%08lx!\n", class );
          mfCleanup();
      }
    }

  }
}


#pragma page "main()"
/*===================================================================
**
** main()  initialize, issue the welcome message, setup a game field
** and start the new game...
**
**=================================================================*/
void main( int argc, char **argv )
{
  int   k;
  long  Seconds, Micros;


  SayInit();
  mfOpen();

  CurrentTime( &Seconds, &Micros );

  SetAPen( mfRP, WHITE );

  for (k=0; Intro[k]; ++k) {
    Move( mfRP, 20L, 40L+k*11 );
    Text( mfRP, Intro[k], strlen(Intro[k]) );
  }

  for (k=0; Intro[k]; ++k) {
    SayT( Intro[k] );
  }

  Micros = (Micros / 1000) & 0x3ff;
  for (k=0; k<Micros; ++k) {
    Seconds = rand();
  }

  NewGame();
  WindowService();

  mfCleanup();
}

