home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 Mobile / Chip_Mobile_2001.iso / palm / spiele / pilotmin / pilotmin.exe / pmines / src / mine.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-27  |  21.9 KB  |  975 lines

  1. /*
  2.  * PilotMines is Copyright (c) 1997-2000 by Thomas Pundt
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software for any
  5.  * purpose, without fee, and without a written agreement is hereby granted,
  6.  * provided that the above copyright notice and this paragraph and the
  7.  * following two paragraphs appear in all copies.
  8.  *
  9.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
  10.  * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
  11.  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
  12.  * THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  13.  *
  14.  * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
  15.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  16.  * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
  17.  * BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
  18.  * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  19.  *
  20.  */
  21.  
  22. /*
  23.  * Incorporates changes from Lucas Bremgartner <breml@trash.net>
  24.  * and Laurent Thaler <lthaler@free.net>
  25.  */
  26.  
  27. #include <Common.h>
  28. #include <System/KeyMgr.h>
  29. #include <System/SysAll.h>
  30. #include <UI/UIAll.h>
  31.  
  32. #include "minercp.h"
  33. #include "mine.h"
  34. #include "hscore.h"
  35.  
  36. /*
  37.  * bit 5   : Marked
  38.  * bit 4   : Cover/uncovered
  39.  * bit 3-0 : number of mines in adjoining cells
  40.  */
  41. #define MINE    0x09
  42. #define COVERED 0x10
  43. #define MARKED    0x20
  44. #define TMPMARK    0x40
  45.  
  46. static int width, height, length_bitmap, bitmap_offset;
  47.  
  48. #define InArray(x,y)    (((x)>=0 && (x)<width) && ((y)>=0 && (y)<height))
  49. #define IsVisible(x,y)    ((game.minefield[x][y] & COVERED) ? 0 : 1)
  50.  
  51. /*
  52.  * Game options
  53.  */
  54. #define ShowAll          1
  55. #define AlternateControl 2
  56. #define FirstNoMine      4
  57. #define CornerNoMine     8    // due to Laurent Thaler
  58.  
  59. // every level has it's own number of mines per level.
  60. static int levelmines[] = { 
  61.   10, 30,  50,  60, // 16x14-Layout
  62.   20, 50,  80, 100, // 20x18-Layout
  63.   40, 80, 140, 170  // 26x24-Layout
  64. };
  65.  
  66. static struct { SWord x,y; } stack[WIDTH*HEIGHT];
  67.  
  68. static int numuncovered, marked, nummines, StckPtr;
  69. static Boolean isHighlighted;
  70. static ULong GameStartedAt;
  71. static WinHandle bitmaps = 0;  // Offscreen Window with tile bitmaps
  72.  
  73. long score = 0;
  74.  
  75. static DmOpenRef pmDB;
  76. Game game;
  77.  
  78. /*
  79.  * mapping function, that applies f to all neighbours of (x,y)
  80.  */
  81. static int Neighbours (func f, SWord x, SWord y)
  82. {
  83.   int res = 0;
  84.   res += f(x-1,y-1); res += f(x,y-1); res += f(x+1,y-1);
  85.   res += f(x-1,y  );                  res += f(x+1,y  );
  86.   res += f(x-1,y+1); res += f(x,y+1); res += f(x+1,y+1);
  87.   return res;
  88. }
  89.  
  90. static int IsMine(SWord x, SWord y)
  91. {
  92.   return (InArray(x,y) && (game.minefield[x][y] & 0xf) == MINE);
  93. }
  94.                                                                 
  95. static int IsMarked(SWord x, SWord y)
  96. {
  97.  return (InArray(x,y) && (game.minefield[x][y] & MARKED));
  98. }
  99.  
  100. /*
  101.  * computes the number of mines around field (x,y)
  102.  */
  103. static int countMines(int x, int y)
  104. {
  105.   return Neighbours (IsMine, x, y);
  106. }
  107.  
  108. /*
  109.  * computes the marked fields (supposed mines) around field (x,y)
  110.  */
  111. static int foundMines(int x, int y)
  112. {
  113.   return Neighbours (IsMarked, x, y);
  114. }
  115.  
  116. /*
  117.  * pushes (x,y) on the stack, if field (x,y) isn't marked, isn't 
  118.  * visible, and isn't already on the stack.
  119.  */
  120. static int push(SWord x, SWord y)
  121. {
  122.   if (InArray(x,y) && !(game.minefield[x][y] & (MARKED|TMPMARK)) &&
  123.      (game.minefield[x][y] & COVERED)) {
  124.     game.minefield[x][y] |= TMPMARK;
  125.     stack[StckPtr].x=x;
  126.     stack[StckPtr].y=y;
  127.     StckPtr++;
  128.   }
  129.   return StckPtr;
  130. }
  131.  
  132. /*
  133.  * pops the topmost element (x,y) off the stack, and stores it in
  134.  * the parameters x and y.
  135.  */
  136. static void pop(SWord *x, SWord *y)
  137. {
  138.   StckPtr--;
  139.   *x=stack[StckPtr].x;
  140.   *y=stack[StckPtr].y;
  141. }
  142.  
  143. /*
  144.  * Draw the cell at coordinates (x,y); if Id==0, look at the contents 
  145.  * of field (x,y) and decide what pattern to draw; else draw pattern
  146.  * Id.
  147.  */
  148. static void DrawCell(SWord x, SWord y, int Id)
  149. {
  150.   RectangleType rect;
  151.   Byte field = game.minefield[x][y] & ~TMPMARK;
  152.   
  153.   if (Id==0) {
  154.     if (field & MARKED)
  155.       Id = ID_Flag + bitmap_offset;
  156.     else if (field & COVERED)
  157.       Id = ID_Covered + bitmap_offset;
  158.     else 
  159.       Id = ID_Blank + field + bitmap_offset;
  160.   }
  161.  
  162.   // Copy bitmap from offscreen window to active window
  163.   rect.topLeft.x = (Id - ID_Flag - bitmap_offset) * length_bitmap;
  164.   rect.topLeft.y = 0;
  165.   rect.extent.x = rect.extent.y = length_bitmap;
  166.   WinCopyRectangle(bitmaps,
  167.            WinGetActiveWindow(),
  168.            &rect,
  169.            x*length_bitmap,
  170.            (y+2)* length_bitmap,
  171.            scrCopy);
  172. }
  173.  
  174. /*
  175.  * Draw the "mines left to mark" status information
  176.  */
  177. static void DrawMarked()
  178. {
  179.    char buf[3];
  180.    buf[0] = (nummines - marked) / 100 + 48;
  181.    buf[1] = ((nummines - marked) % 100 ) / 10 + 48;
  182.    buf[2] = (nummines - marked) % 10 + 48;
  183.    WinDrawInvertedChars(buf, 3, 10, 0);
  184. }
  185.  
  186. /*
  187.  * Draw the "passed time" status information; if a menubar is active,
  188.  * don't draw.
  189.  */
  190. static void DrawTime()
  191. {
  192.   char buf[5] = "00:00";
  193.   ULong actseconds;
  194.   MenuBarPtr menu;
  195.  
  196.   if ((menu = MenuGetActiveMenu()) && menu->attr.visible)
  197.     return;
  198.  
  199.   actseconds = TimGetSeconds() - GameStartedAt;
  200.  
  201.   // pilot switched off 
  202.   if (actseconds-game.seconds>2) {
  203.     GameStartedAt = TimGetSeconds() - game.seconds;
  204.     actseconds = TimGetSeconds() - GameStartedAt;
  205.   }
  206.  
  207.   game.seconds = actseconds;
  208.   if (actseconds>3599)
  209.     actseconds = 3599;
  210.  
  211.   buf[4] = actseconds % 10 + 48;
  212.   buf[3] = (actseconds / 10) % 6 + 48;
  213.   buf[1] = (actseconds / 60) % 10 + 48;
  214.   buf[0] = (actseconds / 600) % 10 + 48;
  215.   WinDrawInvertedChars(buf, 5, 130, 0);
  216. }
  217.  
  218. /*
  219.  * Draw the field with all elements uncovered.
  220.  */
  221. static void UncoverAll()
  222. {
  223.   SWord x,y;
  224.  
  225.   if (!(game.options & ShowAll))
  226.     return;
  227.   
  228.   for (y=0; y<height; y++)
  229.     for (x=0; x<width; x++)
  230.       if (!IsVisible(x,y)) {
  231.         game.minefield[x][y] &= 0xf;
  232.         DrawCell(x,y,0);
  233.       }
  234. }
  235.  
  236. /*
  237.  * Draw the field; is called, if status was restored after an application
  238.  * switch.
  239.  */
  240. static void ShowFormMine()
  241. {
  242.   SWord x,y;
  243.  
  244.   FrmDrawForm(FrmGetActiveForm());
  245.   for (y=0; y<height; y++)
  246.     for (x=0; x<width; x++)
  247.       DrawCell(x,y,0);
  248.   GameStartedAt = TimGetSeconds() - game.seconds;
  249.   DrawMarked();
  250. }
  251.  
  252. /*
  253.  * show the contents of field (x,y); "recursively" (well a recursive
  254.  * function actually would crash the application :-) uncover all 
  255.  * neighbours of a field, if it has no mine as neighbour.
  256.  */
  257. static int Show(SWord x, SWord y)
  258. {
  259.   push(x,y);
  260.   while(StckPtr) {  
  261.     pop(&x,&y);
  262.     game.minefield[x][y] &= ~COVERED;
  263.     numuncovered++;
  264.     DrawCell(x, y, 0);
  265.     if (IsMine(x,y)) {
  266.       if (game.done == IsRunning) 
  267.         game.done = IsLost;
  268.         UncoverAll();
  269.     } else {
  270.       if (game.done == IsRunning && numuncovered + nummines == width*height) {
  271.         game.done = IsWon;
  272.         score = TimGetSeconds() - GameStartedAt;
  273.         if (score>3599) score = 3599;
  274.         if (score==0) score = 1;
  275.       }
  276.       if (game.minefield[x][y] == TMPMARK) {
  277.         Neighbours (push, x, y);
  278.       }
  279.     }
  280.   }
  281.   return 0;
  282. }
  283.  
  284. /*
  285.  * highlight covered field (x,y). Is called, if you tap on an uncovered field.
  286.  */
  287. static int Highlight(SWord x, SWord y)
  288. {
  289.   if (InArray(x,y) && !IsMarked(x,y) && !IsVisible(x,y))
  290.     DrawCell(x, y, ID_Gray + bitmap_offset);
  291.   return 0;
  292. }
  293.  
  294. /*
  295.  * unhighlight previously highlighted field.
  296.  */
  297. static int unHighlight(SWord x, SWord y)
  298. {
  299.   if (InArray(x,y) && !IsMarked(x,y) && !IsVisible(x,y))
  300.     DrawCell(x, y, 0);
  301.   return 0;
  302. }
  303.  
  304. static void CreateOffscreenWindow()
  305. {
  306.   SWord i;
  307.   RectangleType r;
  308.   WinHandle tmpHandle;
  309.   VoidHand bitmapHandle;
  310.   BitmapPtr bitmap;
  311.   Word err;
  312.  
  313.   // level also determines board layout!!
  314.   length_bitmap = 10;
  315.   width = 16;
  316.   height = 14;
  317.   bitmap_offset = 0;
  318.  
  319.   if (game.level > 3) {
  320.     length_bitmap = 8;
  321.     width = 20;
  322.     height = 18;
  323.     bitmap_offset = 100;
  324.   }
  325.   if (game.level > 7) {
  326.     length_bitmap = 6;
  327.     width = 26;
  328.     height = 24;
  329.     bitmap_offset = 200;
  330.   }
  331.  
  332.   // the borders in 26x24 mode won't exactly fill the board, so we
  333.   // erase the background.
  334.   r.topLeft.x=0;
  335.   r.topLeft.y=12;
  336.   r.extent.x=160;
  337.   r.extent.y=148;
  338.   WinEraseRectangle(&r,0);
  339.  
  340.   bitmaps = WinCreateOffscreenWindow( 14*length_bitmap, 
  341.                       length_bitmap, 
  342.                       screenFormat, 
  343.                       &err );
  344.   tmpHandle = WinSetDrawWindow(bitmaps);
  345.   
  346.   for (i=0; i<13; i++) {
  347.     bitmapHandle = DmGetResource ('Tbmp', ID_Flag + i + bitmap_offset);
  348.     bitmap = MemHandleLock (bitmapHandle);
  349.     WinDrawBitmap(bitmap, i*length_bitmap, 0);
  350.     MemHandleUnlock (bitmapHandle);
  351.     DmReleaseResource (bitmapHandle);
  352.   } 
  353.  
  354.   WinSetDrawWindow (tmpHandle);
  355. }
  356.  
  357. /*
  358.  * compute values "numuncovered" and "marked" after restoring 
  359.  * saved status from an application switch.
  360.  */
  361. static void InitBoard()
  362. {
  363.   SWord x, y;
  364.  
  365.   numuncovered = 0;
  366.   marked = 0;
  367.  
  368.   // also sets "height" and "width"
  369.   CreateOffscreenWindow();
  370.  
  371.   for (y=0; y<height; y++)
  372.     for (x=0; x<width; x++) {
  373.       if (IsMarked(x,y))
  374.         marked++;
  375.       if (IsVisible(x,y))
  376.         numuncovered++;
  377.   }
  378.  
  379. }
  380.  
  381. /*
  382.  * Mines are distributed on the board; depending on choosen mode
  383.  * the position of the first tap or corners are also taken into
  384.  * account.
  385.  */
  386. static void SetupBoard (SWord tap_x, SWord tap_y)
  387. {
  388.   SWord      ix,iy,pieces;
  389.  
  390.   SysRandom(TimGetSeconds());
  391.   for (pieces=0; pieces<nummines; pieces++) {
  392.     do {
  393.       ix = SysRandom(0) % (160 / length_bitmap);
  394.       iy = SysRandom(0) % (140 / length_bitmap);
  395.     } while ( IsMine(ix,iy)                          // already has mine
  396.           || ((game.options & FirstNoMine) &&    // field of first tap
  397.           ((ix == tap_x) && (iy == tap_y)) )
  398.           || ((game.options & CornerNoMine) &&   // one of the corners
  399.           (    ((ix == 0)       && (iy == 0)) 
  400.             || ((ix == 0)       && (iy == height-1)) 
  401.             || ((ix == width-1) && (iy == 0)) 
  402.             || ((ix == width-1) && (iy == height-1)) ))
  403.           );
  404.     game.minefield[ix][iy] = MINE+COVERED;
  405.   }
  406.  
  407.   for (iy=0; iy<height; iy++)
  408.     for (ix=0; ix<width; ix++)
  409.       if (!IsMine(ix,iy))
  410.         game.minefield[ix][iy] = countMines(ix,iy)+COVERED;
  411. }
  412.  
  413. /*
  414.  * Initialize the game and make all necessary drawings. 
  415.  * Computing an initial mine layout is delayed until after the
  416.  * first tap.
  417.  */
  418. static void InitFormMine()
  419. {
  420.   SWord      x,y;
  421.  
  422.   if (bitmaps) {
  423.     WinDeleteWindow(bitmaps, false);
  424.     CreateOffscreenWindow();
  425.   }
  426.  
  427.   FrmDrawForm(FrmGetActiveForm());
  428.  
  429.   for (y=0; y<height; y++) {
  430.     for (x=0; x<width; x++) {
  431.       game.minefield[x][y] = COVERED;
  432.       DrawCell(x, y, ID_Covered + bitmap_offset);
  433.     }
  434.   }
  435.  
  436.   numuncovered = marked = StckPtr = score = 0;
  437.   game.done = IsToBeStarted;
  438.   isHighlighted = false;
  439.   DrawMarked();
  440. }
  441.  
  442. /*
  443.  * Handle any action necessary, if you tap on field (x,y); 
  444.  * "ctrl == true" means, you've also pressed the PageUp or PageDown button;
  445.  * in that case, a covered unmarked field is marked or a covered marked
  446.  * field is unmarked. Else, a covered field is uncovered.
  447.  */
  448. static Boolean HandlePenDownEvent(SWord x, SWord y, Boolean ctrl)
  449. {
  450.   if (!InArray(x,y))
  451.     return false;
  452.  
  453.   if (game.done == IsToBeStarted) {
  454.     SetupBoard(x,y); // here to take first tap into account.
  455.     game.done = IsRunning;
  456.     GameStartedAt = TimGetSeconds();
  457.     game.seconds = 0;
  458.     if (game.options & CornerNoMine) {
  459.       Show(0,0);
  460.       Show(width-1,height-1);
  461.       Show(width-1,0);
  462.       Show(0,height-1);
  463.     }
  464.   }
  465.  
  466.   switch (ctrl) {
  467.   case false:
  468.     if (IsVisible(x,y)) {
  469.       if (foundMines(x,y) == (game.minefield[x][y] & 0xf)) {
  470.         Neighbours (Show, x, y);
  471.       } else {
  472.         Neighbours (Highlight, x, y);
  473.         isHighlighted = true;
  474.       }
  475.     } else {
  476.       if (game.options & AlternateControl) { // "Dylan style controls"
  477.         if (!(game.minefield[x][y] & MARKED)) {
  478.           game.minefield[x][y] |= MARKED;
  479.           DrawCell(x, y, 0);
  480.           marked++;
  481.         } else {
  482.           game.minefield[x][y] &= ~MARKED;
  483.           marked--;
  484.           Show(x,y);
  485.         }
  486.         DrawMarked();
  487.       } else {
  488.         if (!(game.minefield[x][y] & MARKED)) {
  489.           Show(x,y);
  490.         }
  491.       }
  492.     }
  493.     break;
  494.   case true:
  495.     if (!IsVisible(x,y)) {
  496.       if (!(game.minefield[x][y] & MARKED)) {
  497.         game.minefield[x][y] |= MARKED;
  498.         DrawCell(x, y, 0);
  499.         marked++;
  500.       } else {
  501.         game.minefield[x][y] &= ~MARKED;
  502.         DrawCell(x, y, 0);
  503.         marked--;
  504.       }
  505.       DrawMarked();
  506.     }
  507.     break;
  508.   }
  509.  
  510.   return true;
  511. }
  512.  
  513. /*
  514.  * Dispatch any menu events we have to handle
  515.  */
  516. static Boolean MyHandleMenuEvent(EventPtr e)
  517. {
  518.   Boolean handled;
  519.   
  520.   CALLBACK_PROLOGUE
  521.   handled = false;
  522.   
  523.   MenuEraseStatus (MenuGetActiveMenu());
  524.   switch(e->data.menu.itemID) {
  525.  
  526.   case ID_MenuItem: /* New Game */
  527.     InitFormMine();
  528.     handled = true;
  529.     break;
  530.     
  531.   case ID_MenuItem+10: /* About Pilot Mines */
  532.     FrmPopupForm(ID_FrmAbout);
  533.     handled = true; 
  534.     break;
  535.  
  536.   case ID_MenuItem+11: /* Preferences */
  537.     FrmPopupForm(ID_FrmPreferences);
  538.     handled = true; 
  539.     break;
  540.  
  541.   case ID_MenuItem+12: /* High Scores */
  542.     FrmPopupForm(ID_FrmHighScores);
  543.     handled = true; 
  544.     break;
  545.  
  546.   case ID_MenuItem+13: /* Reset High Scores */
  547.     InitHighScore();
  548.     FrmPopupForm(ID_FrmHighScores);
  549.     handled = true; 
  550.     break;
  551.  
  552.   default:
  553.     break;
  554.   }
  555.  
  556.   CALLBACK_EPILOGUE
  557.   return handled;
  558. }
  559.  
  560. /*
  561.  * dispatch all events we have to handle in the main form (the one
  562.  * showing the mine field)
  563.  */
  564. static Boolean MineFormHandleEvent(EventPtr e)
  565. {
  566.   Boolean handled;
  567.   static SWord saveX, saveY;
  568.   
  569.   CALLBACK_PROLOGUE
  570.   handled = false;
  571.  
  572.   if (game.done == Restart)
  573.     InitFormMine();
  574.  
  575.   if (game.done == IsRunning) 
  576.     DrawTime();
  577.  
  578.   if (game.done == HighScoreWon) {
  579.     if (!FrmAlert(GameOver)) {
  580.       InitFormMine();
  581.     } else {
  582.       game.done = IsFinishedWon;
  583.     }
  584.   }
  585.  
  586.   if (game.done == HighScoreLost) {
  587.     if (!FrmAlert(GameOver+1)) {
  588.       InitFormMine();
  589.     } else {
  590.       game.done = IsFinishedLost;
  591.     }
  592.   }
  593.  
  594.   switch (e->eType) {
  595.  
  596.   case menuEvent:
  597.     handled = MyHandleMenuEvent(e);
  598.     break;
  599.  
  600.   case frmOpenEvent:
  601.     handled = true;
  602.     break;
  603.  
  604.   case keyDownEvent:
  605.     switch(e->data.keyDown.chr) {
  606.     case 'n':
  607.       InitFormMine();
  608.       handled = true;
  609.       break;
  610.  
  611.     case 'h':
  612.       FrmPopupForm(ID_FrmHighScores);
  613.       handled = true;
  614.       break;
  615.  
  616. #ifdef ENGLISH
  617.     case 'p':
  618. #else
  619.     case 'e':
  620. #endif
  621.       FrmPopupForm(ID_FrmPreferences);
  622.       handled = true;
  623.       break;
  624.     }
  625.     break;
  626.  
  627.   case penDownEvent:
  628.     saveX=e->screenX / length_bitmap;
  629.     saveY=e->screenY / length_bitmap - 2;
  630.     handled = HandlePenDownEvent(saveX, saveY, 
  631.               (KeyCurrentState() & (keyBitPageUp | keyBitPageDown))!=0);
  632.  
  633.     switch (game.done) {
  634.     case IsWon:
  635.       game.done = IsFinishedWon;
  636.       if (isHighScore((int)score))
  637.         FrmPopupForm(ID_FrmHighScores);
  638.       else
  639.         if (!FrmAlert(GameOver))
  640.           InitFormMine();
  641.       break;
  642.     case IsLost:
  643.       game.done = IsFinishedLost;
  644.       if (!FrmAlert(GameOver+1))
  645.         InitFormMine();
  646.       break;
  647.     }
  648.  
  649.     break;
  650.  
  651.   case penUpEvent:
  652.     if (isHighlighted) {
  653.       Neighbours (unHighlight, saveX, saveY);
  654.       isHighlighted = false;
  655.     }
  656.     handled = true;
  657.     break;
  658.  
  659.   default:
  660.     break;
  661.   }  
  662.  
  663.   CALLBACK_EPILOGUE
  664.   return handled;
  665. }
  666.  
  667. /**
  668.  * About Form
  669.  */
  670. static Boolean AboutFormHandleEvent(EventPtr e)
  671. {
  672.   Boolean handled;
  673.   
  674.   CALLBACK_PROLOGUE
  675.   handled = false;
  676.  
  677.   switch (e->eType) {
  678.  
  679.   case frmOpenEvent:
  680.     FrmDrawForm(FrmGetActiveForm());
  681.     handled = true; 
  682.     break;
  683.  
  684.   case ctlSelectEvent:
  685.     if (e->data.ctlSelect.controlID == ID_FrmAboutButton) {
  686.       FrmReturnToForm(0);
  687.       handled = true; 
  688.       break;
  689.     }
  690.     break;
  691.  
  692.   default:
  693.     break;
  694.   }
  695.  
  696.   CALLBACK_EPILOGUE
  697.   return handled;
  698. }
  699.  
  700. /**
  701.  * Preferences Form
  702.  */
  703.  
  704. /*
  705.  * preset the radio buttons for selecting the difficulty level and
  706.  * the check box, that controls if all fields are uncovered, if you
  707.  * loose a game
  708.  */
  709. static void InitFormPref()
  710. {
  711.   FormPtr frm = FrmGetActiveForm();
  712.   FrmSetControlGroupSelection(frm, 1, ID_PrefButton + (game.level % 4));
  713.   FrmSetControlGroupSelection(frm, 2, ID_PrefButton + 10 + (game.level / 4));
  714.   FrmSetControlValue(frm, FrmGetObjectIndex(frm, ID_PrefButton + 6), 
  715.                      (game.options & ShowAll));
  716.   FrmSetControlValue(frm, FrmGetObjectIndex(frm, ID_PrefButton + 7),
  717.                      (game.options & AlternateControl));
  718.   FrmSetControlValue(frm, FrmGetObjectIndex(frm, ID_PrefButton + 8),
  719.                      (game.options & FirstNoMine));
  720.   FrmSetControlValue(frm, FrmGetObjectIndex(frm, ID_PrefButton + 9), 
  721.              (game.options & CornerNoMine));
  722. }
  723.  
  724. /*
  725.  * handle any events in the preferences form; i.e. setting game
  726.  * difficulty level and "Uncover all" check box.
  727.  */
  728. static Boolean PrefFormHandleEvent(EventPtr e)
  729. {
  730.   Boolean handled;
  731.   
  732.   CALLBACK_PROLOGUE
  733.   handled = false;
  734.  
  735.   switch (e->eType) {
  736.  
  737.   case frmOpenEvent:
  738.     FrmDrawForm(FrmGetActiveForm());
  739.     handled = true; 
  740.     break;
  741.  
  742.   case ctlSelectEvent:
  743.     switch (e->data.ctlSelect.controlID) {
  744.  
  745.     // look at status of radio buttons and save level information accordingly
  746.     case ID_PrefButton + 4: /* Ok */
  747.       game.level = FrmGetObjectId(FrmGetActiveForm(),
  748.                      FrmGetControlGroupSelection(FrmGetActiveForm(), 1)) 
  749.                      - ID_PrefButton;
  750.       game.level += ( FrmGetObjectId(FrmGetActiveForm(),
  751.                 FrmGetControlGroupSelection(FrmGetActiveForm(), 2)) 
  752.               - ID_PrefButton - 10 ) * 4;
  753.       nummines = levelmines[game.level];
  754.  
  755.       FrmReturnToForm(0);
  756.       game.done = Restart;
  757.       handled = true; 
  758.       break;
  759.  
  760.     // don't do anything, simply return to main form
  761.     case ID_PrefButton + 5: /* Cancel */
  762.       FrmReturnToForm(0);
  763.       handled = true; 
  764.       break;
  765.  
  766.     // toggle "uncover all" status information
  767.     case ID_PrefButton + 6: /* Show all */
  768.       game.options ^= ShowAll;
  769.       handled = true;
  770.       break;
  771.  
  772.     // select alternate "Dylan style controls" (Dylan Ginsburg)
  773.     case ID_PrefButton + 7: 
  774.       game.options ^= AlternateControl;
  775.       handled = true;
  776.       break;
  777.  
  778.     // select alternate "First field is never a mine" (Lucas Bremgartner)
  779.     case ID_PrefButton + 8:
  780.       game.options ^= FirstNoMine;
  781.       handled = true;
  782.       break;
  783.  
  784.     // select alternate "No mines in corners" (Laurent Thaler)
  785.     case ID_PrefButton + 9:
  786.       game.options ^= CornerNoMine;
  787.       handled = true;
  788.       break;
  789.     }
  790.  
  791.     break;
  792.  
  793.   default:
  794.     break;
  795.   }
  796.  
  797.   CALLBACK_EPILOGUE
  798.   return handled;
  799. }
  800.  
  801.  
  802. /**
  803.  * dispatch all previously unhandled events, and set event handling 
  804.  * routines for the different forms
  805.  */
  806. static Boolean ApplicationHandleEvent(EventPtr e)
  807. {
  808.   Boolean handled;
  809.  
  810.   CALLBACK_PROLOGUE
  811.   handled = false;
  812.  
  813.   if (e->eType == frmLoadEvent) {
  814.     Word frmId = e->data.frmLoad.formID;
  815.     FormPtr frm = FrmInitForm(frmId);
  816.     
  817.     FrmSetActiveForm(frm);
  818.     
  819.     switch (frmId) {
  820.     case ID_FrmMine:
  821.       if (game.done == Restart)
  822.         InitFormMine();
  823.       else
  824.         ShowFormMine();
  825.       FrmSetEventHandler(frm, MineFormHandleEvent);
  826.       handled = true;
  827.       break;
  828.  
  829.     case ID_FrmAbout:
  830.       FrmSetEventHandler(frm, AboutFormHandleEvent);
  831.       handled = true;
  832.       break;
  833.  
  834.     case ID_FrmPreferences:
  835.       InitFormPref();
  836.       FrmSetEventHandler(frm, PrefFormHandleEvent);
  837.       handled = true;
  838.       break;
  839.  
  840.     case ID_FrmHighScores:
  841.       FrmSetEventHandler(frm, HighscoresFormHandleEvent);
  842.       handled = true;
  843.       break;
  844.     }
  845.  
  846.   }
  847.  
  848.   CALLBACK_EPILOGUE
  849.   return handled;
  850. }
  851.  
  852.  
  853. /**
  854.  * Database (here: game status information) handling routines.
  855.  */
  856. static Err OpenDatabase(void)
  857. {
  858.   UInt          index = 0;
  859.   VoidHand      RecHandle;
  860.   VoidPtr       RecPointer;
  861.   Err           err;
  862.  
  863.   // Create database, if it doesn't exist, and save default game status.
  864.   if (!(pmDB = DmOpenDatabaseByTypeCreator('Data', 'tpPM', dmModeReadWrite))) {
  865.     if ((err = DmCreateDatabase(0, "PilotMinesDB", 'tpPM', 'Data', false)))
  866.       return err;
  867.     pmDB = DmOpenDatabaseByTypeCreator('Data', 'tpPM', dmModeReadWrite);
  868.  
  869.     RecHandle = DmNewRecord(pmDB, &index, sizeof(game));
  870.     DmWrite(MemHandleLock(RecHandle), 0, &game, sizeof(game));
  871.     MemHandleUnlock(RecHandle);
  872.     DmReleaseRecord(pmDB, index, true);
  873.   }
  874.  
  875.   // Load a saved game status.
  876.   RecHandle = DmGetRecord(pmDB, 0);
  877.   RecPointer = MemHandleLock(RecHandle);
  878.   MemMove(&game, RecPointer, sizeof(game));
  879.  
  880.   // convert v1 database to v2 format
  881.   if (game.version == 1) {
  882.     int i, j;
  883.     game.version = 2;
  884.     game.done = Restart;
  885.     for (i=0; i<4; i++) {
  886.       for (j=0; j<5; j++) {
  887.     MemMove(&game.hscore[i][j], RecPointer+230+(i*5+j)*18, 18);
  888.     MemSet(&game.hscore[i+4][j], 18, 0);
  889.     MemSet(&game.hscore[i+8][j], 18, 0);
  890.       }
  891.     }
  892.     game.seconds = 0;
  893.   }
  894.  
  895.   MemHandleUnlock(RecHandle);
  896.   DmReleaseRecord(pmDB, 0, true);
  897.  
  898.   return 0;
  899. }
  900.  
  901. /*
  902.  * Save game status information.
  903.  */
  904. static void SaveStatus()
  905. {
  906.   VoidPtr p = MemHandleLock(DmResizeRecord(pmDB, 0, sizeof(game)));
  907.   game.seconds = TimGetSeconds() - GameStartedAt;
  908.   DmWrite(p, 0, &game, sizeof(game));
  909.   MemPtrUnlock(p);
  910.   DmReleaseRecord(pmDB, 0, true);
  911. }
  912.  
  913. /*
  914.  * Main program.
  915.  */
  916. DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
  917. {
  918.   if (cmd==sysAppLaunchCmdNormalLaunch) {
  919.     EventType e;
  920.     short err;
  921.  
  922.     // initialize game status information
  923.     game.level = 2;
  924.     game.version = 2;
  925.     game.options = ShowAll | FirstNoMine | AlternateControl;
  926.     game.done = Restart;
  927.     game.seconds = 0;
  928.     InitHighScore();
  929.  
  930.     // load a saved game
  931.     if ((err = OpenDatabase()))
  932.       return err;
  933.  
  934.     // restore game status from loaded game, if necessary
  935.     nummines = levelmines[game.level];
  936.     InitBoard();
  937.  
  938.     // is needed on PalmOS 3.3, but why...
  939.     WinSetActiveWindow(WinGetDisplayWindow());
  940.     FrmGotoForm(ID_FrmMine);
  941.  
  942.     do {
  943.       EvtGetEvent(&e,35);
  944.  
  945.       // don't make noise when pressing PgUp/PgDn
  946.       if (e.eType == keyDownEvent && 
  947.          (e.data.keyDown.modifiers & commandKeyMask) &&
  948.          (e.data.keyDown.chr == pageUpChr || e.data.keyDown.chr == pageDownChr))
  949.          continue;
  950.  
  951.       if (SysHandleEvent(&e)) {
  952.         continue;
  953.       }
  954.  
  955.       if (MenuHandleEvent(NULL, &e, &err)) {
  956.         continue;
  957.       }
  958.   
  959.       if (ApplicationHandleEvent(&e)) {
  960.         continue;
  961.       }
  962.  
  963.       FrmDispatchEvent( &e );
  964.       
  965.     } while (e.eType != appStopEvent);
  966.  
  967.     FrmCloseAllForms();
  968.     SaveStatus();
  969.     DmCloseDatabase(pmDB);
  970.     WinDeleteWindow(bitmaps, false);
  971.   }
  972.  
  973.   return 0;
  974. }
  975.