home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume28 / ldb / part05 / fe_curses.c
Encoding:
C/C++ Source or Header  |  1992-03-15  |  32.5 KB  |  1,090 lines

  1. /*    fe_curses.c        9/5/91
  2.  *
  3.  * Copyright 1991  Perry R. Ross
  4.  *
  5.  * Permission to use, copy, modify, and distribute this software and its
  6.  * documentation without fee is hereby granted, subject to the restrictions
  7.  * detailed in the README file, which is included here by reference.
  8.  * Any other use requires written permission from the author.  This software
  9.  * is distributed "as is" without any warranty, including any implied
  10.  * warranties of merchantability or fitness for a particular purpose.
  11.  * The author shall not be liable for any damages resulting from the
  12.  * use of this software.  By using this software, the user agrees
  13.  * to these terms.
  14.  */
  15.  
  16. #include "ldb.h"
  17.  
  18. /*======================================================================
  19.  * This file is the "curses" front-end for ldb.  It performs all
  20.  * display output and keyboard input in a way that will (hopefully)
  21.  * allow other front-ends to be added later.  These could include
  22.  * MAC's (blech), IBM PC's (double blech), and X, although it must
  23.  * be stated that, as of this writing, ldb is not organized in an
  24.  * event-driven manner, so it will take more work to port to X than
  25.  * just writing fe_motif.c or what have you.  But I'm working on it.
  26.  *
  27.  * All publicly-accessible functions in the front-end begin with Fe.
  28.  * These are the functions that must be written to implement a new
  29.  * front-end.  There are a few private functions in this file, which
  30.  * begin with P.  These are used internally by fe_curses.c and need not
  31.  * be implemented in other front-ends.
  32.  *
  33.  * The front-end is activated by calling FeInitialize.  In addition to
  34.  * performing any required initialization, it is mandatory for FeInitialize
  35.  * to set FeIsActive to non-zero.  The front-end is closed down by
  36.  * calling FeFinishSession, which must set FeIsActive to 0.  No calls
  37.  * to any Fe functions may be made while FeIsActive is 0.
  38.  *======================================================================
  39.  */
  40.  
  41. PRIVATE char PGetChr();
  42. PRIVATE PGetString();
  43. PRIVATE PDrawComment();
  44.  
  45. /* VAX C doesn't have tgetstr, but if you're using vax-c,    */
  46. /* you're probably using a DEC terminal anyway.            */
  47. #ifdef vaxc
  48. #define PClearScreen() fputs("\33[H\33[2J",stdout);fflush(stdout)
  49. #else
  50. PRIVATE PClearScreen();
  51. #endif
  52.  
  53.  
  54. /*----------------------------------------------------------------------
  55.  *    FeInitialize -- initialize the front end
  56.  *
  57.  * This function initializes the curses package, turns off echo,
  58.  * turns on cbreak mode (to allow reading one character at a time),
  59.  * turns off mapping return to newline, and sets the FeIsActive flag.
  60.  *----------------------------------------------------------------------
  61.  */
  62.  
  63. FeInitialize()
  64. {
  65.  
  66. initscr();
  67. noecho();
  68. cbreak();
  69. nonl();
  70. FeIsActive = 1;
  71. }
  72.  
  73.  
  74. /*----------------------------------------------------------------------
  75.  *    FeFinishSession -- shut down the front end
  76.  *
  77.  * This function moves the cursor to the bottom of the screen, clears
  78.  * the bottom line, closes down the curses package, and clears the
  79.  * FeIsActive flag.
  80.  *----------------------------------------------------------------------
  81.  */
  82.  
  83. FeFinishSession()
  84. {
  85.  
  86. if (FeIsActive) {
  87.     move(23,0);
  88.     clrtoeol();
  89.     refresh();
  90.     endwin();
  91.     FeIsActive = 0;
  92.     }
  93. }
  94.  
  95.  
  96. /*----------------------------------------------------------------------
  97.  *    FeDrawScreen -- draw the constant parts of the screen
  98.  *
  99.  * This function draws the parts of the screen that don't change for
  100.  * each game.  This includes the board outline and a few other
  101.  * miscellaneous things.
  102.  *----------------------------------------------------------------------
  103.  */
  104.  
  105. FeDrawScreen()
  106. {
  107. static char horz[] = "_____________________________________________________";
  108. int i;
  109.  
  110. clear();
  111. mvaddstr(2,5,horz);
  112. mvaddstr(17,5,horz);
  113. for (i = 3; i < 18; i++) {
  114.     mvaddch(i,5,'|');
  115.     mvaddch(i,29,'|');
  116.     mvaddch(i,33,'|');
  117.     mvaddch(i,57,'|');
  118.     }
  119. mvaddstr(10,30,"BAR");
  120. mvaddstr(3,61,"----------------");
  121. mvaddstr(4,63,"Roll  Move");
  122. mvaddstr(12,61,"----------------");
  123. mvaddstr(13,63,"Roll  Move");
  124. mvaddstr(18,63,"-----[ ]------");
  125. for (i = 18; i < 24; i++)
  126.     mvaddch(i,62,'|');
  127. mvaddstr(18,0,"Messages:");
  128. mvaddstr(19,0,"Sent:");
  129. mvaddstr(21,0,"Rcvd:");
  130. refresh();
  131. }
  132.  
  133.  
  134.  
  135. /*----------------------------------------------------------------------
  136.  *    FeDrawGame -- draw all items associated with a game
  137.  *
  138.  * This function displays all information related to a specific game.
  139.  * This includes the point labels, move blocks, cube; shoot, just
  140.  * about everything you can think of.
  141.  *----------------------------------------------------------------------
  142.  */
  143.  
  144. FeDrawGame(g)
  145. struct game *g;
  146. {
  147. int i, p, r1, r2, p1, p2;
  148. char blots[12], tmp[60], *n;
  149. static char pts1[] = " 1   2   3   4   5   6 |   | 7   8   9  10  11  12";
  150. static char pts2[] = "24  23  22  21  20  19 |   |18  17  16  15  14  13";
  151.  
  152. move(0,0);
  153. clrtoeol();
  154. move(0,0);
  155. n = (g->opname != NULL) ? g->opname : "UNKNOWN";
  156. printw("Playing: %s (%s)",g->opaddr,n);        /* who am I playing? */
  157. if (g->flags & F_INVERT) {        /* board is inverted?  */
  158.     mvaddstr(16,6,pts2);        /* draw inverted point labels */
  159.     mvaddstr(4,6,pts1);
  160.     r1 = 11;            /* remember which move block to use */
  161.     r2 = 2;
  162.     p = 1;
  163.     }
  164. else {
  165.     mvaddstr(4,6,pts2);        /* draw normal point labels */
  166.     mvaddstr(16,6,pts1);
  167.     r1 = 2;                /* remember which move block to use */
  168.     r2 = 11;
  169.     p = 0;
  170.     }
  171. if (g->mydir > 0) {        /* I'm playing up, switch move blocks */
  172.     i = r1;
  173.     r1 = r2;
  174.     r2 = i;
  175.     p = 1 - p;
  176.     }
  177. move(r1,61);
  178. clrtoeol();                /* label the move blocks */
  179. sprintf(tmp,"Opponent (%s)",colorname(g->opcolor));
  180. i = 61 + ( (16 - strlen(tmp)) >> 1 );    /* center the string */
  181. mvaddstr(r1,i,tmp);
  182. move(r2,61);
  183. clrtoeol();
  184. sprintf(tmp,"You (%s)",colorname(g->mycolor));
  185. i = 61 + ( (16 - strlen(tmp)) >> 1 );
  186. mvaddstr(r2,i,tmp);
  187.  
  188. FeLabelBoard(bdlabels[g->curbd]);    /* which board am I looking at? */
  189. switch (g->curbd) {            /* which board should I draw? */
  190. case BD_BEFOP:
  191.     FeDrawBoard(g->opbd,g->opmvs,g->opdir,0,g->flags & F_INVERT);
  192.     break;
  193. case BD_AFTOP:
  194.     FeDrawBoard(g->mybd,g->opmvs,g->opdir,1,g->flags & F_INVERT);
  195.     break;
  196. case BD_CUR:
  197.     FeDrawBoard(g->board,NULL,g->mydir,0,g->flags & F_INVERT);
  198.     break;
  199.     }
  200. mvaddch(5,59,'|');            /* draw those little arrows */
  201. mvaddch(14,59,'|');            /* that tell us which direction */
  202. mvaddch(15,59,'|');            /* we are going */
  203. if (p == 0) {
  204.     mvaddstr(4,1,"-->");
  205.     mvaddstr(4,58,"--");
  206.     mvaddch(6,59,'V');
  207.     mvaddstr(16,58,"<-");
  208.     mvaddstr(16,1,"<--");
  209.     mvaddch(14,58,' ');
  210.     mvaddch(14,60,' ');
  211.     mvaddch(13,59,' ');
  212.     }
  213. else {
  214.     mvaddstr(4,1,"<--");
  215.     mvaddstr(4,58,"<-");
  216.     mvaddch(6,59,'|');
  217.     mvaddstr(16,58,"--");
  218.     mvaddstr(16,1,"-->");
  219.     mvaddch(14,58,'/');
  220.     mvaddch(14,60,'\\');
  221.     mvaddch(13,59,'.');
  222.     }
  223. *blots = '\0';                /* did any of our blots get hit? */
  224. for (i = 0, p = 0; i < 4; i++) {
  225.     FeDrawMove(g,0,i);        /* draw my moves */
  226.     FeDrawMove(g,1,i);        /* draw opponent's moves */
  227.     if (g->blot[i] > 0) {
  228.         strcat(blots," ");    /* add a blot to the list */
  229.         sprintf(tmp,"%d",g->blot[i]);
  230.         strcat(blots,tmp);
  231.         p++;
  232.         }
  233.     }
  234. FeDrawCube(g);        /* draw the current game value */
  235. PDrawComment(0, g);        /* draw my old comment */
  236. PDrawComment(1, g);        /* draw opponent's comment */
  237. if (g->state == ST_MYACCEPT)
  238.     strcpy(tmp,"Opponent has doubled.");
  239. else if (g->state == ST_GAMEOVER) {    /* game is over, find out why */
  240.     switch (g->term) {
  241.     case T_IWIN:        /* I won, check for gammon/backgammon */
  242.         if (g->board[OFFPT(g->opdir)].qty == 0) {
  243.             p1 = (g->mydir > 0) ? 19 : 0;    /* check my inner tbl*/
  244.             p2 = (g->mydir > 0) ? 25 : 6;    /* for op's pieces */
  245.             if (addpcs(g->board,g->opcolor,p1,p2) > 0)
  246.                 sprintf(tmp,"Backgammon!  You win %d points.",
  247.                     3*g->gameval);
  248.             else
  249.                 sprintf(tmp,"Gammon!  You win %d points.",
  250.                     2*g->gameval);
  251.             }
  252.         else
  253.             sprintf(tmp,"You win!  Game value was %d point%s.",
  254.                 g->gameval,(g->gameval == 1) ? "" : "s");
  255.         break;
  256.     case T_ILOSE:        /* I lost, check for gammon/backgammon */
  257.         if (g->board[OFFPT(g->mydir)].qty == 0) {
  258.             p1 = (g->opdir > 0) ? 19 : 0;/* check op's inner tbl*/
  259.             p2 = (g->opdir > 0) ? 25 : 6;    /* for my pieces */
  260.             if (addpcs(g->board,g->mycolor,p1,p2) > 0)
  261.                 sprintf(tmp,"Backgammon!  You lose %d points.",
  262.                     3*g->gameval);
  263.             else
  264.                 sprintf(tmp,"Gammon!  You lose %d points.",
  265.                     2*g->gameval);
  266.             }
  267.         else
  268.             sprintf(tmp,"You lose.  Game value was %d point%s.",
  269.                 g->gameval,(g->gameval == 1) ? "" : "s");
  270.         break;
  271.     case T_ICONCEDE:            /* I wimped out */
  272.         sprintf(tmp,"You conceded.  You lose %d point%s.",
  273.             g->gameval,(g->gameval == 1) ? "" : "s");
  274.         break;
  275.     case T_OPCONCEDE:            /* Opponent wimped out */
  276.         sprintf(tmp,"Opponent conceded.  You win %d point%s.",
  277.             g->gameval,(g->gameval == 1) ? "" : "s");
  278.         break;
  279.     case T_IDECLINE:            /* I declined the double */
  280.         sprintf(tmp,"Double declined.  You lose %d point%s.",
  281.             g->gameval,(g->gameval == 1) ? "" : "s");
  282.         break;
  283.     case T_OPDECLINE:        /* Opponent declined my double */
  284.         sprintf(tmp,"Double declined.  You win %d point%s.",
  285.             g->gameval,(g->gameval == 1) ? "" : "s");
  286.         break;
  287.         }
  288.     }
  289. else if (*blots)
  290.     sprintf(tmp,"Blot%s hit:%s",(p == 1) ? "" : "s",blots);
  291. else
  292.     *tmp = '\0';
  293. FeStatusLine(tmp);
  294. FeMessage(g->dispmsg);        /* put message (if any) on message line */
  295. if (g->dispmsg != NULL) {    /* if there was a message, it has been */
  296.     free(g->dispmsg);    /* displayed, so it can thrown away */
  297.     g->dispmsg = NULL;
  298.     }
  299. refresh();
  300. }
  301.  
  302.  
  303. /*----------------------------------------------------------------------
  304.  *    FeDrawPoint -- draw all pieces on a point
  305.  *
  306.  * This function redraws all 15 slots on a point.  It is passed a
  307.  * board image and the index of the point to draw, from which it
  308.  * extracts the number of pieces on that point and the character used
  309.  * to represent pieces.  It will draw as many of these pieces as
  310.  * exist on the point, and will draw blanks over the remaining slots
  311.  * to erase any pieces that may have existed before.
  312.  * If the nh argument is greater than 0, it specifies how many of
  313.  * the pieces drawn should be highlighted.  This is used to highlight
  314.  * pieces moved by the opponent.
  315.  *----------------------------------------------------------------------
  316.  */
  317.  
  318. FeDrawPoint(b,pt,nh,inv)
  319. board b;        /* the board array */
  320. int pt;            /* which point are we to draw */
  321. int nh;            /* how many pieces should be highlighted */
  322. int inv;        /* is the board inverted? */
  323. {
  324. static int cols[BOARDSIZE] = {  31,7,11,15,19,23,27,35,39,43,47,51,55,
  325.                 55,51,47,43,39,35,27,23,19,15,11,7,31,2,2};
  326. int sr, r;        /* the row we are at */
  327. int c;        /* the column we are at */
  328. int d;        /* which direction does the column grow 1/-1 */
  329. int i;        /* counter */
  330. char x;        /* char to draw piece with */
  331. int nn;        /* number of normal pieces */
  332.  
  333. if ( (pt > 12) && (pt != DOWNOFF)) {
  334.     sr = inv ? 15 : 5;    /* starting row is 5 (unless inverted) */
  335.     d = inv ? -1 : 1;    /* direction is down (unless inverted) */
  336.     }
  337. else {
  338.     sr = inv ? 5 : 15;    /* starting row is 15 (unless inverted) */
  339.     d = inv ? 1 : -1;    /* direction is up (unless inverted) */
  340.     }
  341. c = cols[pt];
  342. x = b[pt].color;        /* char to draw piece with */
  343. if (nh < 0)
  344.     nh = 0;
  345. else if (nh > b[pt].qty)
  346.     nh = b[pt].qty;
  347. nn = b[pt].qty - nh;        /* how many normal pcs */
  348. r = sr;
  349. for (i = 0; i < 15; i++) {        /* draw all 15 slots on this point */
  350.     if (nn <= 0) {        /* no more normal pieces */
  351.         if (nh <= 0)    /* and no highlighted pieces */
  352.             x = ' ';    /* so draw blanks */
  353.         else {
  354.             standout();    /* go into highlight mode */
  355.             nh--;
  356.             }
  357.         }
  358.     else
  359.         nn--;
  360.     mvaddch(r,c,x);            /* draw this piece */
  361.     standend();            /* make sure we're not highlighting */
  362.     if (i == 4) {
  363.         r = sr;            /* reset row */
  364.         c--;            /* use col to left of first row */
  365.         }
  366.     else if (i == 9) {
  367.         r = sr;            /* reset row */
  368.         c += 2;            /* use col to right of first row */
  369.         }
  370.     else
  371.         r += d;            /* bump row number */
  372.     }
  373. }
  374.  
  375.  
  376. /*----------------------------------------------------------------------
  377.  *    FeDrawMove -- draw a line in a move block
  378.  *
  379.  * This function draws one line in a move block.  This consists of the
  380.  * value of the roll, the starting and ending position of the piece
  381.  * moved, and an asterisk if the move was from the opponent and hit
  382.  * one of our blots.
  383.  *----------------------------------------------------------------------
  384.  */
  385.  
  386. FeDrawMove(g,who,mn)
  387. struct game *g;            /* the game structure */
  388. int who;            /* 0 = opponent, 1 = me */
  389. int mn;                /* which move to draw */
  390. {
  391. int p, r, d;
  392. struct mv *m;
  393.  
  394. d = who ? g->mydir : g->opdir;        /* this move upbound or downbound? */
  395. p = (d > 0);                /* upper or lower block? */
  396. if (g->flags & F_INVERT)        /* inverted board */
  397.     p = !p;                /* switch move blocks */
  398. r = mn + (p ? 5 : 14);            /* figure out the row number */
  399. m = who ? &g->mvs[mn] : &g->opmvs[mn];    /* find the move structure */
  400. move(r,64);
  401. clrtoeol();                /* clear the old move */
  402. if (m->roll > 0) {
  403.     move(r,64);
  404.     printw("%d",m->roll);        /* draw the roll */
  405.     move(r,69);
  406.     if (m->pt < 0) {        /* if it is unused, say so */
  407.         addstr("UNUSED");
  408.         return;
  409.         }
  410.     if ( ( (p = m->pt) == UPBAR) || (m->pt == DOWNBAR) ) {
  411.         p = BARPT(d);        /* if coming off bar, say so */
  412.         printw("BAR-");
  413.         }
  414.     else
  415.         printw("%d-",m->pt);    /* draw starting point */
  416.     if ( ( (p += d*m->roll) <= 0) || (p >= 25) )
  417.         printw("OFF");        /* if bearing off, say so */
  418.     else
  419.         printw("%d",p);        /* draw ending point */
  420.     if ( (who == 0) && g->blot[mn])    /* if opponent move hit a blot */
  421.         mvaddch(r,76,'*');    /* mark it */
  422.     }
  423. }
  424.  
  425.  
  426. /*----------------------------------------------------------------------
  427.  *    FeDrawBoard -- draw all points on a board
  428.  *
  429.  * This is a convenience function that calls FeDrawPoint for all
  430.  * points on a board.  It takes as an argument an array of moves,
  431.  * as well as an argument that determines whether DrawPoint should be
  432.  * instructed to highlight the source of those moves, the destination,
  433.  * or nothing.
  434.  *----------------------------------------------------------------------
  435.  */
  436.  
  437. FeDrawBoard(b,mvs,dir,sd,inv)
  438. board b;            /* board image */
  439. struct mv mvs[4];        /* moves to highlight (NULL = none) */
  440. int dir;            /* direction */
  441. int sd;                /* 0=highlight source, 1=dest */
  442. int inv;            /* is the board inverted? */
  443. {
  444. int i, s, e;
  445. static char hcnt[BOARDSIZE];    /* number of pieces to highlight */
  446.  
  447. for (i = 0; i < BOARDSIZE; i++)
  448.     hcnt[i] = 0;        /* init to no highlight */
  449. if (mvs != NULL) {    /* find all points that should be highlighted */
  450.     for (i = 0; i < 4; i++) {
  451.         if ( (mvs[i].roll <= 0) || ( (s = mvs[i].pt) < 0) )
  452.             continue;    /* this move is unused */
  453.         if ( (s < 1) || (s > 24) )    /* if coming off bar */
  454.             s = BARPT(dir);        /* use correct bar point */
  455.         e = s + dir*mvs[i].roll;    /* add in the roll used */
  456.         if ( (e < 1) || (e > 24) )    /* off the board */
  457.             e = OFFPT(dir);    /* use correct off point */
  458.         if (sd > 0) {            /* we are showing dest */
  459.             hcnt[e]++;        /* inc destination count */
  460.             hcnt[s]--;        /* handle continued moves */
  461.             }
  462.         else {                /* we are showing start */
  463.             hcnt[s]++;        /* inc start count */
  464.             hcnt[e]--;        /* handle continued moves */
  465.             }
  466.         }
  467.     }
  468. for (i = 0; i < BOARDSIZE; i++)        /* draw each point */
  469.     FeDrawPoint(b,i,hcnt[i],inv);
  470. }
  471.  
  472.  
  473. /*----------------------------------------------------------------------
  474.  *    FeLabelBoard -- center a string above the board.
  475.  *
  476.  * This function is used to label which board the user is seeing.
  477.  *----------------------------------------------------------------------
  478.  */
  479.  
  480. FeLabelBoard(s)
  481. char *s;
  482. {
  483. int i;
  484.  
  485. if (strlen(s) > 50)        /* 50 chars is all the room we have */
  486.     s[50] = '\0';        /* chop it off */
  487. mvaddstr(1,5,BLANKS(50));    /* clear old contents */
  488. if ( (i = 31 - strlen(s)/2) < 0)
  489.     i = 0;
  490. mvaddstr(1,i,s);        /* draw the string */
  491. }
  492.  
  493.  
  494. /*----------------------------------------------------------------------
  495.  *    FeGetPoint -- read a point number from the user
  496.  *
  497.  * This function prompts the user for a point number.  The user types
  498.  * the input on the line of the move block corresponding to the
  499.  * roll being used.  Normally, the input is a number between 1 and 24.  
  500.  * Numbers that do not use 2 digits (i.e. 1-9) must have a non-digit
  501.  * character typed after them; alternatively, the user may enter them
  502.  * with a leading 0.  There are a number of special characters that
  503.  * are also recognized:
  504.  *    char            return value
  505.  *    ---------------------------------------------------------------
  506.  *    space            the "spdflt" argument.
  507.  *    return/linefeed        the "crdflt" argument.
  508.  *    DEL/ESC/BS        cancel move.
  509.  *    b/B            the user's bar point.
  510.  *    p/P            the point from which a piece would have
  511.  *                to be moved to land on spdflt.
  512.  *    o/O            The point from which the selected roll
  513.  *                could be used to bear off.  If that point
  514.  *                is unoccupied, the next lower occupied
  515.  *                point is returned.
  516.  *----------------------------------------------------------------------
  517.  */
  518.  
  519. FeGetPoint(g,r,crdflt,spdflt)
  520. struct game *g;                /* game structure */
  521. int r;                    /* which row in move block */
  522. int crdflt;                /* what to return for cr/nl */
  523. int spdflt;                /* what to return for space */
  524. {
  525. int n, row;
  526. char buf[4];
  527.  
  528. row = r;
  529. if (g->flags & F_INVERT)        /* which move block do I use? */
  530.     row += (g->mydir < 0) ? 5 : 14;    /* inverted board */
  531. else
  532.     row += (g->mydir > 0) ? 5 : 14;    /* normal board */
  533. move(row,69);
  534. clrtoeol();
  535. move(row,69);
  536. refresh();
  537. *buf = PGetChr(0);
  538. if ( (*buf == '\n') || (*buf == '\r') )    /* return means repeat move */
  539.     return(crdflt);
  540. if (*buf == ' ')            /* space means continue move */
  541.     return(spdflt);
  542. if ( (*buf == '\177') || (*buf == '\033') || (*buf == '\b') )
  543.     return(-1);            /* DEL/ESC/BS means cancel move */
  544. if ( (*buf == 'b') || (*buf == 'B') )    /* bar */
  545.     return(BARPT(g->mydir));
  546. if ( (*buf == 'p') || (*buf == 'P') ) {    /* P means spdflt - roll*dir */
  547.     n = spdflt - g->mvs[r].roll*g->mydir;    /* make point */
  548.     if ( (n < 1) || (n > 24) )        /* not on board */
  549.         n = 99;                /* force invalid point */
  550.     return(n);
  551.     }
  552. if ( (*buf == 'o') || (*buf == 'O') ) {    /* O means bear a piece off */
  553.     n = ( (g->mydir > 0) ? 25 : 0 ) - g->mydir*g->mvs[r].roll;
  554.     while ( (n > 0) && (n <= 24) &&
  555.        ( (g->board[n].qty <= 0) || (g->board[n].color != g->mycolor)) )
  556.         n += g->mydir;
  557.     if ( (n < 1) || (n > 24) )    /* no piece found */
  558.         n = 99;            /* force invalid point */
  559.     return(n);
  560.     }
  561. if ( ! isdigit(*buf))
  562.     return(99);            /* force invalid point message */
  563. addch(*buf);                /* echo the char */
  564. refresh();
  565. buf[1] = PGetChr(0);
  566. buf[2] = '\0';                /* null terminate */
  567. if ( ((n = atoi(buf)) == UPBAR) || (n == DOWNBAR) )
  568.     return(BARPT(g->mydir));
  569. return(n);
  570. }
  571.  
  572.  
  573. /*----------------------------------------------------------------------
  574.  *    PGetChr -- get a single character
  575.  *
  576.  * This function gets one character from the user and returns it.
  577.  * If the "e" argument is non-zero, the character is echoed at the
  578.  * current cursor position.  The ^L and ^R characters are intercepted
  579.  * and cause the screen to be redrawn without returning from PGetChr.
  580.  *----------------------------------------------------------------------
  581.  */
  582.  
  583. PRIVATE char PGetChr(e)
  584. int e;
  585. {
  586. char c;
  587. int y, x;
  588.  
  589. loop:
  590. if ( ((c = getch() & 0x7f) == 0x0c) || (c == 0x12) ) {    /* ^L or ^R? */
  591.     wrefresh(curscr);    /* repaint current screen */
  592.     goto loop;        /* and get another char */
  593.     }
  594. if (c == rc.superkey) {            /* uh oh, we're busted */
  595.     getyx(stdscr,y,x);        /* save old cursor postition */
  596.     PClearScreen();            /* get the screen cleared fast */
  597.     nl();                /* set tty back to normal */
  598.     echo();
  599.     system(rc.supercmd);        /* run the supervisor command */
  600.     nonl();                /* ok, we're safe again */
  601.     noecho();
  602.     PClearScreen();            /* get the screen cleared fast */
  603.     wrefresh(curscr);        /* redisplay the game */
  604.     move(y,x);            /* put cursor back where it was */
  605.     refresh();
  606.     goto loop;            /* and get another character */
  607.     }
  608. if (e && isprint(c)) {        /* echo char? */
  609.     addch(c);        /* yup */
  610.     refresh();
  611.     }
  612. return(c);
  613. }
  614.  
  615.  
  616. /*----------------------------------------------------------------------
  617.  *    PClearScreen -- clear the screen somehow
  618.  *
  619.  * This function clears the physical display without affecting what
  620.  * the curses package thinks is there.  If the "cl" (clear screen)
  621.  * capability is defined, it uses that.  If that fails, it tries
  622.  * to move to 0,0 and use the "cd" (clear to end of display).
  623.  * Failing that, it goes to the bottom of the screen and scrolls
  624.  * it 24 times.
  625.  *----------------------------------------------------------------------
  626.  */
  627.  
  628. #ifndef vaxc
  629. PRIVATE PClearScreen()
  630. {
  631. char *s, *x, buf[80];
  632.  
  633. x = buf;
  634. if ( (s = tgetstr("cl",&x)) == NULL) {        /* no clear screen */
  635.     if ( (s = tgetstr("cd",&x)) != NULL) {    /* do we have clr to end? */
  636.         move(0,0);        /* yup, use it */
  637.         refresh();
  638.         fputs(s,stdout);
  639.         }
  640.     else {            /* well, do it the hard way */
  641.         move(23,0);
  642.         refresh();
  643.         printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
  644.         }
  645.     }
  646. else
  647.     fputs(s,stdout);    /* send clear screen */
  648. fflush(stdout);            /* git along, li'l characters */
  649. }
  650. #endif
  651.  
  652.  
  653. /*----------------------------------------------------------------------
  654.  *    FeGetComment -- prompt for a comment to send along with a move
  655.  *
  656.  * This function allows the user to enter a 2-line comment.  If the
  657.  * user types a non-empty string, it is stored in the mycmt/mycmt2
  658.  * fields of the game structure passed as an argument.  The previous comment,
  659.  * if any, is discarded, as is the last comment received from the opponent.
  660.  *----------------------------------------------------------------------
  661.  */
  662.  
  663. FeGetComment(g)
  664. struct game *g;
  665. {
  666. char buf[120];
  667. char *lptrs[2];
  668. int n;
  669.  
  670. if (g->mycmt != NULL)        /* throw away old comment */
  671.     free(g->mycmt);
  672. if (g->mycmt2 != NULL)
  673.     free(g->mycmt2);
  674. if (g->opcmt != NULL) {        /* throw away opponent's old comment */
  675.     free(g->opcmt);
  676.     g->opcmt = NULL;
  677.     }
  678. if (g->opcmt2 != NULL) {
  679.     free(g->opcmt2);
  680.     g->opcmt2 = NULL;
  681.     }
  682. n = PGetString(buf,56,lptrs,2,19,6);    /* get new comment */
  683. g->mycmt = NULL;    /* mark comment as empty */
  684. g->mycmt2 = NULL;
  685. if (n > 0)
  686.     g->mycmt = save(lptrs[0]);    /* save first line */
  687. if (n > 1)
  688.     g->mycmt2 = save(lptrs[1]);    /* save second line */
  689. }
  690.  
  691.  
  692.  
  693. /*----------------------------------------------------------------------
  694.  *    PGetString -- read a multi-line string from the user
  695.  *
  696.  * This function allows the user to enter multiple lines of a fixed maximum
  697.  * length.  The normal line editing characters are recognized and
  698.  * processed.  These include:
  699.  *    DEL/BS        The character before the cursor is deleted.
  700.  *    ^X/^U        The entire line is erased.
  701.  *    ^L/^R        The screen is redrawn.
  702.  *    ^W/^B        The word before the cursor is erased.
  703.  * Typing past the end of a line automatically causes a word-
  704.  * wrap to the next line.  Words are delimited by spaces.  Typing
  705.  * a carriage return or line feed on the last line, or on an empty
  706.  * line, terminates PGetString; otherwise, it moves to the next line.
  707.  * ESC terminates PGetString regardless of the cursor position.
  708.  * Backspacing from the beginning of one line to the end of the previous
  709.  * is allowed.
  710.  *----------------------------------------------------------------------
  711.  */
  712.  
  713. PRIVATE PGetString(buf,len,lines,nls,y,x)
  714. char *buf, *lines[];
  715. int len, nls, y, x;
  716. {
  717. char c, *bp, *p;
  718. int cl, l;
  719.  
  720. for (cl = 0; cl < nls; cl++) {
  721.     lines[cl] = NULL;        /* clear line pointers */
  722.     mvaddstr(y+cl,x,BLANKS(len));    /* clear line */
  723.     }
  724. cl = 0;                    /* current line = 0 */
  725. l = 0;                /* length of current line */
  726. move(y,x);            /* go to first location in field */
  727. bp = buf;            /* ptr to next storage location */
  728. lines[0] = buf;            /* init first line pointer */
  729. refresh();
  730. while (1) {
  731.     switch (c = PGetChr(0)) {
  732.     case '\177':        /* DEL */
  733.     case '\b':        /* BS */
  734.         if (l <= 0) {    /* at beginning of line */
  735.             if (cl <= 0)    /* on first line */
  736.                 break;        /* nothing to delete */
  737.             cl--;        /* back up one line */
  738.             *--bp = '\0';    /* back up buffer pointer */
  739.             l = strlen(lines[cl]);    /* load line length */
  740.             move(y+cl,x+l);    /* move cursor to end of prev line */
  741.             }
  742.         else {
  743.             bp--;        /* back up buffer pointer */
  744.             l--;        /* decrement length */
  745.             move(y+cl,x+l);
  746.             addch(' ');    /* erase the char from the screen */
  747.             move(y+cl,x+l);
  748.             }
  749.         refresh();
  750.         break;
  751.     case '\2':        /* ^B -- erase previous character */
  752.     case '\27':        /* ^W */
  753.         if (l <= 0) {        /* beginning of line */
  754.             if (cl <= 0)    /* on first line */
  755.                 break;        /* nothing to delete */
  756.             cl--;        /* back up one line */
  757.             *--bp = '\0';    /* back up buffer pointer */
  758.             l = strlen(lines[cl]);    /* load line length */
  759.             }
  760.         while (l > 0) {        /* skip leading spaces, if any */
  761.             if (*--bp != ' ') {
  762.                 bp++;
  763.                 break;
  764.                 }
  765.             l--;
  766.             *bp = '\0';
  767.             }
  768.         while (l > 0) {        /* delete to last space */
  769.             if (*--bp == ' ') {
  770.                 bp++;
  771.                 break;
  772.                 }
  773.             *bp = '\0';
  774.             l--;
  775.             mvaddch(y+cl,x+l,' ');
  776.             }
  777.         move(y+cl,x+l);
  778.         refresh();
  779.         break;
  780.     case '\33':        /* ESC */
  781.         *bp++ = '\0';        /* terminate the string */
  782.         if (l <= 0)        /* empty line */
  783.             return(cl);    /* don't include it in #lines */
  784.         return(cl+1);    /* return number of lines */
  785.     case '\r':        /* CR */
  786.     case '\n':        /* NL */
  787.         *bp++ = '\0';    /* terminate the string */
  788.         if (l <= 0)        /* empty line */
  789.             return(cl);    /* don't include it in #lines */
  790.         if (cl >= nls-1)        /* last line */
  791.             return(cl+1);    /* return number of lines */
  792.         lines[++cl] = bp;    /* go to next line */
  793.         l = 0;
  794.         move(y+cl,x);
  795.         refresh();
  796.         break;
  797.     case '\30':        /* ^X -- erase entire line & goto prev line */
  798.     case '\25':        /* ^U */
  799.         mvaddstr(y+cl,x,BLANKS(len));
  800.         if (cl > 0) {    /* back up one line */
  801.             bp = lines[cl--] - 1;
  802.             l = strlen(lines[cl]);
  803.             }
  804.         else {        /* already on top line, go to beginning */
  805.             bp = buf;
  806.             l = 0;
  807.             }
  808.         move(y+cl,x+l);
  809.         refresh();
  810.         break;
  811.     case '\t':            /* convert tab to a space */
  812.         c = ' ';
  813.         /* fall through */
  814.     default:
  815.         if (iscntrl(c)){        /* bad char */
  816.             fputc('\7',stderr);    /* beep */
  817.             fflush(stderr);
  818.             break;            /* & ignore character */
  819.             }
  820.         if (l >= len) {        /* typed up to end of line */
  821.             if (cl >= nls-1) {    /* last line, can't go on */
  822.                 fputc('\7',stderr);    /* beep */
  823.                 fflush(stderr);
  824.                 break;        /* & ignore character */
  825.                 }
  826.             *bp++ = c;        /* store rcvd char */
  827.             for (p = bp-1; (l > 0) && (! isspace(*p)); p--, l--);
  828.             if ( (p <= buf) || (*p == '\0') ) {
  829.                 *bp++ = '\0';
  830.                 lines[++cl] = bp;    /* didn't find word */
  831.                 l = 0;
  832.                 }
  833.             else {
  834.                 *p++ = '\0';    /* terminate previous line */
  835.                 mvaddstr(y+cl,x,BLANKS(len));    /* redraw */
  836.                 mvaddstr(y+cl,x,lines[cl]);    /* w/o word */
  837.                 lines[++cl] = p;    /* start new line */
  838.                 *bp = '\0';        /* terminate word */
  839.                 l = strlen(p);        /* set line len */
  840.                 mvaddstr(y+cl,x,p);    /* draw word */
  841.                 }
  842.             move(y+cl,x+l);
  843.             }
  844.         else {
  845.             *bp++ = c;        /* put char in string */
  846.             l++;            /* bump length */
  847.             addch(c);        /* echo char to screen */
  848.             }
  849.         refresh();
  850.         break;
  851.         }
  852.     }
  853. }
  854.  
  855.  
  856. /*----------------------------------------------------------------------
  857.  *    FeDrawMenu -- draw menu choices in menu box
  858.  *
  859.  * This function takes an array of strings, terminated by a NULL
  860.  * pointer, and writes each string into successive lines of the
  861.  * menu box in the bottom right corner of the screen.  If there are
  862.  * more strings than will fit in the box, the extra strings are
  863.  * ignored.  If NULL is passed as the menu array, the menu box
  864.  * is cleared.
  865.  *----------------------------------------------------------------------
  866.  */
  867.  
  868. FeDrawMenu(m)
  869. char *m[];
  870. {
  871. int i;
  872.  
  873. for (i = 0; i < 5; i++) {        /* clear all lines in menu box */
  874.     move(19+i,63);
  875.     clrtoeol();
  876.     }
  877. if (m == NULL)                /* no menu to display */
  878.     return;
  879. for (i = 0; (m[i] != NULL) && (i < 5); i++) {
  880.     if (strlen(m[i]) > 15)        /* menu string is too long */
  881.         m[15] = '\0';        /* so shorten it */
  882.     mvaddstr(19+i,64,m[i]);        /* put string in menu area */
  883.     }
  884. refresh();
  885. }
  886.  
  887.  
  888. /*----------------------------------------------------------------------
  889.  *    FeMenu -- get menu choice from user
  890.  *
  891.  * This function accepts a menu choice from the user.  The menu choices
  892.  * are passed as an array of strings, terminated by a NULL pointer.
  893.  * Users select one of these strings by typing the first letter of the
  894.  * string, thus the first letter of the strings must be unique.
  895.  *
  896.  * FeMenu also handles two special cases:
  897.  *    1. The user types a number between 1 and 6 (a roll)
  898.  *    2. The user types some character that the caller wishes
  899.  *       to handle directly.
  900.  * If a roll entry is valid, the caller may pass up to two different
  901.  * rolls that are valid in the r1 and r2 arguments.  Any characters
  902.  * the caller wishes to handle are passed as a string in the
  903.  * "extra" argument.  These typically include space and newline.
  904.  *----------------------------------------------------------------------
  905.  */
  906.  
  907. char FeMenu(m,r1,r2,extra)
  908. char *m[];            /* array of menu choices */
  909. int r1, r2;            /* rolls (pass 0 if no roll valid) */
  910. char *extra;            /* chars that caller wants to handle */
  911. {
  912. int i;
  913. char c, x;
  914.  
  915. while (1) {
  916.     mvaddch(18,69,' ');
  917.     move(18,69);            /* put cursor in its little box */
  918.     refresh();
  919.     c = PGetChr(1);            /* get a character */
  920.     FeMessage(NULL);        /* clear message line */
  921.     if ( (extra != NULL) && (strchr(extra,c) != NULL) )
  922.         return(c);        /* these chars are handled by caller */
  923.     if ( (c >= '1') && (c <= '6') ) {    /* handle digits differently */
  924.         if (r1 <= 0) {
  925.             FeMessage("Roll not valid here.");
  926.             continue;
  927.             }
  928.         x = c - '0';        /* convert to number */
  929.         if ( (x == r1) || (x == r2) )    /* is it a valid roll? */
  930.             return(c);        /* yup, return it */
  931.         FeMessage("No such roll.");
  932.         continue;
  933.         }
  934.     if (islower(c))            /* ignore case */
  935.         c = toupper(c);
  936.     for (i = 0; m[i] != NULL; i++) {    /* search menu strings */
  937.         x = *m[i];
  938.         if (islower(x))            /* ignore case */
  939.             x = toupper(x);
  940.         if (c == x)            /* found it */
  941.             return(c);
  942.         }
  943.     FeMessage("Invalid command.");
  944.     }
  945. }
  946.  
  947.  
  948. /*----------------------------------------------------------------------
  949.  *    FeMessage -- print a highlighted message on bottom line
  950.  *
  951.  * This function prints a string in reverse video on line 23.
  952.  * The message length is restricted to 62 characters to avoid
  953.  * running into the menu box.  If NULL is passed as a message,
  954.  * the message line is cleared.
  955.  *----------------------------------------------------------------------
  956.  */
  957.  
  958. FeMessage(s)
  959. char *s;
  960. {
  961. char c = 0;
  962.  
  963. mvaddstr(23,0,BLANKS(62));        /* clear message line */
  964. if (s != NULL) {        /* if we have a message to print */
  965.     if (strlen(s) > 62) {    /* check that it's not too long */
  966.         c = s[62];    /* save char at this position */
  967.         s[62] = '\0';    /* and end the string */
  968.         }
  969.     move(23,0);
  970.     standout();
  971.     addstr(s);        /* print the message */
  972.     standend();
  973.     if (c != '\0')        /* if we shortened it, restore it */
  974.         s[62] = c;
  975.     }
  976. refresh();
  977. }
  978.  
  979.  
  980. /*----------------------------------------------------------------------
  981.  *    FeStatusLine -- draw string on status line
  982.  *
  983.  * This function puts a string on line 18 in reverse video.  It is
  984.  * used to display blots hit, double offers, etc.
  985.  *----------------------------------------------------------------------
  986.  */
  987.  
  988. FeStatusLine(s)
  989. char *s;
  990. {
  991. char c = 0;
  992. int l;
  993.  
  994. mvaddstr(18,10,BLANKS(50));    /* clear status line */
  995. if (s != NULL) {        /* if we have a message to print */
  996.     if ( (l = strlen(s)) > 50) {    /* check that it's not too long */
  997.         c = s[50];    /* save char at this position */
  998.         s[50] = '\0';    /* and end the string */
  999.         l = 50;
  1000.         }
  1001.     move(18,(50 - l)/2 + 10);
  1002.     standout();
  1003.     addstr(s);        /* print the message */
  1004.     standend();
  1005.     if (c != '\0')        /* if we shortened it, restore it */
  1006.         s[50] = c;
  1007.     }
  1008. refresh();
  1009. }
  1010.  
  1011.  
  1012. /*----------------------------------------------------------------------
  1013.  *    FeDrawCube -- draw doubling cube
  1014.  *
  1015.  * This function draws the doubling cube.  The cube is displayed beside
  1016.  * the inner table of the player who owns in (i.e. the one who didn't
  1017.  * double last).  If neither player has doubled, the cube is drawn
  1018.  * in the middle of the board.
  1019.  *----------------------------------------------------------------------
  1020.  */
  1021.  
  1022. FeDrawCube(g)
  1023. struct game *g;
  1024. {
  1025. int r, c;
  1026. char buf[8];
  1027.  
  1028. mvaddstr(3,0,"    ");        /* clear all cube locations */
  1029. mvaddstr(10,0,"    ");
  1030. mvaddstr(17,0,"    ");
  1031. if (g->gameval == (1 << g->adcnt))    /* nobody has doubled */
  1032.     r = 10;            /* cube is in the middle of the board */
  1033. else {            /* assume I didn't double last, mydir is up, */
  1034.     r = 0;        /* and board is not inverted */
  1035.     if (g->flags & F_IDOUBLED)    /* if I did double last */
  1036.         r = 1 - r;        /* switch rows */
  1037.     if (g->mydir < 0)        /* if my direction is down */
  1038.         r = 1 - r;        /* switch rows */
  1039.     if (g->flags & F_INVERT)    /* if board is inverted */
  1040.         r = 1 - r;        /* switch rows */
  1041.     r = r ? 17 : 3;            /* which row am I left with? */
  1042.     }
  1043.  
  1044. sprintf(buf,"%d",g->gameval);        /* generate the game value */
  1045. if ( (c = 4 - strlen(buf)) < 0) {    /* doubled past 4 digits? */
  1046.     strcpy(buf,"****");        /* we are out of columns */
  1047.     c = 0;
  1048.     }
  1049. move(r,c);
  1050. standout();
  1051. mvaddstr(r,c,buf);        /* go there and draw game value */
  1052. standend();
  1053. }
  1054.  
  1055.  
  1056. /*----------------------------------------------------------------------
  1057.  *    PDrawComment -- print a comment stored in a game
  1058.  *
  1059.  * This function takes a pointer to a game and draws the comment
  1060.  * strings to the screen.  If "who" is 0, the "mycmt" strings
  1061.  * are drawn on lines 19 and 20.  Otherwise, the "opcmt" strings
  1062.  * are drawn on lines 21 and 22.  Any unused space on these lines
  1063.  * is cleared.
  1064.  *----------------------------------------------------------------------
  1065.  */
  1066.  
  1067. PRIVATE PDrawComment(who,g)
  1068. int who;
  1069. struct game *g;
  1070. {
  1071. int line;
  1072. char *s1, *s2;
  1073.  
  1074. line = who ? 21 : 19;
  1075. s1 = who ? g->opcmt : g->mycmt;
  1076. s2 = who ? g->opcmt2 : g->mycmt2;
  1077. mvaddstr(line,6,BLANKS(56));
  1078. mvaddstr(line+1,6,BLANKS(56));
  1079. if (s1 != NULL) {
  1080.     if (strlen(s1) > 56)
  1081.         s1[56] = '\0';
  1082.     mvaddstr(line,6,s1);
  1083.     }
  1084. if (s2 != NULL) {
  1085.     if (strlen(s2) > 56)
  1086.         s2[56] = '\0';
  1087.     mvaddstr(line+1,6,s2);
  1088.     }
  1089. }
  1090.