home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume19 / xtmines / part01 / play.c < prev   
Encoding:
C/C++ Source or Header  |  1993-04-28  |  28.8 KB  |  1,149 lines

  1. /* xtmines: game where you try to cross a minefield */
  2. /* play.c: */
  3. /* Written by Timothy Tsai  April 13, 1992 */
  4.  
  5. #include "xtmines.h"
  6.  
  7. /* This is a kludge for systems without usleep() */
  8. #ifdef NOUSLEEP
  9. #define USLEEP_LOOPS    15000
  10. void usleep(usecs)
  11. unsigned usecs;
  12. {
  13.     int    i;
  14.     for (i=0;i<USLEEP_LOOPS;i++);
  15. }
  16. #endif
  17.  
  18. void PrintStr(window,str)
  19. windtype window;
  20. char *str;
  21. {
  22.     unsigned int    ww,wh;    /* get width and height of window */
  23.     unsigned int    wbw;    /*     border width               */
  24.     Font        font;
  25.     XFontStruct    *fs;
  26.     Window        a;
  27.     int        b,c;    /* dummy vars */
  28.     unsigned int    e;    /* dummy vars */
  29.     GC        gc;
  30.  
  31.     gc = (window==status) ? gcs : (window==time_left) ? gct : gcm;
  32.     XSetForeground(disp,gc,color[font_color].pixel);
  33.     XSetBackground(disp,gc,color[background_color].pixel);
  34.     XGetGeometry(disp,wind[window],&a,&b,&c,&ww,&wh,&wbw,&e);
  35.     fs = (window==status) ? fontsstruct : 
  36.          (window==time_left) ? fonttstruct : fontmstruct;
  37.     XDrawImageString(disp,wind[window],gc,
  38.         ((ww-2*wbw)-XTextWidth(fs,str,strlen(str)))/2,
  39.         (wh - fs->ascent - fs->descent)/2 +
  40.             fs->ascent,str,strlen(str));
  41.     XFlush(disp);
  42. }
  43.  
  44. void InvPrintStr(window,str)
  45. windtype window;
  46. char *str;
  47. {
  48.     unsigned int    ww,wh;    /* get width and height of window */
  49.     unsigned int    wbw;    /*     border width               */
  50.     Font        font;
  51.     XFontStruct    *fs;
  52.     Window        a;
  53.     int        b,c;    /* dummy vars */
  54.     unsigned int    e;    /* dummy vars */
  55.     GC        gc;
  56.  
  57.     gc = (window==status) ? gcs : (window==time_left) ? gct : gcm;
  58.     XSetForeground(disp,gc,color[background_color].pixel);
  59.     XSetBackground(disp,gc,color[font_color].pixel);
  60.     XGetGeometry(disp,wind[window],&a,&b,&c,&ww,&wh,&wbw,&e);
  61.     fs = (window==status) ? fontsstruct : 
  62.          (window==time_left) ? fonttstruct : fontmstruct;
  63.     XDrawImageString(disp,wind[window],gc,
  64.         ((ww-2*wbw)-XTextWidth(fs,str,strlen(str)))/2,
  65.         (wh - fs->ascent - fs->descent)/2 +
  66.             fs->ascent,str,strlen(str));
  67.     XFlush(disp);
  68. }
  69.  
  70. void WindPrint(window,str)
  71. windtype window;
  72. char *str;
  73. {
  74.     XClearWindow(disp,wind[window]);
  75.     PrintStr(window,str);
  76. }
  77.  
  78. char *num_rank_to_words(num_rank)
  79. ranktype num_rank;
  80. {
  81.     switch (num_rank) {
  82.         case grunt    : return ("grunt"); break;
  83.         case corporal    : return ("corporal"); break;
  84.         case lieutenant    : return ("lieutenant"); break;
  85.         case captain    : return ("captain"); break;
  86.         case mmajor    : return ("major"); break;
  87.         case general    : return ("general"); break;
  88.         case president    : return ("president"); break;
  89.         case king    : return ("king"); break;
  90.         case emperor    : return ("emperor"); break;
  91.         case angel    : return ("angel"); break;
  92.         case god    : return ("god"); break;
  93.         default        : fprintf(stderr,"Error: Illegal rank\n");
  94.                   exit(2);
  95.     }
  96. }
  97.  
  98. char    last_status_str[MAXSTRLEN];
  99. void print_status(str)
  100. char *str;
  101. {
  102.  
  103.     strcpy(last_status_str,str);
  104.     WindPrint(status,str);
  105. }
  106.  
  107. char    *clock_time_str(time)
  108. int time;
  109. {
  110.     int        h,m,s;
  111.     static char    str[MAXSTRLEN];
  112.  
  113.     h = time/3600;
  114.     m = (time-(h*3600))/60;
  115.     s = (time-(h*3600)-(m*60));
  116.     if (h != 0)
  117.         sprintf(str,"%d:%02d:%02d",h,m,s);
  118.     else if (m != 0)
  119.         sprintf(str,"%d:%02d",m,s);
  120.     else
  121.         sprintf(str,"%d",s);
  122.     return (str);
  123. }
  124.  
  125. void print_display()
  126. {
  127.     char        str[MAXSTRLEN];
  128.  
  129.     sprintf(str,"Bombs: %d",num_bombs_left);
  130.     WindPrint(bombs_left,str);
  131.  
  132.     sprintf(str,"Bonus: %d",num_bonus);
  133.     WindPrint(bonus_wind,str);
  134.  
  135.     sprintf(str,"Time: %s",clock_time_str(num_time_left));
  136.     WindPrint(time_left,str);
  137.  
  138.     sprintf(str,"Score: %d",num_score);
  139.     WindPrint(score,str);
  140.  
  141.     sprintf(str,"Grenades: %d",num_grenades_left);
  142.     WindPrint(grenades_left,str);
  143.  
  144.     sprintf(str,"Rank: %s",num_rank_to_words(level));
  145.     WindPrint(rank,str);
  146.  
  147.     WindPrint(quit_wind,"Quit");
  148.  
  149.     if (used_showfig)
  150.         sprintf(str,"Show = *%s", (show==sh_man) ? "man" : "fig");
  151.     else
  152.         sprintf(str,"Show = %s", (show==sh_man) ? "man" : "fig");
  153.     WindPrint(show_wind,str);
  154.  
  155.     WindPrint(tgrenade_wind,"Throw Grenade");
  156.  
  157.     WindPrint(giveup_wind,"Surrender");
  158.  
  159.     sprintf(str,"Automark = %s",automark ? "on" : "off");
  160.     WindPrint(automark_wind,str);
  161.  
  162.     sprintf(str,"Lives: %d",num_lives);
  163.     WindPrint(lives_left,str);
  164.  
  165.     WindPrint(pause_wind,"Pause");
  166.  
  167.     if (used_sanity)
  168.         sprintf(str,"Sanity = *%s",sanity ? "on" : "off");
  169.     else
  170.         sprintf(str,"Sanity = %s",sanity ? "on" : "off");
  171.     WindPrint(sanitycheck_wind,str);
  172.  
  173.     if (used_eautomark)
  174.         sprintf(str,"ExtAmark = *%s",extended_automark ? "on" : "off");
  175.     else
  176.         sprintf(str,"ExtAmark = %s",extended_automark ? "on" : "off");
  177.     WindPrint(eautomark_wind,str);
  178.  
  179.     WindPrint(refresh_wind,"Refresh");
  180.  
  181.     WindPrint(status,last_status_str);
  182.  
  183.     XFlush(disp);
  184. }
  185.  
  186. Pixmap bitmap[NUMBITMAPS];
  187. int bmwidth[NUMBITMAPS];
  188. int bmheight[NUMBITMAPS];
  189. /* This function will draw the bitmap indicated by bm in the field at the */
  190. /*   position (vx,vy).  If show==sh_fig, then a figure will be drawn      */
  191. /*   instead of the man bitmap.  The figure is the number of surrounding  */
  192. /*   bombs.  The font for figures is the same as the font for the status. */
  193. void draw(bm,vx,vy)
  194. bmtype bm;
  195. int vx,vy;
  196. {
  197.     int    x,y;
  198.     int    numbombs;
  199.     char    str[2];
  200.  
  201.     setxy(vx,vy,&x,&y);
  202.     if ((bm==bm_man) && (show==sh_fig)) {
  203.        clear(vx,vy);
  204.        numbombs = bomb_status(vx,vy);
  205.        str[0] = '0' + numbombs; str[1]=NULL;
  206.        XDrawString(disp,field,gcs,
  207.         1+vx*(man_width+1)+(man_width-fontshoww)/2,
  208.         1+vy*(man_height+1)+(man_height-fontshowa)/2+fontshowa,
  209.         str,strlen(str));
  210.     }
  211.     else
  212.        XCopyPlane(disp,bitmap[bm],field,gcm,0,0,bmwidth[bm],bmheight[bm],
  213.         x+1,y+1,1);
  214.     XFlush(disp);
  215. }
  216.  
  217. void clear(vx,vy)
  218. int vx,vy;
  219. {
  220.     int    x,y;
  221.  
  222.     setxy(vx,vy,&x,&y);
  223.     XClearArea(disp,field,x+1,y+1,bmwidth[bm_man],bmwidth[bm_safe],FALSE);
  224. }
  225.  
  226. fieldstruct FIELD[NUMCOLS][NUMROWS];
  227. void draw_field()
  228. {
  229.     int    r,c;
  230.     double    rowsep,colsep;
  231.  
  232.     /* draw vertical lines */
  233.     colsep = ((double)(w-2*bw)/(double)NUMCOLS);
  234.     for (c=0; c<NUMCOLS; c++) {
  235.         XSetForeground(disp,gcm,color[field_color].pixel);
  236.         XSetBackground(disp,gcm,color[background_color].pixel);
  237.         XDrawLine(disp,field,gcm,(int)(c*colsep),0,(int)(c*colsep),fh);
  238.     }
  239.  
  240.     /* draw horizontal lines */
  241.     rowsep = ((double)fh/(double)NUMROWS);
  242.     for (r=0; r<NUMROWS; r++) {
  243.         XSetForeground(disp,gcm,color[field_color].pixel);
  244.         XSetBackground(disp,gcm,color[background_color].pixel);
  245.         XDrawLine(disp,field,gcm,0,(int)(r*rowsep),w,(int)(r*rowsep));
  246.     }
  247.  
  248.     /* draw contents */
  249.     for (c=0;c<NUMCOLS;c++)
  250.         for (r=0;r<NUMROWS;r++)
  251.             if (FIELD[c][r].m==fm_safe)
  252.                 draw(bm_safe,c,r);
  253.             else if (FIELD[c][r].m==fm_bomb)
  254.                 draw(bm_bombmark,c,r);
  255.             else switch (FIELD[c][r].c) {
  256.                 case fc_empty    : break;
  257.                 case fc_man    : draw(bm_man,c,r); break;
  258.                 case fc_bomb    : if (dead)
  259.                             draw(bm_bomb,c,r);
  260.                           break;
  261.                 case fc_tombstone
  262.                         : draw(bm_tombstone,c,r); break;
  263.                 case fc_goal    : draw(bm_goal,c,r); break;
  264.                 case fc_trail    : draw(bm_trail,c,r); break;
  265.                 default        : fprintf(stderr,
  266.                     "Error: Illegal FIELD content\n");
  267.                     exit(1);
  268.             }
  269.     XFlush(disp);
  270. }
  271.  
  272. /* remove all Exposure events from the events queue; return the number
  273.    of actual events removed */
  274. int remove_expose_events()
  275. {
  276.     int    r=0;        /* number of events removed so far */
  277.     XEvent    event;        /* not used */
  278.  
  279.     while (XCheckMaskEvent(disp,ExposureMask,&event))
  280.         r++;
  281.     return (r);
  282. }
  283.  
  284. void refresh()
  285. {
  286.     XEvent        event;
  287.     int        done=FALSE;    /* finished dequeuing all exposes? */
  288.  
  289.     print_display();
  290.     draw_field();
  291.     draw(bm_man,manvx,manvy);
  292.     XFlush(disp);
  293.  
  294.     /* now dequeue all immediately succeeding exposure events */
  295.     /*    to prevent multiple, useless refreshes              */
  296.     remove_expose_events();
  297. }
  298.  
  299. /* set up windows for pausing;  the field should be blanked to prevent
  300.    cheating;  the time should be adjusted so there is no penalty for pausing;
  301. */
  302. int do_pause()
  303. {
  304.     int        time_at_start_of_pause;
  305.  
  306.     char        str[MAXSTRLEN];
  307.     unsigned int    ww,wh;    /* get width and height of window */
  308.     unsigned int    wbw;    /*     border width               */
  309.     XFontStruct    *fs;
  310.     Window        a;
  311.     int        b,c;    /* dummy vars */
  312.     unsigned int    e;    /* dummy vars */
  313.  
  314.     int            wait=TRUE;
  315.     XEvent            event;
  316.     XButtonPressedEvent    *eventbp;
  317.     XKeyPressedEvent    *eventkp;
  318.     KeySym            ksym;
  319.     char            s;
  320.     int            done;
  321.  
  322.     WindPrint(pause_wind,"Continue");
  323.     time_at_start_of_pause = time(0);
  324.  
  325.     /* blank out field and write "P-A-U-S-E" message on field */
  326.     XClearWindow(disp,field);
  327.     strcpy(str,"P-A-U-S-E");
  328.     XSetForeground(disp,gcs,color[font_color].pixel);
  329.     XSetBackground(disp,gcs,color[background_color].pixel);
  330.     XGetGeometry(disp,field,&a,&b,&c,&ww,&wh,&wbw,&e);
  331.     fs = fontsstruct;
  332.     XDrawImageString(disp,field,gcs,
  333.         ((ww-2*wbw)-XTextWidth(fs,str,strlen(str)))/2,
  334.         (wh - fs->ascent - fs->descent)/2 +
  335.             fs->ascent,str,strlen(str));
  336.     XFlush(disp);
  337.  
  338.     /* Wait for keypress or button click to signal done with pause */
  339.     while (wait == TRUE) {
  340.        while (!XPending(disp))
  341.         usleep(XPENDING_UDELAY);
  342.        XNextEvent(disp,&event);
  343.        if (event.type==ButtonPress) {
  344.         eventbp = (XButtonPressedEvent *) &event;
  345.         if (eventbp->window == wind[pause_wind])
  346.                     wait = FALSE;
  347.        }
  348.        else if (event.type == KeyPress) {
  349.         eventkp = (XKeyPressedEvent *) &event;
  350.         XLookupString(&event,&s,1,&ksym,NULL);
  351.         if ((s == 'c') || (s == 'C'))
  352.             wait = FALSE;
  353.        }
  354.        else if (event.type == Expose) {
  355.        /* do refresh in pause mode; pause_wind and field are different */
  356.         print_display();
  357.         WindPrint(pause_wind,"Continue");
  358.         sprintf(str,"Time: %s",clock_time_str(
  359.             num_time_left_at_start-
  360.             (time_at_start_of_pause-time_at_start_of_level
  361.                            -pause_time)));
  362.         WindPrint(time_left,str);
  363.  
  364.         /* blank out field and write "P-A-U-S-E" message on field */
  365.         WindPrint(pause_wind,"Continue");
  366.         XClearWindow(disp,field);
  367.         strcpy(str,"P-A-U-S-E");
  368.         XSetForeground(disp,gcs,color[font_color].pixel);
  369.         XSetBackground(disp,gcs,color[background_color].pixel);
  370.         XGetGeometry(disp,field,&a,&b,&c,&ww,&wh,&wbw,&e);
  371.         fs = fontsstruct;
  372.         XDrawImageString(disp,field,gcs,
  373.             ((ww-2*wbw)-XTextWidth(fs,str,strlen(str)))/2,
  374.             (wh - fs->ascent - fs->descent)/2 +
  375.                 fs->ascent,str,strlen(str));
  376.         XFlush(disp);
  377.     
  378.         /* now dequeue all immediately succeeding exposure events */
  379.         /*    to prevent multiple, useless refreshes              */
  380.         remove_expose_events();
  381.        }
  382.     }
  383.  
  384.     pause_time += time(0) - time_at_start_of_pause;
  385.  
  386.     /* Restore windows to continue game */
  387.     XClearWindow(disp,field);
  388.     refresh();
  389.  
  390.     WindPrint(pause_wind,"Pause");
  391. }
  392.  
  393. int setxy(vx,vy,x,y)
  394. int vx,vy,*x,*y;
  395. {
  396. /* convert virtual coordinates to actual field pixel coordinates */
  397.     double    rowsep,colsep;
  398.  
  399.     if (((vx>=0) && (vx<NUMCOLS)) &&
  400.         ((vy>=0) && (vy<NUMROWS))) {
  401.         colsep = ((double)(w-2*bw)/(double)NUMCOLS);
  402.         rowsep = ((double)fh/(double)NUMROWS);
  403.         *x = vx*colsep;
  404.         *y = vy*rowsep;
  405.         return (SUCCESS);
  406.     }
  407.     else
  408.         return (OUT_OF_RANGE);
  409. }
  410.  
  411. int setvxvy(x,y,vx,vy)
  412. int x,y,*vx,*vy;
  413. {
  414. /* convert actual field pixel coordinates to virtual coordinates */
  415.     double    rowsep,colsep;
  416.  
  417.     if (((x>=0) && (x<w-2*bw)) &&
  418.         ((y>=0) && (y<fh))) {
  419.         colsep = ((double)(w-2*bw)/(double)NUMCOLS);
  420.         rowsep = ((double)fh/(double)NUMROWS);
  421.         *vx = x/colsep;
  422.         *vy = y/rowsep;
  423.         return (SUCCESS);
  424.     }
  425.     else
  426.         return (OUT_OF_RANGE);
  427. }
  428.  
  429. int bomb_status(vx,vy)
  430. int vx,vy;
  431. {
  432.     int    numbombs=0;
  433.  
  434.     if (vy>0) {
  435.         if ((vx>0) && (FIELD[vx-1][vy-1].c==fc_bomb))
  436.             numbombs++;
  437.         if (FIELD[vx][vy-1].c==fc_bomb)
  438.             numbombs++;
  439.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].c==fc_bomb))
  440.             numbombs++;
  441.     }
  442.     if (vy<NUMROWS-1) {
  443.         if ((vx>0) && (FIELD[vx-1][vy+1].c==fc_bomb))
  444.             numbombs++;
  445.         if (FIELD[vx][vy+1].c==fc_bomb)
  446.             numbombs++;
  447.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].c==fc_bomb))
  448.             numbombs++;
  449.     }
  450.     if ((vx>0) && (FIELD[vx-1][vy].c==fc_bomb))
  451.         numbombs++;
  452.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].c==fc_bomb))
  453.         numbombs++;
  454.  
  455.     return (numbombs);
  456. }
  457.  
  458. void show_bomb_status(vx,vy)
  459. int vx,vy;
  460. {
  461.     int    numbombs;
  462.     char    str[MAXSTRLEN];
  463.  
  464.     numbombs = bomb_status(vx,vy);
  465.  
  466.     sprintf(str,"%d surrounding bomb%c",numbombs,numbombs==1 ? ' ' : 's');
  467.     print_status(str);
  468.  
  469.     if ((automark) && (numbombs==0))
  470.         mark_allaround_ok(manvx,manvy,0);
  471.  
  472. /* show_all_bombs:  shows all bombs on field   */
  473. /*                  if any marks are incorrect */
  474. /*                     highlight those squares */
  475. void show_all_bombs()
  476. {
  477.     int    c,r;    /* column and row under consideration */
  478.  
  479.     for (c=0;c<NUMCOLS;c++)
  480.        for (r=0;r<NUMROWS;r++)
  481.         if (FIELD[c][r].c == fc_bomb)
  482.            if (FIELD[c][r].m == fm_safe)
  483.             draw(bm_safewrong,c,r);    /* bomb, marked safe */
  484.            else
  485.             draw(bm_bomb,c,r);    /* bomb, unmarked or correct */
  486.         else if (FIELD[c][r].m == fm_bomb)
  487.            draw(bm_bombmarkwrong,c,r);    /* no bomb, marked unsafe */
  488. }
  489.  
  490. void do_promotion()
  491. {
  492.         char                    str[MAXSTRLEN];
  493.     XEvent            event;
  494.         XButtonPressedEvent     *eventbp;
  495.         XKeyPressedEvent        *eventkp;
  496.         int                     wait = TRUE;
  497.  
  498.     finished = TRUE;
  499.     num_score += current_bonus();
  500.     printf("Your bonus for reaching the goal at rank %s is\n",
  501.         num_rank_to_words(level));
  502.     printf("     %5d = %d*%d \t (level*Time left)\n",
  503.         current_bonus(),level+1,num_time_left);
  504.     if (num_bombs_left>0)
  505.         printf("           + %d    \t (Num bombs left)\n",
  506.         num_bombs_left);
  507.     if (num_grenades_left>0)
  508.         printf("           + %d*%d \t (Num grenades left*ppg)\n",
  509.         num_grenades_left,POINTS_PER_GRENADE);
  510.     if (num_tombstones>0)
  511.         printf("           - %d*%d \t (Num tombstones*ppts)\n",
  512.         num_tombstones,POINTS_PER_TOMBSTONE);
  513. /* This used to be used for ver 1.0
  514.     if (used_showfig)
  515.         printf("           - %d \t (Used showfig?)\n",
  516.         POINTS_FOR_USED_SHOWFIG);
  517. */
  518.     if (used_sanity)
  519.         printf("           - %d \t (Used sanity check?)\n",
  520.         (PERCENT_FOR_USED_SANITY*bonus_without_penalties())/100);
  521.     if (used_eautomark)
  522.         printf("           - %d \t (Used extended_automark?)\n",
  523.         (PERCENT_FOR_USED_EAUTOMARK*bonus_without_penalties())/100);
  524.     printf("Your current score is %d = %d + %d\n",
  525.         num_score,num_score-current_bonus(),current_bonus());
  526.  
  527.     show_all_bombs();
  528.  
  529.     /* now update score on screen incrementally */
  530.     {
  531.         int i;
  532.         /* this will make sure that at most 100 steps are made */
  533.         int step = current_bonus()/100;
  534.  
  535.         for (i=num_score-current_bonus();i<num_score;i+=step) {
  536.             sprintf(str,"Score: %d",i);
  537.             PrintStr(score,str);
  538.         }
  539.         sprintf(str,"Score: %d",num_score);
  540.         WindPrint(score,str);
  541.     }
  542.  
  543.     if (level == HIGHEST_PLAYABLE_RANK) {
  544.        sprintf(str,
  545.        "Congratulations, you have reached completed the highest rank!");
  546.        level++;
  547.        show_and_add_scores();
  548.     }
  549.     else
  550.        sprintf(str,"Congratulations, you have been promoted to %s",
  551.         num_rank_to_words(level+1));
  552.     print_status(str);
  553.  
  554.     /* reward additional lives and grenades */
  555.     if ((level>=LOWEST_LEVEL_FOR_REWARD) &&
  556.         (num_score>=LOWEST_SCORE_FOR_REWARD)) {
  557.         if (++num_grenades_left > NUM_GRENADES_AT_START)
  558.             num_grenades_left = NUM_GRENADES_AT_START;
  559.         sprintf(str,"Grenades: %d",num_grenades_left);
  560.         WindPrint(grenades_left,str);
  561.         if (++num_lives>MAXLIVES) num_lives=MAXLIVES;
  562.         sprintf(str,"Lives: %d",num_lives);
  563.         WindPrint(lives_left,str);
  564.     }
  565.  
  566.     sleep(3);
  567.  
  568.     print_status ("In the field, press key or click button to continue");
  569.     while (wait == TRUE) {
  570.        while (!XPending(disp))
  571.         usleep(XPENDING_UDELAY);
  572.        XNextEvent(disp,&event);
  573.        if (event.type==ButtonPress) {
  574.         eventbp = (XButtonPressedEvent *) &event;
  575.         if (eventbp->window == field)
  576.             wait = FALSE;
  577.         else if (eventbp->window == wind[quit_wind]) {
  578.             level++;
  579.             show_and_add_scores();
  580.         }
  581.        }
  582.        else if (event.type == KeyPress) {
  583.         eventkp = (XKeyPressedEvent *) &event;
  584.         if (eventkp->window == field)
  585.             wait = FALSE;
  586.        }
  587.        else if (event.type == Expose) {
  588.         refresh();
  589.         show_all_bombs();
  590.        }
  591.     }
  592. }
  593.  
  594. /* next_to_trail:  returns TRUE is at least one of the up to eight adjacent */
  595. /*                 squares is already traversed and marked as fc_trail;     */
  596. /*                 vx and vy is the square in question                      */
  597. int next_to_trail(vx,vy)
  598. int vx,vy;
  599. {
  600.     int    numbombs=0;
  601.  
  602.     if (vy>0) {
  603.         if ((vx>0) && (FIELD[vx-1][vy-1].c==fc_trail))
  604.             return (TRUE);
  605.         if (FIELD[vx][vy-1].c==fc_trail)
  606.             return (TRUE);
  607.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].c==fc_trail))
  608.             return (TRUE);
  609.     }
  610.     if (vy<NUMROWS-1) {
  611.         if ((vx>0) && (FIELD[vx-1][vy+1].c==fc_trail))
  612.             return (TRUE);
  613.         if (FIELD[vx][vy+1].c==fc_trail)
  614.             return (TRUE);
  615.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].c==fc_trail))
  616.             return (TRUE);
  617.     }
  618.     if ((vx>0) && (FIELD[vx-1][vy].c==fc_trail))
  619.         return (TRUE);
  620.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].c==fc_trail))
  621.         return (TRUE);
  622.  
  623.     return (FALSE);
  624. }
  625.  
  626. int move_man(vx,vy)
  627. int vx,vy;
  628. {
  629.     int    ax,ay;        /* adjusted x and y */
  630.     int    reached_goal=FALSE;
  631.  
  632.     setxy(vx,vy,&ax,&ay);
  633.  
  634.     if (FIELD[vx][vy].m == fm_bomb) {
  635.         XBell(disp,0);
  636.         print_status("Square is marked as unsafe!");
  637.         return (SQUARE_IS_UNSAFE);
  638.     }
  639.     if (((abs(manvx-vx)<=1) && (abs(manvy-vy)<=1)) ||
  640.         (FIELD[vx][vy].c==fc_trail) ||
  641.         (next_to_trail(vx,vy) && (FIELD[vx][vy].m==fm_safe))) {
  642.        switch (FIELD[vx][vy].c) {
  643.         case fc_goal    :
  644.         case fc_trail    :
  645.         case fc_empty    : /* only allow movement of one square */
  646.                   if ((FIELD[vx][vy].c==fc_goal) && (!dead))
  647.                     reached_goal = TRUE;
  648.                   draw(bm_trail,manvx,manvy);
  649.                   FIELD[manvx][manvy].c = fc_trail;
  650.                   manvx = vx; manvy = vy;
  651.                   draw(bm_man,vx,vy);
  652.                   FIELD[manvx][manvy].c = fc_man;
  653.                   FIELD[manvx][manvy].m = fm_nomark;
  654.                   if (reached_goal)
  655.                     do_promotion();
  656.                   break;
  657.         case fc_man    : show_bomb_status(manvx,manvy);
  658.                   draw(bm_man,manvx,manvy);
  659.                   break;
  660.         case fc_bomb    : draw(bm_trail,manvx,manvy);
  661.                   FIELD[manvx][manvy].c = fc_trail;
  662.                   die(vx,vy);
  663.                   break;
  664.         case fc_tombstone:
  665.                   break;
  666.         default        : fprintf(stderr,
  667.                     "Error: Illegal FIELD contents\n");
  668.                   exit(2);
  669.        }
  670.        show_bomb_status(manvx,manvy);
  671.     }
  672.     else {
  673.        print_status("Can't move that far!");
  674.        XBell(disp,0);          
  675.     }
  676.     return (SUCCESS);
  677. }
  678.  
  679. int remove_mark(vx,vy)
  680. int vx,vy;
  681. {
  682.     if (FIELD[vx][vy].m == fm_nomark)
  683.         return (NO_MARK_TO_REMOVE);    
  684.     else {
  685.         FIELD[vx][vy].m = fm_nomark;
  686.         clear(vx,vy);
  687.         return (SUCCESS);
  688.     }
  689. }
  690.  
  691. int mark_ok(vx,vy)
  692. int vx,vy;
  693. {
  694.     switch (FIELD[vx][vy].c) {
  695.         case fc_empty:
  696.         case fc_bomb:    FIELD[vx][vy].m = fm_safe;
  697.                 draw(bm_safe,vx,vy);
  698.                 return (SUCCESS);
  699.         case fc_trail:
  700.         case fc_man:
  701.         case fc_tombstone:
  702.         case fc_goal:    return (CANT_MARK);
  703.         default:    fprintf(stderr,
  704.                     "Error: Illegal FIELD contents\n");
  705.                 exit(3);
  706.     }
  707. }
  708.  
  709. /* if level<0, then only mark immediately surrounding squares;  don't do
  710.    extended automarking
  711. */
  712. int mark_allaround_ok(vx,vy,level)
  713. int vx,vy,level;    /* level=level of recursion */
  714. {
  715.     int    c,r;    /* col and row to clear FIELD[][].done flags */
  716.  
  717.     /* clear all done flags if on original call */
  718.     if (level==0)
  719.         for (c=0;c<NUMCOLS;c++)
  720.            for (r=0;r<NUMROWS;r++)
  721.             FIELD[c][r].done = FALSE;
  722.  
  723.     if (vy>0) {
  724.         if ((vx>0) && (FIELD[vx-1][vy-1].m==fm_nomark))
  725.             mark_ok(vx-1,vy-1);
  726.         if (FIELD[vx][vy-1].m==fm_nomark)
  727.             mark_ok(vx,vy-1);
  728.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].m==fm_nomark))
  729.             mark_ok(vx+1,vy-1);
  730.     }
  731.     if (vy<NUMROWS-1) {
  732.         if ((vx>0) && (FIELD[vx-1][vy+1].m==fm_nomark))
  733.             mark_ok(vx-1,vy+1);
  734.         if (FIELD[vx][vy+1].m==fm_nomark)
  735.             mark_ok(vx,vy+1);
  736.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].m==fm_nomark))
  737.             mark_ok(vx+1,vy+1);
  738.     }
  739.     if ((vx>0) && (FIELD[vx-1][vy].m==fm_nomark))
  740.         mark_ok(vx-1,vy);
  741.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].m==fm_nomark))
  742.         mark_ok(vx+1,vy);
  743.  
  744.     if ((level>=0) && (extended_automark) && (!FIELD[vx][vy].done)) {
  745.     /* mark square as being already marked-allaround to prevent repeat */
  746.         FIELD[vx][vy].done = TRUE;
  747.         if (bomb_status(vx,vy) == 0) {
  748.             draw(bm_trail,vx,vy);
  749.             FIELD[vx][vy].c = fc_trail;
  750.             FIELD[vx][vy].m = fm_nomark;
  751.         }
  752.         if (vy>0) {
  753.             if ((vx>0) && (FIELD[vx-1][vy-1].m==fm_safe) &&
  754.                 (bomb_status(vx-1,vy-1)==0))
  755.                 mark_allaround_ok(vx-1,vy-1,level+1);
  756.             if ((FIELD[vx][vy-1].m==fm_safe) &&
  757.                 (bomb_status(vx,vy-1)==0))
  758.                 mark_allaround_ok(vx,vy-1,level+1);
  759.             if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].m==fm_safe) &&
  760.                 (bomb_status(vx+1,vy-1)==0))
  761.                 mark_allaround_ok(vx+1,vy-1,level+1);
  762.         }
  763.         if (vy<NUMROWS-1) {
  764.             if ((vx>0) && (FIELD[vx-1][vy+1].m==fm_safe) &&
  765.                 (bomb_status(vx-1,vy+1)==0))
  766.                 mark_allaround_ok(vx-1,vy+1,level+1);
  767.             if ((FIELD[vx][vy+1].m==fm_safe) &&
  768.                 (bomb_status(vx,vy+1)==0))
  769.                 mark_allaround_ok(vx,vy+1,level+1);
  770.             if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].m==fm_safe) &&
  771.                 (bomb_status(vx+1,vy+1)==0))
  772.                 mark_allaround_ok(vx+1,vy+1,level+1);
  773.         }
  774.         if ((vx>0) && (FIELD[vx-1][vy].m==fm_safe) &&
  775.             (bomb_status(vx-1,vy)==0))
  776.             mark_allaround_ok(vx-1,vy,level+1);
  777.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].m==fm_safe) &&
  778.             (bomb_status(vx+1,vy)==0))
  779.             mark_allaround_ok(vx+1,vy,level+1);
  780.     }
  781.  
  782.     /* put man marker back on field */
  783.     if (level<=0)
  784.         draw(bm_man,manvx,manvy);
  785.  
  786.     return (SUCCESS);
  787. }
  788.  
  789. int mark_bomb(vx,vy)
  790. int vx,vy;
  791. {
  792.     switch (FIELD[vx][vy].c) {
  793.         case fc_empty:
  794.         case fc_bomb:    FIELD[vx][vy].m = fm_bomb;
  795.                 draw(bm_bombmark,vx,vy);
  796.                 return (SUCCESS);
  797.         case fc_trail:
  798.         case fc_man:
  799.         case fc_tombstone:
  800.         case fc_goal:    return (CANT_MARK);
  801.         default:    fprintf(stderr,
  802.                     "Error: Illegal FIELD contents\n");
  803.                 exit(3);
  804.     }
  805. }
  806.  
  807. void die(vx,vy)
  808. int vx,vy;
  809. {
  810.     char            str[MAXSTRLEN];
  811.  
  812.     explode(vx,vy);
  813.     draw(bm_tombstone,vx,vy);
  814.     FIELD[vx][vy].c = fc_tombstone;
  815.     num_tombstones++;
  816.     if (--num_lives<0)
  817.         num_lives = 0;
  818.     sprintf(str,"Lives: %d",num_lives);
  819.     WindPrint(lives_left,str);
  820.     if ((num_lives<=0) && (!dead)) {
  821.         show_bomb_status(manvx,manvy);
  822.         dead = TRUE;
  823.         show_all_bombs();
  824.         print_status("You have just died!  GAME OVER!");
  825.         XBell(disp,0);
  826.         XBell(disp,0);
  827.         XBell(disp,0);
  828.         XFlush(disp);
  829.         sleep(3);
  830.     }
  831.     draw(bm_man,0,0);
  832.     FIELD[0][0].c = fc_man;
  833.     FIELD[0][0].m = fm_nomark;
  834.     manvx = manvy = 0;
  835.     show_bomb_status(manvx,manvy);
  836. }
  837.  
  838. int explode(vx,vy)
  839. int vx,vy;
  840. {
  841.     int    i;
  842.     char    str[MAXSTRLEN];
  843.  
  844.     if (FIELD[vx][vy].c == fc_bomb) {
  845.         num_bombs_left--;
  846.         sprintf(str,"Bombs: %d",num_bombs_left);
  847.         WindPrint(bombs_left,str);
  848.     }
  849.  
  850.     /* simulate explosion on screen */
  851.     for (i=0;i<10;i++) {
  852.         draw(bm_pow,vx,vy);
  853.         usleep(BOMB_EXPLODE_UDELAY);
  854.         draw(bm_bomb,vx,vy);
  855.         usleep(BOMB_EXPLODE_UDELAY);
  856.     }
  857.     clear(vx,vy);
  858.     FIELD[vx][vy].c = fc_empty;
  859.     FIELD[vx][vy].m = fm_nomark;
  860.  
  861.  
  862.     /* check to see if other bombs nearby--Manhattan dirs only */
  863.     if ((vx>0) && (FIELD[vx-1][vy].c==fc_bomb)) explode(vx-1,vy);
  864.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].c==fc_bomb)) explode(vx+1,vy);
  865.     if ((vy>0) && (FIELD[vx][vy-1].c==fc_bomb)) explode(vx,vy-1);
  866.     if ((vy<NUMROWS-1) && (FIELD[vx][vy+1].c==fc_bomb)) explode(vx,vy+1);
  867.  
  868.     /* check to see if man nearby--Manhattan dirs only */
  869.     if ((vx>0) && (FIELD[vx-1][vy].c==fc_man)) die(vx-1,vy);
  870.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].c==fc_man)) die(vx+1,vy);
  871.     if ((vy>0) && (FIELD[vx][vy-1].c==fc_man)) die(vx,vy-1);
  872.     if ((vy<NUMROWS-1) && (FIELD[vx][vy+1].c==fc_man)) die(vx,vy+1);
  873.  
  874.     return(SUCCESS);
  875. }
  876.  
  877. /* bonus_without_penalties:  returns the current bonus remaining for */
  878. /*    this level, without used_* penalties                         */
  879. int bonus_without_penalties()
  880. {
  881.     int    b;    /* temp value for current bonus */
  882.  
  883.     b = (level+1)*num_time_left
  884.         + num_bombs_left
  885.         + num_grenades_left*POINTS_PER_GRENADE
  886.         - num_tombstones*POINTS_PER_TOMBSTONE;
  887.  
  888.     if (b < 0)
  889.         b = 0;
  890.  
  891.     return (b);
  892. }
  893.  
  894. /* current_bonus:  returns the current amount of bonus */
  895. /*                    remaining for this level         */
  896. int current_bonus()
  897. {
  898.     int    b;    /* temp value for current bonus */
  899.  
  900.     b = bonus_without_penalties()
  901. /* This used to be used for ver 1.0
  902.         - used_showfig*POINTS_FOR_USED_SHOWFIG;
  903. */
  904.         - used_sanity*
  905.            (PERCENT_FOR_USED_SANITY*bonus_without_penalties())/100
  906.         - used_eautomark*
  907.            (PERCENT_FOR_USED_EAUTOMARK*bonus_without_penalties())/100;
  908.  
  909.     if (b < 0)
  910.         b = 0;
  911.  
  912.     return (b);
  913. }
  914.         
  915.  
  916. /* update_bonus:  recalculate the amount of bonus remaining for this level */
  917. /*                display the correct new bonus on screen                  */
  918. int update_bonus()
  919. {
  920.     char        str[MAXSTRLEN];
  921.     static int    last_bonus;
  922.  
  923.     if ((num_bonus = current_bonus()) != last_bonus) {
  924.         sprintf(str,"Bonus: %d",num_bonus);
  925.         WindPrint(bonus_wind,str);
  926.         last_bonus = num_bonus;
  927.     }
  928. }
  929.  
  930. int update_time()
  931. {
  932.     long    st,ct;    /* starting and current times */
  933.     char    str[MAXSTRLEN];
  934.     int    previous_num_time_left=num_time_left;
  935.  
  936.     st = time_at_start_of_level;
  937.     ct = time(0);
  938.     num_time_left = num_time_left_at_start - (ct-st-pause_time);
  939.     update_bonus();
  940.     if (num_time_left<=0) {
  941.         print_status("Time is out!  GAME OVER!");
  942.         sprintf(str,"Time: %s",clock_time_str(num_time_left));
  943.         WindPrint(time_left,str);
  944.         XBell(disp,0);
  945.         XBell(disp,0);
  946.         XBell(disp,0);
  947.         return (TIME_IS_OVER);    /* TIME_IS_OVER = TRUE */
  948.     }
  949.     else if (num_time_left != previous_num_time_left) {
  950.         sprintf(str,"Time: %s",clock_time_str(num_time_left));
  951.         WindPrint(time_left,str);
  952.         return (SUCCESS);
  953.     }
  954.     return (SUCCESS);
  955. }
  956.  
  957. int throw_grenade()
  958. {
  959.     char            str[MAXSTRLEN];
  960.     XEvent            event;
  961.     XButtonPressedEvent    *eventbp;
  962.     int            vx,vy;
  963.  
  964.     if (num_grenades_left==0) {
  965.         print_status("No grenades left!");
  966.         XBell(disp,0);
  967.         return (NO_GRENADES_LEFT);
  968.     }
  969.  
  970.     XClearWindow(disp,wind[tgrenade_wind]);
  971.     InvPrintStr(tgrenade_wind,"Throw Grenade");
  972.     print_status("Ready to throw grenade");
  973.  
  974.     /* Get position to throw grenade */
  975.     while (!XPending(disp))
  976.         usleep(XPENDING_UDELAY);
  977.     XNextEvent(disp,&event);
  978.     eventbp = (XButtonPressedEvent *) &event;
  979.     if ((event.type != ButtonPress) || (eventbp->window != field) ||
  980.         (setvxvy(eventbp->x,eventbp->y,&vx,&vy) == OUT_OF_RANGE)) {
  981.         print_status("Click on the position to throw grenade!");
  982.         XBell(disp,0);
  983.         WindPrint(tgrenade_wind,"Throw Grenade");
  984.         return (ILLEGAL_POSITION);
  985.     }
  986.  
  987.     /* check to see if within range */
  988.     if (abs(manvx-vx) + abs(manvy-vy) > GRENADE_DISTANCE) {
  989.         sprintf(str,
  990.             "Can't toss grenade that far!  Max distance is %d.\n",
  991.             GRENADE_DISTANCE);
  992.         print_status(str);
  993.         XBell(disp,0);
  994.         WindPrint(tgrenade_wind,"Throw Grenade");
  995.         return (TOO_FAR_TO_TOSS_GRENADE);
  996.     }
  997.  
  998.     /* passed checks -- toss grenade */
  999.     if (FIELD[vx][vy].c == fc_man) {
  1000.         die(vx,vy);
  1001.         sprintf(str,"Grenades: %d",--num_grenades_left);
  1002.         WindPrint(grenades_left,str);
  1003.         WindPrint(tgrenade_wind,"Throw Grenade");
  1004.         return (DIED);
  1005.     }
  1006.     else if ((FIELD[vx][vy].c != fc_tombstone) && 
  1007.         (FIELD[vx][vy].c != fc_goal)) {
  1008.         explode(vx,vy);
  1009.         show_bomb_status(manvx,manvy);
  1010.         draw(bm_man,manvx,manvy);
  1011.         sprintf(str,"Grenades: %d",--num_grenades_left);
  1012.         WindPrint(grenades_left,str);
  1013.         WindPrint(tgrenade_wind,"Throw Grenade");
  1014.         return (SUCCESS);
  1015.     }
  1016.     else {
  1017.         strcpy(str,"No bomb at that position.  ");
  1018.         strcat(str,"Can't blow up tombstone or goal.");
  1019.         print_status(str);
  1020.         XBell(disp,0);
  1021.         WindPrint(tgrenade_wind,"Throw Grenade");
  1022.         return (CANT_EXPLODE);
  1023.     }
  1024. }
  1025.  
  1026. /* num_adj_squares_marked_bomb: returns num adjacent squares user has marked
  1027.     as being unsafe
  1028. */
  1029. int num_adj_squares_marked_bomb(vx,vy)
  1030. int vx,vy;
  1031. {
  1032.     int    nummarks=0;
  1033.  
  1034.     if (vy>0) {
  1035.         if ((vx>0) && (FIELD[vx-1][vy-1].m==fm_bomb))
  1036.             nummarks++;
  1037.         if (FIELD[vx][vy-1].m==fm_bomb)
  1038.             nummarks++;
  1039.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].m==fm_bomb))
  1040.             nummarks++;
  1041.     }
  1042.     if (vy<NUMROWS-1) {
  1043.         if ((vx>0) && (FIELD[vx-1][vy+1].m==fm_bomb))
  1044.             nummarks++;
  1045.         if (FIELD[vx][vy+1].m==fm_bomb)
  1046.             nummarks++;
  1047.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].m==fm_bomb))
  1048.             nummarks++;
  1049.     }
  1050.     if ((vx>0) && (FIELD[vx-1][vy].m==fm_bomb))
  1051.         nummarks++;
  1052.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].m==fm_bomb))
  1053.         nummarks++;
  1054.  
  1055.     return (nummarks);
  1056. }
  1057.  
  1058. /* num_adj_squares_marked_safe: returns num adjacent squares user has marked
  1059.     as being safe
  1060. */
  1061. int num_adj_squares_marked_safe(vx,vy)
  1062. int vx,vy;
  1063. {
  1064.     int    nummarks=0;
  1065.  
  1066.     if (vy>0) {
  1067.         if ((vx>0) && (FIELD[vx-1][vy-1].m==fm_safe))
  1068.             nummarks++;
  1069.         if (FIELD[vx][vy-1].m==fm_safe)
  1070.             nummarks++;
  1071.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].m==fm_safe))
  1072.             nummarks++;
  1073.     }
  1074.     if (vy<NUMROWS-1) {
  1075.         if ((vx>0) && (FIELD[vx-1][vy+1].m==fm_safe))
  1076.             nummarks++;
  1077.         if (FIELD[vx][vy+1].m==fm_safe)
  1078.             nummarks++;
  1079.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].m==fm_safe))
  1080.             nummarks++;
  1081.     }
  1082.     if ((vx>0) && (FIELD[vx-1][vy].m==fm_safe))
  1083.         nummarks++;
  1084.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].m==fm_safe))
  1085.         nummarks++;
  1086.  
  1087.     return (nummarks);
  1088. }
  1089.  
  1090. /* num_unvisited_adj_squares: returns num adjacent squares user has
  1091.     not yet visited (not marked with fc_trail)
  1092. */
  1093. int num_unvisited_adj_squares(vx,vy)
  1094. int vx,vy;
  1095. {
  1096.     int    num_unvisited=0;
  1097.  
  1098.     if (vy>0) {
  1099.         if ((vx>0) && (FIELD[vx-1][vy-1].c!=fc_trail))
  1100.             num_unvisited++;
  1101.         if (FIELD[vx][vy-1].c!=fc_trail)
  1102.             num_unvisited++;
  1103.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy-1].c!=fc_trail))
  1104.             num_unvisited++;
  1105.     }
  1106.     if (vy<NUMROWS-1) {
  1107.         if ((vx>0) && (FIELD[vx-1][vy+1].c!=fc_trail))
  1108.             num_unvisited++;
  1109.         if (FIELD[vx][vy+1].c!=fc_trail)
  1110.             num_unvisited++;
  1111.         if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy+1].c!=fc_trail))
  1112.             num_unvisited++;
  1113.     }
  1114.     if ((vx>0) && (FIELD[vx-1][vy].c!=fc_trail))
  1115.         num_unvisited++;
  1116.     if ((vx<NUMCOLS-1) && (FIELD[vx+1][vy].c!=fc_trail))
  1117.         num_unvisited++;
  1118.  
  1119.     return (num_unvisited);
  1120. }
  1121.  
  1122. /* do_sanity_check: Make sure user is not losing his mind!
  1123.     If more adjacent squares are marked unsafe than is indicated by the
  1124.     current bomb status, warn the user.
  1125.     If more adjacent squares are marked safe than the number of unvisited
  1126.     squares - the current bomb status, warn the user.
  1127. */
  1128. int do_sanity_check(vx,vy)
  1129. int vx,vy;
  1130. {
  1131.     /* Check first for too many squares marked unsafe */
  1132.     if (num_adj_squares_marked_bomb(vx,vy) > bomb_status(vx,vy)) {
  1133.         print_status("WARNING: too many squares marked UNsafe!");
  1134.         XBell(disp,0);
  1135.         return (CHECK_FAILED);
  1136.     }
  1137.  
  1138.     /* Next check for too many squares marked safe */
  1139.     if (num_adj_squares_marked_safe(vx,vy) > 
  1140.        num_unvisited_adj_squares(vx,vy) - bomb_status(vx,vy)) {
  1141.         print_status("WARNING: too many squares marked safe!");
  1142.         XBell(disp,0);
  1143.         return (CHECK_FAILED);
  1144.     }
  1145.  
  1146.     return (SUCCESS);
  1147. }
  1148.