home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume4 / dragon / patch1 / board.c next >
Encoding:
C/C++ Source or Header  |  1989-05-23  |  26.8 KB  |  1,017 lines

  1. /******************************************************************************
  2. * Dragon - a version of Mah-Jongg for X Windows
  3. *
  4. * Author: Gary E. Barnes    March 1989
  5. *
  6. * board.c - Deals with the Mah-Jongg board.  Setup and execution.
  7. ******************************************************************************/
  8.  
  9. #define _BOARD_C_
  10.  
  11. #include "main.h"
  12. #include "board.h"
  13.  
  14. extern long random();
  15.  
  16.  
  17. void Write_Game( file )
  18.      FILE    *file;
  19. /******************************************************************************
  20. *   file    - Specifies a file open for write
  21. *
  22. * Called to write out the current game context for later rereading.
  23. ******************************************************************************/
  24. {
  25.  
  26.     (void)fwrite( (char*)&Score, 1, sizeof(Score), file );
  27.     (void)fwrite( (char*)&Board_Tiles[0][0], 1, sizeof(Board_Tiles), file );
  28.  
  29. } /* Write_Game */
  30.  
  31.  
  32. void Read_Game( file )
  33.      FILE    *file;
  34. /******************************************************************************
  35. *   file    - Specifies a file open for reading
  36. *
  37. * Called to read in a new current game context.
  38. ******************************************************************************/
  39. {
  40.  
  41.     Click1 = Board_Position_NULL;
  42.     Click2 = Board_Position_NULL;
  43.     (void)fread( (char*)&Score, 1, sizeof(Score), file );
  44.     (void)fread( (char*)&Board_Tiles[0][0], 1, sizeof(Board_Tiles), file );
  45.  
  46. } /* Read_Game */
  47.  
  48.  
  49. static int Pick_Tile( Avail )
  50.      char    *Avail;
  51. /******************************************************************************
  52. *   Avail    - Specifies an [NTILES] array of available tiles.  Unavailable
  53. *          slots contain NO_TILE.
  54. *
  55. * Called to pick a random tile from the Available tiles.
  56. ******************************************************************************/
  57. {
  58.     register char    *t;
  59.     register int    k;
  60.  
  61. /*--Pick a random starting place. */
  62.  
  63.     k = (int)random() % NTILES;
  64.     t = &Avail[k];
  65.  
  66. /*--Search until we find a non-NO_TILE slot. */
  67.  
  68.     while (*t == NO_TILE) {
  69.     ++t;
  70.     if (++k == NTILES) {
  71.         t = &Avail[0];
  72.         k = 0;
  73.     }
  74.     }
  75.  
  76. /*--Return the tile we found and zap the slot. */
  77.  
  78.     k = *t;
  79.     *t = NO_TILE;
  80.     return k;
  81.  
  82. } /* Pick_Tile */
  83.  
  84.  
  85. void Set_Tile_Controls()
  86. /******************************************************************************
  87. * Called whenever the board has been reset or resized.  We recalculate all of
  88. * the drawing controls for the tiles.
  89. ******************************************************************************/
  90. {
  91.     register Board_Position    bp;
  92.     int                row, col;
  93.  
  94. /*--Now set up the control information for all of the tiles.  The special
  95.  *  tiles are easy. */
  96.  
  97.     DEBUG_CALL(Set_Tile_Controls);
  98.     if (Board_Tiles[SPEC4].level > 0) {
  99.     Board_Tiles[SPEC4].x         = Board_Tile0_X + 6 * (Tile_Width + 1)
  100.                         + (Tile_Width + 1) / 2 + 4 * Side_X;
  101.     Board_Tiles[SPEC4].y         = Board_Tile0_Y + 3 * (Tile_Height + 1)
  102.                       + (Tile_Height + 1) / 2 - 3 * Side_Y;
  103.     }
  104.  
  105.     if (Board_Tiles[SPEC3].level > 0) {
  106.     Board_Tiles[SPEC3].x         = Board_Tile0_X + 0 * (Tile_Width + 1);
  107.     Board_Tiles[SPEC3].y         = Board_Tile0_Y + 3 * (Tile_Height + 1)
  108.                       + (Tile_Height + 1) / 2;
  109.     }
  110.  
  111.     if (Board_Tiles[SPEC2].level > 0) {
  112.     Board_Tiles[SPEC2].x         = Board_Tile0_X + 13 * (Tile_Width+1);
  113.     Board_Tiles[SPEC2].y         = Board_Tile0_Y +  3 * (Tile_Height+1)
  114.                       + (Tile_Height + 1) / 2;
  115.     }
  116.  
  117.     if (Board_Tiles[SPEC1].level > 0) {
  118.     Board_Tiles[SPEC1].x         = Board_Tile0_X + 14 * (Tile_Width+1);
  119.     Board_Tiles[SPEC1].y         = Board_Tile0_Y +  3 * (Tile_Height+1)
  120.                             + (Tile_Height + 1) / 2;
  121.     }
  122.  
  123. /*--Do the more regular tiles. */
  124.  
  125.     for (row = 0; row <= 7; ++row) {
  126.     for (col = 12; col >= 1; --col) {
  127.         bp = &Board_Tiles[row][col];
  128.  
  129. /*--Skip any tiles that don't exist. */
  130.  
  131.         if (bp->level == 0) { continue; }
  132.  
  133. /*--Set up the face x/y coordinates. */
  134.  
  135.         bp->x = Board_Tile0_X + col * (Tile_Width + 1);
  136.         bp->y = Board_Tile0_Y + row * (Tile_Height + 1);
  137.  
  138.     }
  139.     }
  140.     DEBUG_RETURN(Set_Tile_Controls);
  141.  
  142. } /* Set_Tile_Controls */
  143.  
  144.  
  145. static void Pick1( bp, Avail )
  146.      register Board_Position     bp;
  147.      char            *Avail;
  148. {
  149.     bp->tiles[0] = Pick_Tile( Avail );
  150.     bp->level = 1;
  151. }
  152.  
  153. static void Pick2( bp, Avail )
  154.      register Board_Position     bp;
  155.      char            *Avail;
  156. {
  157.     bp->tiles[0] = Pick_Tile( Avail );
  158.     bp->tiles[1] = Pick_Tile( Avail );
  159.     bp->level = 2;
  160. }
  161.  
  162. static void Pick3( bp, Avail )
  163.      register Board_Position     bp;
  164.      char            *Avail;
  165. {
  166.     bp->tiles[0] = Pick_Tile( Avail );
  167.     bp->tiles[1] = Pick_Tile( Avail );
  168.     bp->tiles[2] = Pick_Tile( Avail );
  169.     bp->level = 3;
  170. }
  171.  
  172. static void Pick4( bp, Avail )
  173.      register Board_Position     bp;
  174.      char            *Avail;
  175. {
  176.     bp->tiles[0] = Pick_Tile( Avail );
  177.     bp->tiles[1] = Pick_Tile( Avail );
  178.     bp->tiles[2] = Pick_Tile( Avail );
  179.     bp->tiles[3] = Pick_Tile( Avail );
  180.     bp->level = 4;
  181. }
  182.  
  183.  
  184. void Setup_New_Game()
  185. /******************************************************************************
  186. * Called to generate an all-new game.
  187. ******************************************************************************/
  188. {
  189.     register Board_Position    bp;
  190.     char            Avail[NTILES];
  191.     int                row, col, i;
  192.  
  193. /*--Clear the board. */
  194.  
  195.     DEBUG_CALL(Setup_New_Game);
  196.     bp = &Board_Tiles[0][0];
  197.     for (row = 0; row < NROWS; ++row) {
  198.     for (col = 0; col < NCOLS; ++col) {
  199.         bp->tiles[0] = NO_TILE;
  200.         bp->tiles[1] = NO_TILE;
  201.         bp->tiles[2] = NO_TILE;
  202.         bp->tiles[3] = NO_TILE;
  203.         bp->level = 0;
  204.     }
  205.     }
  206.  
  207. /*--Mark all tiles as available. */
  208.  
  209.     i = 0;
  210.     for (row = 0; row < 4; ++row) {
  211.     Avail[i++] = row + 1;
  212.     Avail[i++] = row + 5;
  213.     for (col = 8; col < NFACES; ++col) {
  214.         Avail[i++] = 1 + col % NFACES;
  215.     }
  216.     }
  217.     if (i != NTILES) { (void)fprintf( stderr, "NTILES gak!\n" ); }
  218.  
  219. /*--Fill in the "odd" tile slots. */
  220.  
  221.     Pick1( &Board_Tiles[SPEC1], Avail );
  222.     Pick1( &Board_Tiles[SPEC2], Avail );
  223.     Pick1( &Board_Tiles[SPEC3], Avail );
  224.     Pick1( &Board_Tiles[SPEC4], Avail );
  225.  
  226.     for (col = 1; col <= 12; ++col) {
  227.     Pick1( &Board_Tiles[0][col], Avail );
  228.     Pick1( &Board_Tiles[7][col], Avail );
  229.     }
  230.     for (row = 1; row <= 6; ++row) {
  231.     Pick1( &Board_Tiles[row][ 3], Avail );
  232.     Pick1( &Board_Tiles[row][10], Avail );
  233.     }
  234.     for (row = 2; row <= 5; ++row) {
  235.     Pick1( &Board_Tiles[row][ 2], Avail );
  236.     Pick1( &Board_Tiles[row][11], Avail );
  237.     }
  238.     for (row = 3; row <= 4; ++row) {
  239.     Pick1( &Board_Tiles[row][ 1], Avail );
  240.     Pick1( &Board_Tiles[row][12], Avail );
  241.     }
  242.  
  243. /*--Now do the next square at level 2. */
  244.  
  245.     for (col = 4; col <= 9; ++col) {
  246.     Pick2( &Board_Tiles[1][col], Avail );
  247.     Pick2( &Board_Tiles[6][col], Avail );
  248.     }
  249.     for (row = 2; row <= 5; ++row) {
  250.     Pick2( &Board_Tiles[row][4], Avail );
  251.     Pick2( &Board_Tiles[row][9], Avail );
  252.     }
  253.  
  254. /*--Now do the next square at level 3. */
  255.  
  256.     for (col = 5; col <= 8; ++col) {
  257.     Pick3( &Board_Tiles[2][col], Avail );
  258.     Pick3( &Board_Tiles[5][col], Avail );
  259.     }
  260.     for (row = 3; row <= 4; ++row) {
  261.     Pick3( &Board_Tiles[row][5], Avail );
  262.     Pick3( &Board_Tiles[row][8], Avail );
  263.     }
  264.  
  265. /*--Now do the final square at level 4. */
  266.  
  267.     for (row = 3; row <= 4; ++row) {
  268.     for (col = 6; col <= 7; ++col) {
  269.         Pick4( &Board_Tiles[row][col], Avail );
  270.     }
  271.     }
  272.  
  273. /*--Now set up the control information for all of the tiles. */
  274.  
  275.     Set_Tile_Controls();
  276.     Score = NTILES;
  277.     DEBUG_RETURN(Setup_New_Game);
  278.  
  279. } /* Setup_New_Game */
  280.  
  281.  
  282. /*ARGSUSED*/
  283. void Restart_Game( w, event, params, num_params )
  284.      Widget    w;
  285.      XEvent    *event;
  286.      String    *params;
  287.      Cardinal    *num_params;
  288. /******************************************************************************
  289. * Called when the RESTART button is pressed.  Restart the game.
  290. ******************************************************************************/
  291. {
  292.     int                row;
  293.     int                col;
  294.     register Board_Position    bp;
  295.  
  296. /*--Reset levels and remove hilites. */
  297.  
  298.     DEBUG_CALL(Restart_Game);
  299.     Click1 = Board_Position_NULL;
  300.     Click2 = Board_Position_NULL;
  301.     Score = NTILES;
  302.     bp = &Board_Tiles[0][0];
  303.     for (row = 0; row < NROWS; ++row) {
  304.     for (col = 0; col < NCOLS; ++bp,++col) {
  305.         if      (bp->tiles[3] != NO_TILE) { bp->level = 4; }
  306.         else if (bp->tiles[2] != NO_TILE) { bp->level = 3; }
  307.         else if (bp->tiles[1] != NO_TILE) { bp->level = 2; }
  308.         else if (bp->tiles[0] != NO_TILE) { bp->level = 1; }
  309.         else { bp->level = 0; }
  310.     }
  311.     }
  312.  
  313. /*--Finish setting up and then redraw everything. */
  314.  
  315.     Set_Tile_Controls();
  316.     XClearArea( XtDisplay(Board), XtWindow(Board), 0, 0, 0, 0, TRUE );
  317.     DEBUG_RETURN(Restart_Game);
  318.  
  319. } /* Restart_Game */
  320.  
  321.  
  322. static void Set_Tile_Draw( row, col )
  323.      int    row;
  324.      int    col;
  325. /******************************************************************************
  326. *   row    - Specifies the row of the tile
  327. *   col - Specifies the column of the tile
  328. *
  329. * Called to set the "draw" flag on a tile.  We also recursively set the
  330. * draw flag on anyone that needs to be redrawn because we are being redrawn.
  331. ******************************************************************************/
  332. {
  333.     register Board_Position    bp = &Board_Tiles[row][col];
  334.  
  335. /*--If we don't exist or if we are already being redrawn then stop. */
  336.  
  337.     DEBUG_CALL(Set_Tile_Draw);
  338.     if (bp->level == 0 || bp->draw) {
  339.     return;
  340.     }
  341.  
  342. /*--Redraw us.  Redraw anyone to our left that has a height greater than ours
  343.  *  because their shadow/tile-face overlaps us. */
  344.  
  345.     bp->draw = TRUE;
  346.     if (col > 0 &&
  347.     Board_Tiles[row][col-1].level > bp->level) {
  348.     Set_Tile_Draw( row, col-1 );
  349.     }
  350.  
  351. /*--Redraw anyone below us that has a level greater than ours because their
  352.  *  shadow/tile-face overlaps us. */
  353.  
  354.     if (row < 7 &&
  355.     Board_Tiles[row+1][col].level > bp->level) {
  356.     Set_Tile_Draw( row+1, col );
  357.     }
  358.  
  359. /*--Redraw anyone below-to-the-left of us. */
  360.  
  361.     if (row < 7 &&
  362.     col > 0 &&
  363.     Board_Tiles[row+1][col-1].level > 0) {
  364.     Set_Tile_Draw( row+1, col-1 );
  365.     }
  366.  
  367. /*--Redraw anyone above-to-the-left of us that has a level greater than ours
  368.  *  because their tile-face overlaps our tile-edge. */
  369.  
  370.     if (row > 0 && col > 0 &&
  371.     Board_Tiles[row-1][col-1].level != bp->level) {
  372.     Set_Tile_Draw( row-1, col-1 );
  373.     }
  374.  
  375. /*--If we are certain specific tiles then we may need to set specific other
  376.  *  tiles. */
  377.  
  378.     if (row == 3 || row == 4) {
  379.     if (col == 6 || col == 7) {
  380.         Set_Tile_Draw( SPEC4row, SPEC4col );
  381.     } else if (col == 1) {
  382.         Set_Tile_Draw( SPEC3row, SPEC3col );
  383.     }
  384.     }
  385.     DEBUG_RETURN(Set_Tile_Draw);
  386.  
  387. } /* Set_Tile_Draw */
  388.  
  389.  
  390. static void Remove_Tile( bp, row, col )
  391.      register Board_Position    bp;
  392.      int            row;
  393.      int            col;
  394. /******************************************************************************
  395. * Called to remove the top tile of the indicated Board_Position.
  396. ******************************************************************************/
  397. {
  398.  
  399. /*--If the tile just went away then clear the area and allow the window
  400.  *  background to shine through. */
  401.  
  402.     DEBUG_CALL(Remove_Tiles);
  403.     if (bp->level == 1) {
  404.     if (Tile_Control & SHADOW) {
  405.         XClearArea( XtDisplay(Board), XtWindow(Board),
  406.                 bp->x, bp->y - Side_Y - Shadow_Y,
  407.                 Tile_Width + Side_X + 2 + Shadow_X,
  408.                 Tile_Height + Side_Y + 2 + Shadow_Y,
  409.                 FALSE );
  410.     } else {
  411.         XClearArea( XtDisplay(Board), XtWindow(Board),
  412.                 bp->x, bp->y - Side_Y,
  413.                 Tile_Width + Side_X + 2,
  414.                 Tile_Height + Side_Y + 2,
  415.                 FALSE );
  416.     }
  417.     } else {
  418.     int    sidex = Side_X * bp->level;
  419.     int    sidey = Side_Y * bp->level;
  420.     if (Tile_Control & SHADOW) {
  421.         XClearArea( XtDisplay(Board), XtWindow(Board),
  422.                 bp->x + sidex, bp->y - sidey - Shadow_Y,
  423.                 Tile_Width + 2 + Shadow_X,
  424.                 Tile_Height+ 2 + Shadow_Y,
  425.                 FALSE );
  426.     } else {
  427.         XClearArea( XtDisplay(Board), XtWindow(Board),
  428.                 bp->x + sidex, bp->y - sidey,
  429.                 Tile_Width + 2,
  430.                 Tile_Height+ 2,
  431.                 FALSE );
  432.     }
  433.     Set_Tile_Draw( row, col );
  434.     }
  435.     --bp->level;
  436.  
  437. /*--Schedule the surrounding tiles for redrawing. */
  438.  
  439.     if (col == SPEC1col) {
  440.     if (row == SPEC4row) {
  441.         Set_Tile_Draw( 3, 6 );
  442.         Set_Tile_Draw( 3, 7 );
  443.         Set_Tile_Draw( 4, 6 );
  444.         Set_Tile_Draw( 4, 7 );
  445.         return;
  446.     } else if (row == SPEC3row) {
  447.         Set_Tile_Draw( 3, 1 );
  448.         Set_Tile_Draw( 4, 1 );
  449.         return;
  450.     } else if (row == SPEC2row) {
  451.         Set_Tile_Draw( SPEC1row, SPEC1col );
  452.         Set_Tile_Draw( 3, 12 );
  453.         Set_Tile_Draw( 4, 12 );
  454.         return;
  455.     } else {
  456.         Set_Tile_Draw( SPEC2row, SPEC2col );
  457.         Set_Tile_Draw( 3, 12 );
  458.         Set_Tile_Draw( 4, 12 );
  459.         return;
  460.     }
  461.     }
  462.     if (col == 1 && (row == 3 || row == 4)) {
  463.     Set_Tile_Draw( SPEC3row, SPEC3col );
  464.     }
  465.     if (col == 12 && (row == 3 || row == 4)) {
  466.     Set_Tile_Draw( SPEC2row, SPEC2col );
  467.     }
  468.     if (row > 0) {
  469.     Set_Tile_Draw( row - 1, col + 1 );
  470.     Set_Tile_Draw( row - 1, col     );
  471.     if (col > 0 &&
  472.         Board_Tiles[row-1][col].level == 0) {
  473.         Set_Tile_Draw( row - 1, col - 1 );
  474.     }
  475.     }
  476.     Set_Tile_Draw( row, col+1 );
  477.     if (col > 0) {
  478.     Set_Tile_Draw( row,     col - 1 );
  479.     }
  480.     if (row < 7) {
  481.     Set_Tile_Draw( row + 1, col     );
  482.     if (col > 0) {
  483.         Set_Tile_Draw( row + 1, col - 1 );
  484.     }
  485.     }
  486.     DEBUG_RETURN(Remove_Tile);
  487.  
  488. } /* Remove_Tile */
  489.  
  490.  
  491. static void Touch_Tile( bp, row, col, event )
  492.      register Board_Position     bp;
  493.      register XButtonEvent    *event;
  494. /******************************************************************************
  495. * Called when we click on a specific tile.  We decide what to do.  For a
  496. * single click we hilite the tile unless we already have two tiles hilited.
  497. * For a "double" click with two tiles hilited we will remove both of the
  498. * tiles.
  499. ******************************************************************************/
  500. {
  501.  
  502. /*--If there is no Click1 then this guy becomes it. */
  503.  
  504.     DEBUG_CALL(Touch_Tile);
  505.     if (Click1 == Board_Position_NULL) {
  506.     Click1 = bp;
  507.     Click1_Row = row;
  508.     Click1_Col = col;
  509.     Hilite_Tile( row, col );
  510.     DEBUG_RETURN(Touch_Tile);
  511.     return;
  512.     }
  513.  
  514. /*--If there is no Click2 then this guy becomes it unless he is already Click1.
  515.  */
  516.  
  517.     if (Click1 != bp) {
  518.     if (Click2_Row == row &&
  519.         Click2_Col == col &&
  520.         Click2_Time + Dragon_Resources.Double_Click_Time >= event->time) {
  521.         Click2 = bp;
  522.     }
  523.     if( Click2 == Board_Position_NULL) {
  524.         Click2 = bp;
  525.         Click2_Row = row;
  526.         Click2_Col = col;
  527.         Click2_Time = event->time;
  528.         Hilite_Tile( row, col );
  529.         DEBUG_RETURN(Touch_Tile);
  530.         return;
  531.     }
  532.  
  533. /*--If this guy is not one Click1 and not Click2 then we have an error. */
  534.  
  535.     if (Click2 != bp) {
  536.         XBell( XtDisplay(Board), 0 );
  537.         DEBUG_RETURN(Touch_Tile);
  538.         return;
  539.     }
  540.     }
  541.  
  542. /*--If he double-clicks then remove both tiles. */
  543.  
  544.     if (Click2 != Board_Position_NULL &&
  545.     Click2_Time + Dragon_Resources.Double_Click_Time >= event->time) {
  546.     One_Button_Hint = FALSE;
  547.     Remove_Tile( Click1, Click1_Row, Click1_Col );
  548.     Click1 = Board_Position_NULL;
  549.     Remove_Tile( Click2, Click2_Row, Click2_Col );
  550.     Click2 = Board_Position_NULL;
  551.     Score -= 2;
  552.     Draw_All_Tiles();
  553.     DEBUG_RETURN(Touch_Tile);
  554.     return;
  555.     }
  556.  
  557. /*--2nd click on any tile means turn-it-off. */
  558.  
  559.     if (Click1 == bp) {
  560.     int    s;
  561.     Hilite_Tile( Click1_Row, Click1_Col );
  562.     Click1 = Click2;
  563.     s = Click1_Row;
  564.     Click1_Row = Click2_Row;
  565.     Click2_Row = s;
  566.     s = Click1_Col;
  567.     Click1_Col = Click2_Col;
  568.     Click2_Col = s;;
  569.     Click2 = Board_Position_NULL;
  570.     } else {
  571.     Click2 = Board_Position_NULL;
  572.     Hilite_Tile( Click2_Row, Click2_Col );
  573.     }
  574.     Click2_Time = event->time;
  575.     DEBUG_RETURN(Touch_Tile);
  576.  
  577. } /* Touch_Tile */
  578.  
  579.  
  580. /*ARGSUSED*/
  581. void Tile_Remove( w, event, params, num_params )
  582.      Widget        w;
  583.      XButtonEvent    *event;
  584.      String        *params;
  585.      Cardinal        *num_params;
  586. /******************************************************************************
  587. * Called when the remove-selected-tile-pair mouse button is pressed.
  588. ******************************************************************************/
  589. {
  590.  
  591.     DEBUG_CALL(Tile_Remove);
  592.     if (Click1 != Board_Position_NULL &&
  593.     Click2 != Board_Position_NULL) {
  594.     Click2_Time = event->time;
  595.     Touch_Tile( Click2, Click2_Row, Click2_Col, event );
  596.     }
  597.     DEBUG_RETURN(Tile_Remove);
  598.  
  599. } /* Tile_Remove */
  600.  
  601.  
  602. static Boolean Touch( bp, event )
  603.      register Board_Position     bp;
  604.      register XButtonEvent    *event;
  605. /******************************************************************************
  606. * Return TRUE if this XButtonEvent touched this Board_Position.
  607. ******************************************************************************/
  608. {
  609.     int        face_x = bp->x + bp->level * Side_X;
  610.     int        face_y = bp->y - bp->level * Side_Y;
  611.  
  612. /*--Does this tile exist? */
  613.  
  614.     DEBUG_CALL(Touch);
  615.     if (bp->level == 0) {
  616.     DEBUG_RETURN(Touch);
  617.     return FALSE;
  618.     }
  619.  
  620. /*--Did we touch the face? */
  621.  
  622.     if (event->x >= face_x && event->x <= face_x + Tile_Width + 1 &&
  623.     event->y >= face_y && event->y <= face_y + Tile_Height + 1) {
  624.     DEBUG_RETURN(Touch);
  625.     return TRUE;
  626.     }
  627.  
  628. /*--Did we touch the side? */
  629.  
  630.     if (event->x >= bp->x && event->x <= bp->x + Tile_Width + 1 &&
  631.     event->y >= bp->y && event->y <= bp->y + Tile_Height + 1) {
  632.     DEBUG_RETURN(Touch);
  633.     return TRUE;
  634.     }
  635.  
  636. /*--Guess not. */
  637.  
  638.     DEBUG_RETURN(Touch);
  639.     return FALSE;
  640.  
  641. } /* Touch */
  642.  
  643.  
  644. /*ARGSUSED*/
  645. void Tile_Press( w, event, params, num_params )
  646.           Widget         w;
  647.      register XButtonEvent    *event;
  648.           String        *params;
  649.           Cardinal        *num_params;
  650. /******************************************************************************
  651. * Called when the Board receives a BtnDown event.
  652. ******************************************************************************/
  653. {
  654.     register Board_Position    bp;
  655.     int        x;
  656.     int        y;
  657.     int        row;
  658.     int        col;
  659.  
  660. /*--Figure out a rough row/col coordinate for the click. */
  661.  
  662.     DEBUG_CALL(Tile_Press);
  663.     y = event->y - Board_Tile0_Y;
  664.     if (y < 0) { return; }
  665.     row = y / (Tile_Height + 1);
  666.     if (row > 7) { return; }
  667.     x = event->x - Board_Tile0_X;
  668.     if (x < 0) { return; }
  669.     col = x / (Tile_Width + 1);
  670.     if (col < 0 || row > 14) { goto Touched; }
  671.  
  672. /*--See if we are a special tile. */
  673.  
  674.     if (col == 0) {
  675.     if (Touch( bp = &Board_Tiles[SPEC3], event )) {
  676.         Touch_Tile( bp, SPEC3row, SPEC3col, event );
  677.         goto Touched;
  678.     }
  679.     goto Touched;
  680.     } else if (col == 13) {
  681.     if (Touch( bp = &Board_Tiles[SPEC2], event )) {
  682.         Touch_Tile( bp, SPEC2row, SPEC2col, event );
  683.         goto Touched;
  684.     }
  685.     if (Touch( bp = &Board_Tiles[4][12], event )) {
  686.         Touch_Tile( bp, 4, 12, event );
  687.         goto Touched;
  688.     }
  689.     if (Touch( bp = &Board_Tiles[3][12], event )) {
  690.         Touch_Tile( bp, 3, 12, event );
  691.         goto Touched;
  692.     }
  693.     goto Touched;
  694.     } else if (col == SPEC1col) {
  695.     if (Touch( bp = &Board_Tiles[SPEC1], event )) {
  696.         Touch_Tile( bp, SPEC1row, SPEC1col, event );
  697.         goto Touched;
  698.     }
  699.     if (Touch( bp = &Board_Tiles[SPEC2], event )) {
  700.         Touch_Tile( bp, SPEC2row, SPEC2col, event );
  701.         goto Touched;
  702.     }
  703.     goto Touched;
  704.     } else if ((row == 3 || row == 4) && (col == 6 || col == 7)) {
  705.     if (Touch( bp = &Board_Tiles[SPEC4], event )) {
  706.         Touch_Tile( bp, SPEC4row, SPEC4col, event );
  707.         goto Touched;
  708.     }
  709.     }
  710.  
  711. /*--See if the x/y falls exactly into somebody else's tile face. */
  712.  
  713.     if (col > 0 && row < 7) {
  714.     if (Touch( bp = &Board_Tiles[row+1][col-1], event )) {
  715.         Touch_Tile( bp, row+1, col-1, event );
  716.         goto Touched;
  717.     }
  718.     }
  719.     if (row < 7) {
  720.     if (Touch( bp = &Board_Tiles[row+1][col], event )) {
  721.         Touch_Tile( bp, row+1, col, event );
  722.         goto Touched;
  723.     }
  724.     }
  725.     if (col > 0) {
  726.     if (Touch( bp = &Board_Tiles[row][col-1], event )) {
  727.         Touch_Tile( bp, row, col-1, event );
  728.         goto Touched;
  729.     }
  730.     }
  731.  
  732. /*--We don't have a touch on a neighbor so it must be us. */
  733.  
  734.     if (Touch( bp = &Board_Tiles[row][col], event )) {
  735.     Touch_Tile( bp, row, col, event );
  736.     goto Touched;
  737.     }
  738.  
  739.   Touched :
  740.     DEBUG_RETURN(Tile_Press);
  741.  
  742. } /* Tile_Press */
  743.  
  744.  
  745. static Boolean Tile_Not_Free( row, col )
  746.      int    row;
  747.      int    col;
  748. /******************************************************************************
  749. * Returns TRUE if the tile has neither a left nor a right side free.
  750. ******************************************************************************/
  751. {
  752.  
  753. /*--The 4 in the center can be covered by SPEC4. */
  754.  
  755.     if (row == 3 || row == 4) {
  756.     if ((col == 6 || col == 7) &&
  757.         Board_Tiles[SPEC4].level > 0) { return TRUE; }
  758.     else if (col == 1 &&
  759.          Board_Tiles[SPEC3].level > 0 &&
  760.          Board_Tiles[row][col+1].level > 0) { return TRUE; }
  761.     else if (col == 12 &&
  762.          Board_Tiles[SPEC2].level > 0 &&
  763.          Board_Tiles[row][col-1].level > 0) { return TRUE; }
  764.     }
  765.     
  766. /*--If a tile has a neighbor then he isn't free. */
  767.  
  768.     if (Board_Tiles[row][col-1].level >= Board_Tiles[row][col].level &&
  769.     Board_Tiles[row][col+1].level >= Board_Tiles[row][col].level) {
  770.     return TRUE;
  771.     }
  772.  
  773. /*--Check the special tiles. */
  774.  
  775.     if (col == SPEC1col) {
  776.  
  777. /*--Tiles 1, 3, and 4 are always free. */
  778.  
  779.     if (row != SPEC2row) { return FALSE; }
  780.  
  781. /*--Tile 2 is free if tile 1 is gone or if its two normal neighbors are gone.*/
  782.  
  783.     if (Board_Tiles[SPEC1].level > 0 &&
  784.         (Board_Tiles[3][12].level > 0 ||
  785.          Board_Tiles[4][12].level > 0)) { return TRUE; }
  786.     }
  787.     return FALSE;
  788.  
  789. } /* Tile_Not_Free */
  790.  
  791.  
  792. /*ARGSUSED*/
  793. void Tile_Release( w, event, params, num_params )
  794.      Widget    w;
  795.      XEvent    *event;
  796.      String    *params;
  797.      Cardinal    *num_params;
  798. /******************************************************************************
  799. * Called when the Board receives a BtnUp event.
  800. ******************************************************************************/
  801. {
  802.     extern int    Cheating;
  803.  
  804. /*--If there is a Click2 and if the tile type does not match with Click1 then
  805.  *  unhilite Click2. */
  806.  
  807.     DEBUG_CALL(Tile_Release);
  808.     if (!Cheating &&
  809.     Click1 != Board_Position_NULL &&
  810.     Click2 != Board_Position_NULL) {
  811.     int        tile1, tile2;
  812.  
  813.     tile1 = Click1->tiles[Click1->level-1];
  814.     tile2 = Click2->tiles[Click2->level-1];
  815.     if (/* Do tile faces match for those types that must match exactly? */
  816.         ((tile1 > 8 || tile2 > 8) && tile1 != tile2) ||
  817.         /* Are both tiles seasons? */
  818.         (tile1 <= 4 && tile2 > 4) ||
  819.         /* Are both tiles flowers? */
  820.         (tile1 >= 5 && tile1 <= 8 && (tile2 < 5 || tile2 > 8))) {
  821.         /* They don't match. */
  822.         if (Dragon_Resources.Sticky_Tile) {
  823.         /* Simply remove tile 2 from selected tiles. */
  824.         Hilite_Tile( Click2_Row, Click2_Col );
  825.         } else {
  826.         /* Remove tile 1 from selection and make tile 2 => tile 1.*/
  827.         Hilite_Tile( Click1_Row, Click1_Col );
  828.          Click1         = Click2;
  829.          Click1_Row  = Click2_Row;
  830.          Click1_Col  = Click2_Col;
  831.         Click2_Col  = 0;    /* Prevent dbl-clk removing 1 tile. */
  832.         }
  833.         Click2      = Board_Position_NULL;
  834.         Click2_Time = 0;
  835.     }
  836.     }
  837.  
  838. /*--If this tile has a left or a right neighbor then he isn't allowed. */
  839.  
  840.     if (!Cheating) {
  841.     if (Click2 != Board_Position_NULL &&
  842.         Tile_Not_Free( Click2_Row, Click2_Col)) {
  843.         Hilite_Tile( Click2_Row, Click2_Col );
  844.         Click2 = Board_Position_NULL;
  845.         Click2_Time = 0;
  846.     }
  847.     if (Click1 != Board_Position_NULL &&
  848.         Tile_Not_Free( Click1_Row, Click1_Col)) {
  849.         Hilite_Tile( Click1_Row, Click1_Col );
  850.         Click1 = Board_Position_NULL;
  851.     }
  852.     }
  853.  
  854.     DEBUG_RETURN(Tile_Release);
  855.  
  856. } /* Tile_Release */
  857.  
  858.  
  859. static void Next_Tile( Click, row, col )
  860.      int     Click;
  861.      int    *row;
  862.      int    *col;
  863. /******************************************************************************
  864. * Returns the "next" tile past row/col that exists and is "free".  Returns 0,0
  865. * when we run out of tiles.
  866. ******************************************************************************/
  867. {
  868.     int        tile1, tile2;
  869.  
  870. /*--Loop until we give up.  Advance the column.  Advance the row on column
  871.  *  overflow.  Give up on row overflow. */
  872.  
  873.     DEBUG_CALL(Next_Tile);
  874.     for (;;) {
  875.     ++*col;
  876.     if (*col > 14) {
  877.         *col = 1;
  878.         ++*row;
  879.         if (*row > 7) {
  880.         *row = 0;
  881.         *col = 0;
  882.         break;
  883.         }
  884.     }
  885.  
  886. /*--Check this tile.  If it doesn't exist or isn't free then ignore it. */
  887.  
  888.     if (Board_Tiles[*row][*col].level == 0) { continue; }
  889.     if (Tile_Not_Free( *row, *col )) { continue; }
  890.  
  891. /*--If moving Click1 then return now. */
  892.  
  893.     if (Click == 1) { break; }
  894.  
  895. /*--Continue the search if this tile does not match Click1. */
  896.  
  897.     tile1 =  Click1->tiles[Click1->level-1];
  898.     tile2 = Board_Tiles[*row][*col].tiles[Board_Tiles[*row][*col].level-1];
  899.     if (/* Do tile faces match for those types that must match exactly? */
  900.         ((tile1 > 8 || tile2 > 8) && tile1 != tile2) ||
  901.         /* Are both tiles seasons? */
  902.         (tile1 <= 4 && tile2 > 4) ||
  903.         /* Are both tiles flowers? */
  904.         (tile1 >= 5 && tile1 <= 8 && (tile2 < 5 || tile2 > 8))) {
  905.         /* They don't match. */
  906.         continue;
  907.     }
  908.     break;
  909.     }
  910.     DEBUG_RETURN(Next_Tile);
  911.  
  912. } /* Next_Tile */
  913.  
  914.  
  915. /*ARGSUSED*/
  916. void Hints( w, event, params, num_params )
  917.      Widget        w;
  918.      XButtonEvent    *event;
  919.      String        *params;
  920.      Cardinal        *num_params;
  921. /******************************************************************************
  922. * If Click1 not present then search for the "first" remaining tile otherwise
  923. * use Click1 as our current "base" tile.
  924. * If Click1 present but not Click2 then search for any match for Click1.
  925. * If Click2 not present either then search for the first remaining tile past 
  926. * Click1 otherwise search for the first remaining tile past Click2.
  927. * Keep searching for a new Click2 until we hit a matching tile or until we
  928. * run out.  Exit on match with new tile as Click2.
  929. * Advance Click1 and start a new search for Click2.  If we run out on Click1
  930. * then remove Click1.
  931. ******************************************************************************/
  932. {
  933.  
  934. /*--If we have a Click1 but no Click2 then search for a Click2. */
  935.  
  936.     if (Click1 != Board_Position_NULL &&
  937.     Click2 == Board_Position_NULL) {
  938.     One_Button_Hint = TRUE;
  939.     Click2_Row = 0;
  940.     Click2_Col = 0;
  941.     for (;;) {
  942.         Next_Tile( 2, &Click2_Row, &Click2_Col );
  943.         if (Click2_Col == 0) {
  944.         One_Button_Hint = FALSE;
  945.         Hilite_Tile( Click1_Row, Click1_Col );
  946.         Click1 = Board_Position_NULL;
  947.         DEBUG_RETURN(Hints);
  948.         return;
  949.         }
  950.         if (Click2_Row != Click1_Row ||
  951.         Click2_Col != Click1_Col) {
  952.         Click2 = &Board_Tiles[Click2_Row][Click2_Col];
  953.         Hilite_Tile( Click2_Row, Click2_Col );
  954.         DEBUG_RETURN(Hints);
  955.         return;
  956.         }
  957.     }
  958.     }
  959.  
  960. /*--Find a Click1 to work with if we don't already have one. */
  961.  
  962.     DEBUG_CALL(Hints);
  963.     if (Click1 == Board_Position_NULL) {
  964.     Click1_Row = 0;
  965.     Click1_Col = 0;
  966.     Next_Tile( 1, &Click1_Row, &Click1_Col );
  967.     if (Click1_Col == 0) { 
  968.         DEBUG_RETURN(Hints);
  969.         return;
  970.     }
  971.     Hilite_Tile( Click1_Row, Click1_Col );
  972.     Click1 = &Board_Tiles[Click1_Row][Click1_Col];
  973.     }
  974.  
  975. /*--Find our starting position for Click2 if we don't have one. */
  976.  
  977.     if (Click2 == Board_Position_NULL) {
  978.     Click2_Row = Click1_Row;
  979.     Click2_Col = Click1_Col;
  980.     } else {
  981.     Hilite_Tile( Click2_Row, Click2_Col );
  982.     Click2 = Board_Position_NULL;
  983.     }
  984.  
  985. /*--Loop until we get something. */
  986.  
  987.     for (;;) {
  988.     Next_Tile( 2, &Click2_Row, &Click2_Col );
  989.     if (Click2_Col != 0) {
  990.         if (Click2_Row != Click1_Row ||
  991.         Click2_Col != Click1_Col) {
  992.         Click2 = &Board_Tiles[Click2_Row][Click2_Col];
  993.         Hilite_Tile( Click2_Row, Click2_Col );
  994.         DEBUG_RETURN(Hints);
  995.         return;
  996.         }
  997.     } else {
  998.         Hilite_Tile( Click1_Row, Click1_Col );
  999.         Click1 = Board_Position_NULL;
  1000.         if (One_Button_Hint) {
  1001.         One_Button_Hint = FALSE;
  1002.         return;
  1003.         }
  1004.         Next_Tile( 1, &Click1_Row, &Click1_Col );
  1005.         if (Click1_Col == 0) {
  1006.         DEBUG_RETURN(Hints);
  1007.         return;
  1008.         }
  1009.         Hilite_Tile( Click1_Row, Click1_Col );
  1010.         Click1 = &Board_Tiles[Click1_Row][Click1_Col];
  1011.         Click2_Row = Click1_Row;
  1012.         Click2_Col = Click1_Col;
  1013.     }
  1014.     }
  1015.  
  1016. } /* Hints */
  1017.