[comp.sources.games] v06i059: cubes2 - a networked dice game

games@tekred.CNA.TEK.COM (04/28/89)

Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris)
Posting-number: Volume 6, Issue 59
Archive-name: cubes2/Part01

	[This game uses BSD Unix networking facilities to manage and/or play
	 a multi-user dice game.  An earlier version of this game was
	 posted back in June of 1988.  -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 8)."
# Contents:  README MANIFEST patchlevel.h screen.c
# Wrapped by billr@saab on Thu Apr 27 12:13:34 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2664 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XCubes should compile and run on just about any type of BSD UNIX machine.  No
Xattempt has been made to make cubes work on non-BSD machines.
X
X------------------------------------------------------------------------------
X
XThe game of cubes is a dice game played with five dice.  The server supplies
Xcompetition in the form of robot players, but also allows for multiple human
Xplayers via use of BSD networking facilities.
X
XPlayers take turns rolling the dice.  Points are scored by rolling one or more
Xscoring combinations.  Any time a roll fails to produce at least one scoring
Xcombination, that player's turn is over and no points are added to his/her/its
Xscore.  Each time a roll produces one or more scoring combinations, the player
Xhas the choice to hold or roll again.  If the player holds, his/her/its turn
Xis over and the sum of points accumulated during the turn are added to the
Xplayer's score.
X
XThe first player to reach 10,000 points is declared the winner and the game
Xends.  At that point, the score of each player is recorded in the score file.
X
X------------------------------------------------------------------------------
X
XThere are two pieces to cubes: the server and the client.  To install the
Xserver, you probably need root privileges on your machine.  The Makefile
Xcontains comments that should help you with installing.  Also, check the
Xmanual page cubeserver.6 that comes with this distribution, as it contains
Xinformation about installing and configuring cubes.  The file cubes.h contains
Xsome configuration parameters, but, in general, you should not have need to
Xmodify them.
X
XThis is version 5.1 of cubes.  It is not backward compatible with previous
Xversions of server or client.  Also, the score file format has changed.  If you
Xare currently running cubes 4.1, then the histconv.sh script supplied with
Xthis distribution can be used to convert your score file to 5.1 format.
X
XIn some form or another, cubes has been running at the author's location for
Xalmost a year.  The last source code changes were made to cubes in January
X1989.  Therefore, I think (and hope) you'll find cubes quite mature and
Xrelatively bug free.
X
XI'm happy to receive bug reports and suggestions for improvements to cubes.
XPatches are welcomed.  Please send such to:
X
X	Greg Paris <gmp@rayssd.ray.com>
X	{decuac,necntc,sun,uiucdcs,ukma}!rayssd!gmp
X
X------------------------------------------------------------------------------
X
XThe source code for cubes may be freely redistributed, which is to say that
Xthe cubes source code is not for sale or resale.
X
X------------------------------------------------------------------------------
X
XGreg Paris
XApril 25, 1989
END_OF_FILE
if test 2664 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(1137 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   8	
X README                     1	
X actions.c                  6	
X announce.c                 5	
X avg.c                      8	
X comptbl.c                  4	
X cubeorder.sh               8	
X cuberank.6                 8	
X cubes.6                    4	
X cubes.c                    7	
X cubes.h                    8	
X cubes.long.6               3	
X cubeserv1.c                2	
X cubeserv2.c                6	
X cubeserver.6               8	
X cubestat.6                 2	
X cubestat.c                 7	
X dieopts.c                  5	
X fullname.c                 4	
X histanal.c                 5	
X histconv.sh                8	
X history.c                  6	
X moniker.c                  3	
X patchlevel.h               1	
X random.c                   8	
X risk.c                     7	
X scr.sh                     5	
X screen.c                   1	
X strategies.c               7	
X tactics.c                  2	
X tempers.c                  3	
X turn.c                     4	
END_OF_FILE
if test 1137 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(21 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define PATCHLEVEL 0
END_OF_FILE
if test 21 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(46584 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)screen.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X/*
XXXX	Curses is too much overhead.  I've tried to limit the problem with
XXXX	a bunch of booleans, but the real solution is to scrap curses and
XXXX	write a special purpose screen driver.  Someday...
X*/
X
X/*
XXXX	Note that the value of DISPLEN in cubes.h is determined by this
XXXX	code though it is not referenced here.
X*/
X
X#include	<stdio.h>
X#include	<curses.h>
X#include	<sys/types.h>
X#include	<sys/time.h>
X#include	<sys/ioctl.h>
X#include	<signal.h>
X#include	<setjmp.h>
X#include	<strings.h>
X#include	<ctype.h>
X#include	"cubes.h"
X
X#define		DICEROW			2				/* top line for dice */
X#define		DICECOL			0				/* dice begin at left edge */
X#define		DIELINES		5				/* height of a single die */
X#define		DIEWID		(wide==True?16:10)	/* width of a single die */
XWINDOW	   *die[NDICE];						/* subwindow for each die */
X
X#define		ALLDICEWID		(NDICE*DIEWID)	/* width of all dice together */
X
X#define		DNUMROW			(DICEROW-2)		/* dice numbers at top edge */
X#define		DNUMCOL			DICECOL			/* dnumwin aligns with dice */
X#define		DNUMLINES		1				/* one line of dice numbers */
X#define		DNUMWID			ALLDICEWID		/* dnumwin aligns with dice */
XWINDOW	   *dnumwin;						/* dice number subwindow */
X
X#define		DFACEROW		(DICEROW-1)		/* die face names above dice */
X#define		DFACECOL		DICECOL			/* dfacewin aligns with dice */
X#define		DFACELINES		1				/* one line of die face names */
X#define		DFACEWID		ALLDICEWID		/* dfacewin aligns with dice */
XWINDOW	   *dfacewin;						/* die face names subwindow */
X
X#define		DSTATROW	(DICEROW+DIELINES)	/* top of dstat subwindow */
X#define		DSTATCOL		DICECOL			/* first col of dstat subwindow */
X#define		DSTATLINES		5				/* height of dstat subwindow */
X#define		DSTATWID		ALLDICEWID		/* width of dstat subwindow */
XWINDOW	   *dstatwin;						/* dstat subwindow */
X
X/*
X**	There's an unused window at line 12.  Extends to scorewin.
X*/
X
X#define		INFOROW	(DSTATROW+DSTATLINES+1)	/* where info messages appear */
X#define		INFOCOL			0				/* where info messages appear */
X#define		INFOLINES		1				/* lines in subwindow */
X#define		INFOWID			0				/* extends to edge of screen */
XWINDOW	   *infowin;						/* informational subwindow */
X
X#define		PROMPTROW		(INFOROW+1)		/* where prompt appears */
X#define		PROMPTCOL		0				/* where prompt appears */
X#define		PROMPTLINES		1				/* one line of prompt */
X#define		PROMPTWID		0				/* extends to edge */
XWINDOW	   *promptwin;						/* prompt subwindow */
X
X#define		HELPROW			(LINES-9)		/* where help subwindow begins */
X#define		HELPCOL			0				/* where help subwindow begins */
X#define		HELPLINES		0				/* extends to edge of screen */
X#define		HELPWID			0				/* extends to edge of screen */
XWINDOW	   *helpwin;						/* help message subwindow */
X
X#define		SCOREROW		0				/* where score subwindow begins */
X#define		SCORECOL		(ALLDICEWID+1)	/* where score subwindow begins */
X#define		SCORELINES		(PLAYERS+3)		/* XXX: PLAYERS must be <= 10 */
X#define		SCOREWID		0				/* extends to edge of screen */
XWINDOW	   *scorewin;						/* player scores subwindow */
X
X#define		CLEARDIE		(JOKER+1)		/* symbolic for cleared die */
X#define		FACES			(2*(CLEARDIE+1))/* number of die faces */
X
X/*
X**	Symbolics for drawing characters.  Values are for DEC.
X*/
X#define	Dot		'`'
X#define	Vert	'x'
X#define	Horiz	'q'
X#define	UpLeft	'l'
X#define	UpRite	'k'
X#define	LoLeft	'm'
X#define	LoRite	'j'
X
Xstruct tchars			tty_ch;				/* tty characters */
Xstruct ltchars			tty_lt;				/* local tty characters */
Xstatic char			   *AS, *AE;			/* alternate (graphics) */
Xgraphtype				graphics = Nographics;	/* terminal has no graphics */
Xextern cstat			mystat;				/* current playing status */
Xextern boolean			quiet;				/* in quiet mode (background) */
Xstatic boolean			drawhdr	= True;		/* force redraw of score header */
Xstatic boolean			drawnum	= True;		/* force redraw of dice numbers */
Xstatic boolean			infoclr	= True;		/* infowin is clear */
Xstatic boolean			defhelp	= False;	/* default help is showing */
Xstatic boolean			wide	= False;	/* wide terminal (110+ x 24) */
Xstatic boolean			sqndr	= False;	/* displaying p_squander vals */
Xstatic int				mypgrp;				/* process group */
Xstatic int				currup	= -1;		/* current player up */
Xstatic int				lastup	= -1;		/* last player up */
Xstatic char	dieface[FACES][DIELINES][16];	/* die faces for this term */
X
Xstatic int				refdummy;
X#define	REFRESH()		(refdummy = (quiet == False) ? refresh() : 0)
X#define	WREFRESH(win)	(refdummy = (quiet == False) ? wrefresh(win) : 0)
X
Xextern short			ospeed;
X#define	SLOW			B1200				/* slow data path */
X
Xextern boolean			jokermode;
Xextern boolean			inprogress;
Xextern int				winscore;
Xextern int				mypnum;
Xextern int				turnnum;
Xextern int				nobs;
Xextern boolean			gotintr;
Xextern player			plr[];
Xextern diceset			dice;
Xstatic int				outc();
Xstatic int				suspend();
Xstatic int				unsuspend();
Xstatic int				sigttin();
Xstatic boolean			inbackground();
Xextern int				redraw();
Xextern int				mybeep();
Xstatic char			   *facename();
Xstatic char			   *combname();
Xextern char			   *tgetstr();
Xextern char			   *getenv();
X
X/*
X**	faces: die faces for each die using DEC line drawing characters
X*/
Xstatic char	   *faces[][DIELINES]	= {
X/*B*/	{ "lqqqqqqqk","x       x","x       x","x       x","mqqqqqqqj" },
X/*B*/	{ "lqqqqqqqk","x       x","x       x","x       x","mqqqqqqqj" },
X/*A*/	{ "lqqqqqqqk","x       x","x   `   x","x       x","mqqqqqqqj" },
X/*A*/	{ "lqqqqqqqk","x       x","x   `   x","x       x","mqqqqqqqj" },
X/*2*/	{ "lqqqqqqqk","x `     x","x       x","x     ` x","mqqqqqqqj" },
X/*2*/	{ "lqqqqqqqk","x     ` x","x       x","x `     x","mqqqqqqqj" },
X/*3*/	{ "lqqqqqqqk","x     ` x","x   `   x","x `     x","mqqqqqqqj" },
X/*3*/	{ "lqqqqqqqk","x `     x","x   `   x","x     ` x","mqqqqqqqj" },
X/*4*/	{ "lqqqqqqqk","x `   ` x","x       x","x `   ` x","mqqqqqqqj" },
X/*4*/	{ "lqqqqqqqk","x `   ` x","x       x","x `   ` x","mqqqqqqqj" },
X/*F*/	{ "lqqqqqqqk","x `   ` x","x   `   x","x `   ` x","mqqqqqqqj" },
X/*F*/	{ "lqqqqqqqk","x `   ` x","x   `   x","x `   ` x","mqqqqqqqj" },
X/*6*/	{ "lqqqqqqqk","x `   ` x","x `   ` x","x `   ` x","mqqqqqqqj" },
X/*6*/	{ "lqqqqqqqk","x ` ` ` x","x       x","x ` ` ` x","mqqqqqqqj" },
X/*7*/	{ "lqqqqqqqk","x   ` ` x","x ` ` ` x","x ` `   x","mqqqqqqqj" },
X/*7*/	{ "lqqqqqqqk","x ` `   x","x ` ` ` x","x   ` ` x","mqqqqqqqj" },
X/*8*/	{ "lqqqqqqqk","x ` ` ` x","x `   ` x","x ` ` ` x","mqqqqqqqj" },
X/*8*/	{ "lqqqqqqqk","x ` ` ` x","x `   ` x","x ` ` ` x","mqqqqqqqj" },
X/*J*/	{ "lqqqqqqqk","x lqqqk x","x x   x x","x mqqqj x","mqqqqqqqj" },
X/*J*/	{ "lqqqqqqqk","x lqqqk x","x x   x x","x mqqqj x","mqqqqqqqj" },
X/*B*/	{ "         ","         ","         ","         ","         " },
X/*B*/	{ "         ","         ","         ","         ","         " },
X};
X
X/*
X**	wfaces: wide die faces for each die using DEC line drawing characters
X*/
Xstatic char	   *wfaces[][DIELINES]	= {
X/*B*/	{ " lqqqqqqqqqqqk"," x           x"," x           x"," x           x"," mqqqqqqqqqqqj" },
X/*B*/	{ " lqqqqqqqqqqqk"," x           x"," x           x"," x           x"," mqqqqqqqqqqqj" },
X/*A*/	{ " lqqqqqqqqqqqk"," x           x"," x     `     x"," x           x"," mqqqqqqqqqqqj" },
X/*A*/	{ " lqqqqqqqqqqqk"," x           x"," x     `     x"," x           x"," mqqqqqqqqqqqj" },
X/*2*/	{ " lqqqqqqqqqqqk"," x  `        x"," x           x"," x        `  x"," mqqqqqqqqqqqj" },
X/*2*/	{ " lqqqqqqqqqqqk"," x        `  x"," x           x"," x  `        x"," mqqqqqqqqqqqj" },
X/*3*/	{ " lqqqqqqqqqqqk"," x        `  x"," x     `     x"," x  `        x"," mqqqqqqqqqqqj" },
X/*3*/	{ " lqqqqqqqqqqqk"," x  `        x"," x     `     x"," x        `  x"," mqqqqqqqqqqqj" },
X/*4*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x           x"," x  `     `  x"," mqqqqqqqqqqqj" },
X/*4*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x           x"," x  `     `  x"," mqqqqqqqqqqqj" },
X/*F*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x     `     x"," x  `     `  x"," mqqqqqqqqqqqj" },
X/*F*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x     `     x"," x  `     `  x"," mqqqqqqqqqqqj" },
X/*6*/	{ " lqqqqqqqqqqqk"," x  `     `  x"," x  `     `  x"," x  `     `  x"," mqqqqqqqqqqqj" },
X/*6*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x           x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
X/*7*/	{ " lqqqqqqqqqqqk"," x     `  `  x"," x  `  `  `  x"," x  `  `     x"," mqqqqqqqqqqqj" },
X/*7*/	{ " lqqqqqqqqqqqk"," x  `  `     x"," x  `  `  `  x"," x     `  `  x"," mqqqqqqqqqqqj" },
X/*8*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x  `     `  x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
X/*8*/	{ " lqqqqqqqqqqqk"," x  `  `  `  x"," x  `     `  x"," x  `  `  `  x"," mqqqqqqqqqqqj" },
X/*J*/	{ " lqqqqqqqqqqqk"," x  lqqqqqk  x"," x  x     x  x"," x  mqqqqqj  x"," mqqqqqqqqqqqj" },
X/*J*/	{ " lqqqqqqqqqqqk"," x  lqqqqqk  x"," x  x     x  x"," x  mqqqqqj  x"," mqqqqqqqqqqqj" },
X/*B*/	{ "              ","              ","              ","              ","              " },
X/*B*/	{ "              ","              ","              ","              ","              " },
X};
X
X/*
X**	initdieface: initialize dieface array
X*/
Xinitdieface()
X{
X	register int	line, face;
X	register char  *s;
X
X	for(face = 0;face < FACES;++face) {
X		for(line = 0;line < DIELINES;++line) {
X			s = &dieface[face][line][0];
X			strcpy(s, (wide==True ? wfaces : faces)[face][line]);
X			if(graphics == Digital)
X				continue;
X			if(graphics == Zenith) {
X				for(;*s != '\0';++s) {
X					switch(*s) {
X					case Dot:    *s = '^'; break;
X					case Vert:   *s = '`'; break;
X					case Horiz:  *s = 'a'; break;
X					case UpLeft: *s = 'f'; break;
X					case UpRite: *s = 'c'; break;
X					case LoLeft: *s = 'e'; break;
X					case LoRite: *s = 'd'; break;
X					default: break;
X					}
X				}
X				continue;
X			}
X			for(;*s != '\0';++s) {		/* Nographics */
X				switch(*s) {
X				case Dot:    *s = '*'; break;
X				case Vert:   *s = '|'; break;
X				case Horiz:  *s = '-'; break;
X				case UpLeft: *s = '.'; break;
X				case UpRite: *s = '.'; break;
X				case LoLeft: *s = '`'; break;
X				case LoRite: *s ='\''; break;
X				default: break;
X				}
X			}
X		}
X	}
X}
X
X/*
X**	interrupt: handle interrupt signal
X*/
Xstatic int
Xinterrupt()
X{
X	gotintr = True;
X}
X
X/*
X**	killed: handle terminate signal
X*/
Xstatic int
Xkilled(sig)
X{
X	cleanup(sig|0x10, "killed");
X}
X
X/*
X**	reallyquit: when prudent, ask player if they really want to quit
X*/
Xreallyquit()
X{
X	char   *mesg;
X	char	ans[256];
X
X	gotintr = False;
X	switch(mystat) {
X	case Spider:
X		mesg = "Later, web head...";
X		break;
X	case Watching:
X		mesg = "Ta ta, benchwarmer...";
X		break;
X	case Waiting:
X		mesg = "Sorry you couldn't wait...";
X		break;
X	default:
X		if(prompt("Do you really want to quit? [ny]", ans, True) <= 0)
X			return;
X		if(ans[0] != 'y' && ans[0] != 'Y')
X			return;
X		mesg = "So long, quitter...";
X		break;
X	}
X
X	(void) infomesg(mesg);
X	sprintf(ans, "%cq", ASYNCMARK);		/* async quit command */
X	respond(ans);						/* hope server's not jammed */
X	sleep(1);
X	cleanup(1, "");
X}
X
X/*
X**	startup: initialize the screen
X*/
Xstartup()
X{
X	register int	d;
X	static char		capbuf[256];		/* room for as, ae, and Cg */
X	char		   *pcap = &capbuf[0];
X	char		   *term, *Cg;
X
X	if(inbackground() == True) {
X		fprintf(stderr, "cubes: can't run in background\n");
X		exit(1);
X	}
X
X	/*
X	**	Call setterm explicitly because initscr doesn't return ERR
X	**	if the terminal type doesn't exist.  How stupid.
X	*/
X	if((term = getenv("TERM")) == 0) {
X		fprintf(stderr, "cubes: TERM not set, sorry\n");
X		exit(1);
X	}
X	if(setterm(term) == ERR) {
X		fprintf(stderr, "cubes: no termcap entry for %s\n", term);
X		exit(1);
X	}
X	if(initscr() == ERR) {
X		fprintf(stderr, "cubes: can't initialize screen\n");
X		exit(1);
X	}
X
X	/*
X	**	Check for goofy aspect ratio.
X	*/
X	if(LINES == 24 && COLS > 110)
X		wide = True;
X
X#ifdef	LONGNAMEBUG
X	/*
X	**	If this system has the longname bug, then we must re-get the
X	**	terminal description because the longname call in setterm
X	**	blows away the buffer.  Pretty nice, eh?
X	*/
X	{
X		static char	termbuf[1024];
X
X		if(tgetent(termbuf, term) != 1)
X			cleanup(1, "tgetent failed");
X	}
X#endif	LONGNAMEBUG
X
X	/*
X	**	If graphics type is unspecified we look in the terminal's
X	**	termcap entry to see if it has a Cg (Cubes graphics) entry.
X	**	If so, and if it's valid, we use that value.
X	*/
X	if(graphics == Nographics) {
X		if((Cg = tgetstr("Cg", &pcap)) != 0) {
X			switch(*Cg) {
X			case 'd': case 'D': graphics = Digital; break;
X			case 'r': case 'R': graphics = Digital; break;	/* no ReGIS */
X			case 'z': case 'Z': graphics = Zenith; break;
X			default: graphics = Nographics; break;
X			}
X		}
X	}
X
X	/*
X	**	Subsume SO and SE as necessary for nicer looking dice.
X	*/
X	switch(graphics) {
X	case Digital:
X	case Zenith:
X		AS = tgetstr("as", &pcap);
X		AE = tgetstr("ae", &pcap);
X		if(AS == 0 || AE == 0) {
X			graphics = Nographics;
X			break;
X		}
X		SO = AS, SE = AE;
X		break;
X	case Nographics:
X		break;
X	default:
X		cleanup(1, "bad graphics mode");
X		break;
X	}
X
X	/*
X	**	Generate die faces for this terminal.
X	*/
X	initdieface();
X
X	(void) signal(SIGHUP, SIG_DFL);
X	gotintr = False;
X	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, interrupt);
X	if(signal(SIGTSTP, SIG_IGN) != SIG_IGN) {
X		(void) signal(SIGTSTP, suspend);
X		(void) signal(SIGCONT, unsuspend);
X		(void) signal(SIGTTIN, sigttin);	/* not supposed to happen */
X		(void) signal(SIGTTOU, SIG_IGN);	/* for beeps */
X	}
X
X	/*
X	**	Need local characters to get reprint character.
X	**	If reprint isn't set, pretend it is set to form feed.
X	*/
X	(void) ioctl(_tty_ch, TIOCGLTC, (char *)&tty_lt);
X	if(tty_lt.t_rprntc == '\0')
X		tty_lt.t_rprntc = '\f';
X
X	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGTERM, killed);
X
X	scrollok(stdscr, FALSE);
X	leaveok(stdscr, FALSE);
X	crmode();
X	noecho();
X	REFRESH();
X
X	dnumwin = subwin(stdscr, DNUMLINES, DNUMWID, DNUMROW, DNUMCOL);
X	if(dnumwin == 0)
X		cleanup(1, "can't create dnumwin subwindow");
X
X	dfacewin = subwin(stdscr, DFACELINES, DFACEWID, DFACEROW, DFACECOL);
X	if(dfacewin == 0)
X		cleanup(1, "can't create dfacewin subwindow");
X
X	for(d = 0;d < NDICE;++d) {
X		die[d] = subwin(stdscr, DIELINES, DIEWID, DICEROW, d*DIEWID);
X		if(die[d] == 0)
X			cleanup(1, "can't create die subwindow");
X	}
X
X	dstatwin = subwin(stdscr, DSTATLINES, DSTATWID, DSTATROW, DSTATCOL);
X	if(dstatwin == 0)
X		cleanup(1, "can't create dstatwin subwindow");
X
X	infowin = subwin(stdscr, INFOLINES, INFOWID, INFOROW, INFOCOL);
X	if(infowin == 0)
X		cleanup(1, "can't create infowin subwindow");
X
X	promptwin = subwin(stdscr, PROMPTLINES, PROMPTWID, PROMPTROW, PROMPTCOL);
X	if(promptwin == 0)
X		cleanup(1, "can't create promptwin subwindow");
X
X	scorewin = subwin(stdscr, SCORELINES, SCOREWID, SCOREROW, SCORECOL);
X	if(scorewin == 0)
X		cleanup(1, "can't create scorewin subwindow");
X
X	helpwin = subwin(stdscr, HELPLINES, HELPWID, HELPROW, HELPCOL);
X	if(helpwin == 0)
X		cleanup(1, "can't create helpwin subwindow");
X
X	defhelpmesg();	/* initially wrong -- parameters will be corrected later */
X
X	diceinit();
X	neutral();
X}
X
X/*
X**	diceinit: shouldn't be here, but we want to draw the dice right away
X*/
Xdiceinit()
X{
X	register int	d;
X
X	dice.d_mesg[0] = '\0';
X	dice.d_pts_dice = 0;
X	dice.d_pts_roll = 0;
X	dice.d_pts_turn = 0;
X	dice.d_pts_max = 0;
X	dice.d_rolling = NDICE;
X	dice.d_again = True;
X	dice.d_best = Nothing;
X	for(d = 0;d < NDICE;++d) {
X		dice.d_stat[d] = Free;
X		dice.d_comb[d] = Nothing;
X		dice.d_face[d] = BADFACE;
X	}
X}
X
X/*
X**	cleanup: cleanup the screen and exit
X*/
Xcleanup(ex, message)
Xint		ex;
Xchar   *message;
X{
X	if(quiet == False) {
X		endwin();
X		tputs(CL, LINES, outc);
X		if(graphics != Nographics)
X			tputs(SE, 1, outc);
X	}
X
X	if(*message != '\0')
X		fprintf(stderr, "cubes: %s\n", message);
X	exit(ex);
X}
X
X/*
X**	wflush: flush stdout, then wait for it to drain
X*/
Xwflush()
X{
X	(void) fflush(stdout);
X#ifdef	TIOCWAIT
X	(void) ioctl(fileno(stdout), TIOCWAIT, (char *)0);
X#else	TIOCWAIT
X#ifdef	TIOCOUTQ
X	{
X		long			q;
X		struct timeval	tv;
X
X		while(ioctl(fileno(stdout), TIOCOUTQ, (char *)&q) == 0 && q > 0) {
X			switch(ospeed) {
X			case B1200: q *= 8333L; break;	/* lowest speed */
X			case B1800: q *= 5556L; break;
X			case B2400: q *= 4167L; break;
X			case B4800: q *= 2083L; break;
X			case B9600: q *= 1041L; break;
X			default:    q *=  521L; break;	/* highest, 19.2 and above */
X			}
X			tv.tv_sec =  q / 1000000L;	/* seconds */
X			tv.tv_usec = q % 1000000L;	/* microseconds */
X			(void) select(32, NOSEL, NOSEL, NOSEL, &tv);
X		}
X	}
X#endif	TIOCOUTQ
X#endif	TIOCWAIT
X}
X
X/*
X**	infomesg: display an informational or status message
X**		Try to let the player see it...
X*/
Xinfomesg(mesg)
Xchar   *mesg;
X{
X	if(quiet == False)
X		(void) fflush(stdout);
X
X	wclear(infowin);
X	if(*mesg == '\0')
X		infoclr = True;
X	else {
X		waddstr(infowin, mesg);
X		infoclr = False;
X	}
X	WREFRESH(infowin);
X	neutral();
X
X	if(quiet == False)
X		wflush();
X	return 0;
X}
X
X/*
X**	clearinfo: clear info message
X*/
Xclearinfo()
X{
X	wclear(infowin);
X	WREFRESH(infowin);
X	infoclr = True;
X	return 0;
X}
X
X/*
X**	helpline: structure for constructing help messages
X*/
Xtypedef struct {
X	char	   *he_fmt;
X	int			he_1, he_2, he_3;
X} helpline;
X
X/*
X**	lshelp: long, standard help message
X*/
Xstatic helpline	lshelp[]	= {
X/*"12345678901234567890123456789012345678901234567890123456789012345678901234567890"*/
X{ "| 5o'kind    face * %3d, held dice count        | ** Turn Point Thresholds ** |", P_AOKMULT, },
X{ "| 4o'kind    face * %3d                         | To get on board       %5d |", P_4OKMULT, ONBOARD },
X{ "| 3o'kind    face * %3d                         | To go off board       %5d |", P_3OKMULT, OFFBOARD },
X#ifdef	ASMSTR
X{ "| Straight         %4d, %3d using held dice    |-----------------------------|", P_STRAIGHT, P_ASMSTR, },
X#else	ASMSTR
X{ "| Straight         %4d                         |-----------------------------|", P_STRAIGHT, },
X#endif	ASMSTR
X{ "| Small Str.       %4d                         | ** Game Point Thresholds ** |", P_SMSTR, },
X{ "| One              %4d (face for ones is ten)  | Margin to win by      %5d |", P_ACE, WINMARGIN },
X{ "| Five             %4d                         | Total to win          %5d |", P_FIVE, WINSCORE },
X#define	lswinslot	lshelp[6].he_2
X{ (char *)0, }
X};
X
X/*
X**	ljhelp: long, joker help message
X*/
Xstatic helpline	ljhelp[]	= {
X/*"12345678901234567890123456789012345678901234567890123456789012345678901234567890"*/
X{ "| 5o'kind    face * %3d, held dice count        | ** Turn Point Thresholds ** |", P_AOKMULT, },
X{ "| 4o'kind    face * %3d                         | To get on board       %5d |", P_4OKMULT, ONBOARD },
X{ "| 3o'kind    face * %3d                         | To go off board       %5d |", P_3OKMULT, OFFBOARD },
X#ifdef	ASMSTR
X{ "| Straight         %4d, %3d using held dice    |-----------------------------|", P_STRAIGHT, P_ASMSTR, },
X#else	ASMSTR
X{ "| Straight         %4d                         |-----------------------------|", P_STRAIGHT, },
X#endif	ASMSTR
X{ "| Small Str.       %4d                         | ** Game Point Thresholds ** |", P_SMSTR, },
X{ "| Five, One    %3d, %3d (face for ones is 10)   | Margin to win by      %5d |", P_FIVE, P_ACE, WINMARGIN },
X{ "| Joker            %4d (face for jokers is %2d) | Total to win          %5d |", P_JOKER, P_JOKERMULT, WINSCORE },
X#define	ljwinslot	ljhelp[6].he_3
X{ (char *)0, }
X};
X
X/*
X**	defhelpmesg: display default help message
X*/
Xdefhelpmesg()
X{
X	register int	row;
X	helpline	   *phe;
X
X	wclear(helpwin);
X
X	lswinslot = ljwinslot = winscore;
X	phe = (jokermode == True) ? &ljhelp[0] : &lshelp[0];
X
X	for(row = 1;phe->he_fmt != 0;++row, ++phe) {
X		wmove(helpwin, row, 0);
X		wprintw(helpwin, phe->he_fmt, phe->he_1, phe->he_2, phe->he_3);
X	}
X
X	wmove(helpwin, 0, 0);
X	wbox(helpwin, 9, 79);	/* XXX: assumes mimimum 24x80 */
X	if(graphics != Nographics) {
X		wmove(helpwin, 1, 48);
X		wvbar(helpwin, row-1);
X		wmove(helpwin, row/2, 49);
X		whbar(helpwin, 29);
X	}
X
X	WREFRESH(helpwin);
X	defhelp = True;
X}
X
X/*
X**	helpmesg: display a help message
X*/
Xhelpmesg(mesg)
Xchar   *mesg;
X{
X	static int	row	= -1;
X
X	if(defhelp == True || row == -1) {
X		wclear(helpwin);
X		wmove(helpwin, 0, 0);
X		wbox(helpwin, 9, 79);	/* XXX: assumes 24x80 */
X		defhelp = False;
X		row = 1;
X	}
X
X	wmove(helpwin, row, 2);
X	waddstr(helpwin, mesg);
X	++row;
X
X	/*
X	**	Peek behind the message and see if this was the last one.
X	**	If so, refresh the window.  Set row to -1 to ensure that
X	**	the next help message will be printed properly.
X	*/
X	if(mesg[-1] == ' ') {
X		WREFRESH(helpwin);
X		row = -1;
X	}
X
X	return 0;
X}
X
X/*
X**	urgjmp, urgent: SIGURG handler
X*/
Xstatic jmp_buf	urgjmp;
Xstatic int
Xurgent()
X{
X	(void) signal(SIGALRM, SIG_IGN);	/* ignore alarm */
X	mybeep(1);							/* alert the player */
X	longjmp(urgjmp, 1);					/* abort read */
X}
X
X/*
X**	wakeup: beep the terminal and reset the alarm
X*/
X#define	WAKES	3
Xstatic int	wakecount;
Xstatic int
Xwakeup()
X{
X
X	if(++wakecount == WAKES) {			/* last warning */
X		(void) signal(SIGURG, SIG_IGN);	/* ignore SIGURG */
X		mybeep(1);						/* alert the player */
X		longjmp(urgjmp, 2);				/* abort read */
X	}
X
X	mybeep(wakecount+1);				/* alert the player */
X	(void) alarm(READTIMO/WAKES);		/* again */
X}
X
X/*
X**	tstpjmp, tstp4prompt: SIGTSTP handler for prompt routine
X*/
Xstatic jmp_buf	tstpjmp;
Xstatic int
Xtstp4prompt()
X{
X	suspend();							/* invoke normal TSTP handler */
X	longjmp(tstpjmp, 1);				/* abort read */
X}
X
X/*
X**	intjmp, int4prompt: SIGINT handler for prompt routine
X*/
Xstatic jmp_buf	intjmp;
Xstatic int
Xint4prompt()
X{
X	longjmp(intjmp, 1);
X}
X
X/*
X**	flushinput: handle asynchronous input
X*/
Xflushinput()
X{
X	long	q;
X	char	abuf[BUFSIZ];
X
X	/*
X	**	Read all pending input and scan it for async commands.
X	*/
X	while(ioctl(_tty_ch, FIONREAD, (char *)&q) == 0 && q > 0) {
X		abuf[0] = ASYNCMARK;	/* marks as asynchronous */
X		if(prompt("", abuf+1, True) > 0 && abuf[1] != '\0') {
X			if(abuf[1] == 's' || abuf[1] == 'S')
X				togglesqndr();
X			else
X				respond(abuf);
X		}
X		neutral();
X	}
X}
X
X/*
X**	prompt: print a prompt message and read an answer
X**		If we're async, we don't flush pending input
X**		and we allow backspace to cancel us.
X*/
Xprompt(mesg, answer, isasync)
Xchar   *mesg, *answer;
Xboolean	isasync;
X{
X	register int	c;
X	register char  *s;
X	long			q;
X	char			junk[64];
X	int			  (*sigurg)();
X	int			  (*sigalrm)();
X	int			  (*sigint)();
X	int			  (*sigtstp)();
X
X	/*
X	**	No answer yet.
X	*/
X	answer[0] = '\0';
X
X	/*
X	**	Don't allow asynchronous prompts in quiet mode.
X	*/
X	if(quiet == True && isasync == True)
X		return 0;
X
X	/*
X	**	Save current TSTP signal handler.
X	*/
X	if((sigtstp = signal(SIGTSTP, SIG_IGN)) != SIG_IGN)
X		(void) signal(SIGTSTP, sigtstp);
X
X	/*
X	**	Disallow interrupts for now.
X	*/
X	if((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
X		(void) signal(SIGINT, mybeep);	/* eeek -- beeps SIGINT times! */
X	
X	/*
X	**	Truly flush any pending input.
X	*/
X	if(quiet == False && isasync == False) {
X		while(ioctl(_tty_ch, FIONREAD, (char *)&q) == 0 && q > 0) {
X			(void) read(_tty_ch, junk, sizeof junk);
X			rewind(stdin);
X		}
X	}
X
X	/*
X	**	If server times out on us it sends out of band data, which should
X	**	cause us to receive a SIGURG.  Unfortunately, this signal might
X	**	not be delivered while the read is pending (depends on implementation
X	**	and phase of the moon).  Therefore, we use an alarm to provide
X	**	intermediate alerts and a backup timeout.  The SIGALRM and SIGURG
X	**	handlers work in tandem (hopefully).
X	*/
X	sigurg = signal(SIGURG,  SIG_IGN);
X	sigalrm = signal(SIGALRM, SIG_IGN);
X	if(setjmp(urgjmp) != 0) {
X		(void) signal(SIGINT,  sigint);
X		(void) signal(SIGALRM, sigalrm);
X		(void) signal(SIGURG,  sigurg);
X		(void) signal(SIGTSTP, sigtstp);
X		wclear(promptwin);
X		WREFRESH(promptwin);
X		return -2;	/* timeout */
X	}
X
X	/*
X	**	If we're in an asynchronous prompt, let interrupt cancel.
X	*/
X	if(isasync == True && sigint != SIG_IGN) {
X		(void) signal(SIGINT, int4prompt);
X		if(setjmp(intjmp) != 0) {
X			(void) alarm(0);
X			answer[0] = '\0';
X			(void) signal(SIGINT,  sigint);
X			(void) signal(SIGALRM, sigalrm);
X			(void) signal(SIGURG,  sigurg);
X			(void) signal(SIGTSTP, sigtstp);
X			mybeep(1);
X			wclear(promptwin);
X			WREFRESH(promptwin);
X			neutral();
X			return 0;
X		}
X	}
X
X	/*
X	**	Handle timeout signals.
X	*/
X	wakecount = 0;
X	(void) alarm(0);
X	(void) signal(SIGURG,  urgent);
X	if(isasync == False)
X		(void) signal(SIGALRM, wakeup);		/* wakeup resets alarm */
X	else
X		(void) signal(SIGALRM, int4prompt);	/* timeout cancels prompt */
X	(void) alarm(READTIMO/WAKES);			/* set for first alert */
X
Xreprompt:
X	/*
X	**	Display the prompt (if any) and partial answer (if any).
X	**	We hang here if we're in quiet mode (puts cursor in correct place).
X	*/
X	while(quiet == True)
X		pause();
X	wmove(promptwin, 0, 0);
X	wclear(promptwin);
X	if(*mesg != '\0') {
X		waddstr(promptwin, mesg);
X		waddch(promptwin, ' ');
X	}
X	for(s = answer;*s != '\0';++s)
X		waddch(promptwin, *s);
X	WREFRESH(promptwin);
X
X	/*
X	**	Loop until we get carriage return or some sort of abort.
X	*/
X	for(;;) {
X		/*
X		**	We have a problem here if somebody tries to suspend while being
X		**	prompted.  The read call is resumed, so a character gets stolen
X		**	from the foreground process.  In order to avoid this, we have
X		**	to abort the read by using longjmp.  We do this in the loop
X		**	so the appropriate values of s and c get restored.
X		**
X		**	If we're suspended in an asynchronous prompt, we abort it.
X		*/
X		if(sigtstp != SIG_IGN) {
X			(void) signal(SIGTSTP, tstp4prompt);
X			if(setjmp(tstpjmp) != 0 && isasync == True) {
X				(void) alarm(0);
X				answer[0] = '\0';
X				(void) signal(SIGINT,  sigint);
X				(void) signal(SIGALRM, sigalrm);
X				(void) signal(SIGURG,  sigurg);
X				(void) signal(SIGTSTP, sigtstp);
X				wclear(promptwin);
X				WREFRESH(promptwin);
X				neutral();
X				return 0;
X			}
X		}
X
X		/*
X		**	If we're in quiet mode, we can't do a read, or we might
X		**	steal characters from foreground processes.  Instead, we'll
X		**	jump back to reprompt and hang there.  This also causes
X		**	the cursor to be positioned properly.
X		*/
X		if(quiet == True)
X			goto reprompt;
X
X		/*
X		**	Read input, handling various special characters.
X		*/
X		c = wgetch(promptwin);
X		if(c == EOF || c == '\r' || c == '\n')
X			break;
X		if(c == _tty.sg_erase) {
X			if(--s < answer) {
X				s = answer;
X				if(isasync == True)
X					break;
X				mybeep(1);
X				continue;
X			}
X			waddch(promptwin, '\b');
X			wdelch(promptwin);
X			WREFRESH(promptwin);
X			continue;
X		}
X		if(c == _tty.sg_kill || c == tty_lt.t_werasc) {
X			if(isasync == True)
X				break;
X			answer[0] = '\0';
X			goto reprompt;
X		}
X		if(c == tty_lt.t_rprntc || c == '\f') {
X			if(isasync == True && s == answer) {
X				wclear(promptwin);
X				redraw();
X				break;
X			}
X			redraw();
X			touchwin(promptwin);
X			WREFRESH(promptwin);
X			continue;
X		}
X		if(iscntrl(c)) {
X			mybeep(1);
X			continue;
X		}
X		waddch(promptwin, c);
X		WREFRESH(promptwin);
X		*s++ = c;
X	}
X	*s = '\0';
X	(void) alarm(0);
X	(void) signal(SIGALRM, sigalrm);
X	(void) signal(SIGURG,  sigurg);
X	(void) signal(SIGTSTP, sigtstp);
X
X	wmove(promptwin, 0, 0);
X	wclear(promptwin);
X	WREFRESH(promptwin);
X
X	if(infoclr == False)
X		clearinfo();
X
X	(void) signal(SIGINT, sigint);
X
X	if(defhelp == False)
X		defhelpmesg();
X
X	return (int)(s - answer);
X}
X
X/*
X**	showobs: show the number of observers
X*/
Xshowobs()
X{
X	register int	c;
X
X	/*
X	**	Find the last active player.
X	*/
X	for(c = PLAYERS-1;c > 0;--c)
X		if(plr[c].p_stat != Inactive)
X			break;
X	if(c++ < 0)
X		return;		/* huh? */
X
X	/*
X	**	Update observer(s) display.  We place it beyond the last player.
X	*/
X	if(nobs > 0) {
X		wmove(scorewin, c+2, 0);
X		wprintw(scorewin, "    (plus %d observer%s) ",
X			nobs, nobs == 1 ? "" : "s");
X		++c;
X	}
X	wmove(scorewin, c+2, 0);
X	wclrtobot(scorewin);
X}
X
X/*
X**	nowup: mark player as being up
X**		Updates number of observers.
X**		Updates turn number and player average turn score.
X*/
Xnowup(num)
Xint		num;
X{
X	/*
X	**	Set the turn number and call markhigh after roster redraw
X	**	and at the beginning of the turn order.
X	*/
X	if(currup == -1) {
X		currup = num;
X		markhigh();
X	} else if((currup = num) == 0)
X		markhigh();
X
X	/*
X	**	If it's our turn and squander values are showing,
X	**	switch back to displaying score values.
X	*/
X	if(num == mypnum && sqndr == True)
X		togglesqndr();
X
X	/*
X	**	Clear info message and update observers.
X	*/
X	if(infoclr == False)
X		(void) clearinfo();
X	showobs();
X
X	/*
X	**	Update turn number.  Mark turn average.
X	*/
X	wmove(scorewin, 0, 11);
X	wprintw(scorewin, "%2d", turnnum);
X	markturnavg(num);
X
X	/*
X	**	Clear old arrow.
X	*/
X	if(lastup != -1) {
X		wmove(scorewin, lastup+2, 0);
X		waddstr(scorewin, "   ");
X	}
X
X	/*
X	**	Draw new arrow.
X	*/
X	wmove(scorewin, num+2, 0);
X	switch(graphics) {
X	case Zenith:
X		wstandout(scorewin);
X		waddstr(scorewin, "hhh");
X		wstandend(scorewin);
X		WREFRESH(scorewin);
X		break;
X	case Digital:
X		wstandout(scorewin);
X		waddstr(scorewin, "=->");
X		wstandend(scorewin);
X		WREFRESH(scorewin);
X		break;
X	default:
X		waddstr(scorewin, "=->");
X		WREFRESH(scorewin);
X		break;
X	}
X
X	/*
X	**	If it's our turn, make some noise.
X	*/
X	lastup = num;
X	if(num == mypnum) {
X		if(defhelp == False)
X			defhelpmesg();
X		if(mystat != Computer)
X			mybeep(quiet == True ? 3 : 1);	/* noisier when quiet! */
X		else if(quiet == False)
X			(void) infomesg(
X				"Autopilot engaged; type g<return> to get control.");
X	}
X
X	return 0;
X}
X
X/*
X**	whbar: draw a horizontal bar of specified length in specified window
X*/
Xwhbar(win, len)
XWINDOW *win;
Xint		len;
X{
X	switch(graphics) {
X	case Digital:
X		wstandout(win);
X		while(len-- > 0)
X			waddch(win, Horiz);
X		wstandend(win);
X		break;
X	case Zenith:
X		wstandout(win);
X		while(len-- > 0)
X			waddch(win, 'a');
X		wstandend(win);
X		break;
X	default:
X		while(len-- > 0)
X			waddch(win, '-');
X		break;
X	}
X}
X
X/*
X**	wvbar: draw a vertical bar of specified length in specified window
X*/
Xwvbar(win, len)
XWINDOW *win;
Xint		len;
X{
X	int		x, y;
X
X	getyx(win, y, x);
X
X	switch(graphics) {
X	case Digital:
X		wstandout(win);
X		while(len-- > 0) {
X			wmove(win, y + len, x);
X			waddch(win, Vert);
X		}
X		wstandend(win);
X		break;
X	case Zenith:
X		wstandout(win);
X		while(len-- > 0) {
X			wmove(win, y + len, x);
X			waddch(win, '`');
X		}
X		wstandend(win);
X		break;
X	default:
X		while(len-- > 0) {
X			wmove(win, y + len, x);
X			waddch(win, '|');
X		}
X		break;
X	}
X}
X
X/*
X**	wcorner: draw a corner character in a window
X*/
Xwcorner(win, c)
XWINDOW *win;
Xint		c;
X{
X	char	x;
X
X	switch(graphics) {
X	case Digital:
X		wstandout(win);
X		waddch(win, c);	/* defined Digital */
X		wstandend(win);
X		break;
X	case Zenith:
X		switch(c) {
X		case UpLeft: x = 'f'; break;
X		case UpRite: x = 'c'; break;
X		case LoLeft: x = 'e'; break;
X		case LoRite: x = 'd'; break;
X		}
X		wstandout(win);
X		waddch(win, x);
X		wstandend(win);
X		break;
X	default:
X		waddch(win, '+');
X		break;
X	}
X}
X
X/*
X**	wblank: draw a blank in the graphics character set
X*/
Xwblank(win)
XWINDOW *win;
X{
X	switch(graphics) {
X	case Digital:
X	case Zenith:
X		wstandout(win);
X		waddch(win, ' ');
X		wstandend(win);
X		break;
X	default:
X		waddch(win, ' ');
X		break;
X	}
X}
X
X/*
X**	wside: draw a side character in a window
X*/
Xwside(win)
XWINDOW *win;
X{
X	switch(graphics) {
X	case Digital:
X		wstandout(win);
X		waddch(win, Vert);
X		wstandend(win);
X		break;
X	case Zenith:
X		wstandout(win);
X		waddch(win, '`');
X		wstandend(win);
X		break;
X	default:
X		waddch(win, '|');
X		break;
X	}
X}
X
X/*
X**	wbox: draw a box in a window
X*/
Xwbox(win, lines, cols)
XWINDOW *win;
Xint		lines, cols;
X{
X	int		row;
X	int		x, y;
X
X	getyx(win, y, x);
X
X	wcorner(win, UpLeft);
X	whbar(win, cols-2);
X	wcorner(win, UpRite);
X
X	for(row = 1;row < lines-1;++row) {
X		wmove(win, y+row, x);
X		wside(win);
X		wmove(win, y+row, x+cols-1);
X		wside(win);
X	}
X
X	wmove(win, y+lines-1, x);
X	wcorner(win, LoLeft);
X	whbar(win, cols-2);
X	wcorner(win, LoRite);
X}
X
X/*
X**	togglesqndr: toggle sqndr value and redraw score window
X*/
Xtogglesqndr()
X{
X	int		c;
X
X	/*
X	**	Toggle squander value.
X	*/
X	sqndr = (sqndr == True) ? False : True;
X
X	/*
X	**	If drawhdr is False, we need to update the score window.  
X	**	Otherwise, we can wait for the server to request redraw.
X	*/
X	if(drawhdr == False) {
X		markturnavg(currup);
X		wmove(scorewin, 0, 23);
X		waddstr(scorewin, sqndr == True ? "Sqndr" : "Score");
X		for(c = 0;c < PLAYERS;++c) {
X			if(plr[c].p_stat == Active || plr[c].p_stat == Computer) {
X				wmove(scorewin, c+2, 22);
X				wprintw(scorewin, "%6d",
X					sqndr == True ? -plr[c].p_squander : plr[c].p_score);
X			}
X		}
X		markhigh();
X		WREFRESH(scorewin);
X	}
X}
X
X/*
X**	showplr: update player info
X*/
Xshowplr(c)
Xint		c;
X{
X	/*
X	**	Draw the header when necessary.
X	*/
X	if(drawhdr == True) {
X		wmove(scorewin, 0, 0);
X		wclrtoeol(scorewin);
X		wprintw(scorewin,
X			"%-3s %-18.18s %5s", "Up", "Player",
X			sqndr == True ? "Sqndr" : "Score");
X
X		wmove(scorewin, 1, 0);
X		wclrtoeol(scorewin);
X		whbar(scorewin, 3);
X		wblank(scorewin);
X		whbar(scorewin, 18);
X		wblank(scorewin);
X		whbar(scorewin, 5);
X		drawhdr = False;
X	}
X
X	/*
X	**	Update a specific player's entry.  Calling markhigh here
X	**	is what causes its excessive behavior, but we must...
X	*/
X	wmove(scorewin, c+2, 0);
X	wclrtoeol(scorewin);
X	if(plr[c].p_stat == Active || plr[c].p_stat == Computer)
X		wprintw(scorewin, "    %-18.18s%6d", plr[c].p_name,
X			sqndr == True ? -plr[c].p_squander : plr[c].p_score);
X	if(currup != -1)
X		markhigh();
X
X	WREFRESH(scorewin);
X
X	return 0;
X}
X
X/*
X**	clearplr: clear player info
X*/
Xclearplr(c)
Xint		c;
X{
X	if(c == lastup)
X		lastup = -1;
X
X	wmove(scorewin, c+2, 0);
X	wclrtoeol(scorewin);
X	markhigh();
X	WREFRESH(scorewin);
X
X	return 0;
X}
X
X/*
X**	clearscores: blank the entire score window
X*/
Xclearscores()
X{
X	wclear(scorewin);
X	WREFRESH(scorewin);
X	drawhdr = True;
X	currup = lastup = -1;
X
X	return 0;
X}
X
X/*
X**	markturnavg: display player's turn point average
X*/
Xmarkturnavg(num)
Xint		num;
X{
X	int		numer, denom;
X
X	wmove(scorewin, 0, 17);
X	if(turnnum <= 0 || (turnnum == 1 && inprogress == True))
X		wprintw(scorewin, "%5s", "*");		/* no average yet */
X	else {
X		numer = sqndr == True ? -plr[num].p_squander : plr[num].p_score;
X		denom = inprogress == True ? (turnnum - 1) : turnnum;
X		wprintw(scorewin, "%5d", numer / denom);
X	}
X}
X
X/*
X**	markhigh: mark player(s) with high score, clearing all others
X**		Updates the turns left estimate.
X*/
Xmarkhigh()
X{
X	register int	c, h, hc, oh;
X	long			avg, left;
X	char			ccode, turnbuf[16];
X
X	/*
X	**	Find high score.  Save the index of the last player
X	**	that holds the high score.  That's why >= h.
X	**	Also find the high score amongst other players.
X	*/
X	h = oh = 0, hc = -1;
X	for(c = 0;c < PLAYERS;++c) {
X		if(plr[c].p_stat == Inactive)
X			continue;
X		if(plr[c].p_score >= h)
X			h = plr[(hc = c)].p_score;
X		if(c != mypnum && plr[c].p_score > oh)
X			oh = plr[c].p_score;
X	}
X
X	/*
X	**	Mark those that have high score, clear others.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		wmove(scorewin, c+2, 28);
X		if(h == 0 || plr[c].p_stat == Inactive || plr[c].p_score < h)
X			waddch(scorewin, ' ');
X		else
X			waddch(scorewin, '*');
X	}
X
X	/*
X	**	Estimate number of turns left in game.  Display in header.
X	XXX	This seems like an excessive amount of work to do here,
X	XXX	but this is the only place where it can be done correctly.
X	*/
X	if(inprogress == False)
X		turnbuf[0] = '\0';
X	else if(hc == -1 || h == 0 || turnnum <= 0)
X		strcpy(turnbuf, "+?");
X	else {
X		/*
X		**	Determine condition code.  This code is based on
X		**	how close the leading competitor is to winning.
X		XXX	Code depends on relationship between defined values.
X		**	Apologies to Roy G. Biv...
X		*/
X		left = winscore - oh;
X		     if(left <= OFFBOARD)				ccode = 'r';	/* red */
X		else if(left <= P_ACEMULT * P_3OKMULT)	ccode = 'o';	/* orange */
X		else if(left <= P_STRAIGHT)				ccode = 'y';	/* yellow */
X		else if(left <= P_ACEMULT * P_4OKMULT)	ccode = 'g';	/* green */
X		else if(left <= P_ACEMULT * P_AOKMULT)	ccode = 'b';	/* blue */
X		else									ccode = ' ';	/* no alert */
X
X		/*
X		**	This routine is called from nowup which is called before
X		**	each player's turn, and again when the turn is over
X		**	by showplr (for score updating purposes).  We detect the
X		**	second call by noting when currup is the same as lastup.
X		**	The leader has taken this turn when currup is greater than
X		**	hc or when currup equals hc and currup equals lastup.
X		*/
X		if(currup > hc || (currup == hc && currup == lastup))
X			avg = h / turnnum;			/* leader has taken turn */
X		else
X			avg = h / (turnnum - 1);	/* leader has yet to take turn */
X		if(avg <= 0)					/* never happen? */
X			avg = 1;					/* just in case */
X
X		/*
X		**	Calculate number of points left, accounting for end conditions.
X		**	In the case that the high score is within OFFBOARD of winscore,
X		**	if we're within avg, we'll say we can get it on the next roll.
X		*/
X		if((left = winscore - h) <= 0)
X			left = 0;					/* XXX: doesn't account for overtime */
X		else if(left < OFFBOARD && left > avg)
X			left = OFFBOARD;			/* need this, not avg */
X
X		left = (left + avg - 1) / avg;	/* rounds up */
X		sprintf(turnbuf, "+%ld%c", left, ccode);
X	}
X
X	/*
X	**	Display estimated turns left and condition code in header.
X	*/
X	wmove(scorewin, 0, 13);
X	wprintw(scorewin, "%-4.4s", turnbuf);
X}
X
X/*
X**	cleardice: remove the dice from the screen
X*/
Xcleardice()
X{
X	register int	d;
X
X	wclear(dstatwin);
X	WREFRESH(dstatwin);
X
X	for(d = 0;d < NDICE;++d) {
X		dice.d_face[d] = CLEARDIE;
X		draw1die(d);
X	}
X	diceinit();
X
X	wclear(dnumwin);
X	WREFRESH(dnumwin);
X	drawnum = True;
X
X	return 0;
X}
X
X/*
X**	facetbl: comparative values of faces
XXXX		assumes much, especially about the value of JOKER
X*/
Xstatic int	facetbl[][JOKER+1] = {
X/*		   B   A   2   3   4   F   6   -   -   J */
X/* B */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
X/* A */	{ -1,  0, -1, -1, -1, -1, -1, -1, -1, -1 },
X/* 2 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
X/* 3 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
X/* 4 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
X/* F */	{ -1,  1, -1, -1, -1,  0, -1, -1, -1, -1 },
X/* 6 */	{ -1,  1,  0,  0,  0,  1,  0, -1, -1,  1 },
X/* - */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
X/* - */	{  0,  1,  1,  1,  1,  1,  1,  0,  0,  1 },
X/* J */	{ -1,  1, -1, -1, -1,  1, -1, -1, -1,  0 }
X};
X
X/*
X**	dicecmp: compare dice indices, least valuable first
X*/
Xstatic int
Xdicecmp(p1, p2)
Xint	   *p1, *p2;
X{
X#ifndef	lint
X	int		diff;
X
X	if((diff = (int)dice.d_comb[*p1] - (int)dice.d_comb[*p2]) != 0)
X		return diff;
X	if((diff = (int)dice.d_stat[*p1] - (int)dice.d_stat[*p2]) != 0)
X		return diff;
X#endif	lint
X
X	return facetbl[dice.d_face[*p2]][dice.d_face[*p1]];
X}
X
X/*
X**	drawdice: show dice status and faces and scoring summary
X*/
Xdrawdice()
X{
X	register int	d;
X	char			msgbuf[MESGLEN];
X	int				dlist[NDICE];
X
X	/*
X	**	Update dnumwin when necessary.
X	*/
X	if(drawnum == True) {
X		for(d = 0;d < NDICE;++d) {
X			wmove(dnumwin, 0, d * DIEWID);
X			wprintw(dnumwin, "%*d", (DIEWID+1)/2, d+1);
X		}
X		WREFRESH(dnumwin);
X		drawnum = False;
X	}
X
X	/*
X	**	Draw the dice in a way that will maximize suspense,
X	**	that is, draw the least valuable first.
X	*/
X	for(d = 0;d < NDICE;++d)
X		dlist[d] = d;
X	qsort((char *)dlist, NDICE, sizeof dlist[0], dicecmp);
X	for(d = 0;d < NDICE;++d)
X		(void) draw1die(dlist[d]);
X	
X	/*
X	**	Show the combinations.
X	*/
X	wmove(dstatwin, 0, 0);
X	wclrtoeol(dstatwin);
X	for(d = 0;d < NDICE;++d) {
X		wmove(dstatwin, 0, d * DIEWID);
X		if(dice.d_stat[d] != Free && dice.d_face[d] != BADFACE)
X			waddstr(dstatwin, combname(dice.d_comb[d]));
X	}
X
X/*
X**	CENTER: macro to center messages under the dice
X*/
X#define	CENTER(m) {										\
X	if((d = (DSTATWID-1)/2 - strlen(m)/2) < 0) d = 0;	\
X	wprintw(dstatwin, "%*s%s", d, "", m);				\
X}
X
X	wmove(dstatwin, 2, 0);
X	wclrtoeol(dstatwin);
X	if(dice.d_mesg[0] != '\0')
X		CENTER(dice.d_mesg);
X
X	wmove(dstatwin, 3, 0);
X	wclrtoeol(dstatwin);
X	if(dice.d_pts_turn > 0) {
X		if(dice.d_mesg[0] == '\0' || strcmp(dice.d_mesg, "nothing") == 0)
X			/* do nothing */;
X		else if(dice.d_pts_turn < dice.d_pts_max
X				&& strcmp(dice.d_mesg, "held") == 0) {
X			sprintf(msgbuf, "%d saved, %d given away",
X				dice.d_pts_turn, dice.d_pts_max - dice.d_pts_turn);
X			CENTER(msgbuf);
X		} else {
X			sprintf(msgbuf, "plus %d saved", dice.d_pts_turn);
X			CENTER(msgbuf);
X		}
X	} else if(dice.d_pts_roll < 0) {
X		if(dice.d_pts_max > -dice.d_pts_roll)
X			sprintf(msgbuf, "rolled away %d points", dice.d_pts_max);
X		else
X			sprintf(msgbuf, "threw away %d points", -dice.d_pts_roll);
X		CENTER(msgbuf);
X	}
X
X	wmove(dstatwin, 4, 0);
X	wclrtoeol(dstatwin);
X	for(d = 0;d < NDICE;++d)
X		if(dice.d_comb[d] == Nothing)
X			break;
X	if(d == NDICE) {
X		strcpy(msgbuf, "scored with all dice");
X		CENTER(msgbuf);
X	} else if(dice.d_pts_roll < 0 && dice.d_pts_max > 0 && currup >= 0) {
X		sprintf(msgbuf, "squandered %d so far",
X			dice.d_pts_max + plr[currup].p_squander);
X		CENTER(msgbuf);
X	}
X
X	WREFRESH(dstatwin);
X	return 0;
X
X#undef	CENTER
X}
X
X/*
X**	facename: names for the die faces
X**		All names centered in nine or fifteen spaces.
X*/
Xstatic char *
Xfacename(face)
Xint		face;
X{
X	switch(face) {
X	/*				return wide==True ? "123456789012345" : "123456789";	*/
X	case BADFACE:	return wide==True ? "    in hand    " : " in hand ";
X	case ACE:		return wide==True ? "      one      " : "   one   ";
X	case 2:			return wide==True ? "      two      " : "   two   ";
X	case 3:			return wide==True ? "     three     " : "  three  ";
X	case 4:			return wide==True ? "      four     " : "   four  ";
X	case FIVE:		return wide==True ? "      five     " : "   five  ";
X	case 6:			return wide==True ? "      six      " : "   six   ";
X	case JOKER:		return wide==True ? "     joker     " : "  joker  ";
X	case CLEARDIE:	return wide==True ? "               " : "         ";
X	default:		return wide==True ? "       ?       " : "    ?    ";
X	}
X};
X
X/*
X**	combname: return pointer to string describing combination
X**		all names are centered in nine or fifteen spaces
X*/
Xstatic char *
Xcombname(comb)
Xcombination	comb;
X{
X	switch(comb) {
X	/*					 return wide==True ? "123456789012345" : "123456789"; */
X	case Previous:		 return wide==True ? "       +       " : "    +    ";
X	case Nothing:		 return wide==True ? "               " : "         ";
X	case Joker:			 return wide==True ? "     joker     " : "  joker  ";
X	case Five:			 return wide==True ? "      five     " : "   five  ";
X	case Ace:			 return wide==True ? "      one      " : "   one   ";
X	case Three_of_a_kind:return wide==True ? "    3o'kind    " : " 3o'kind ";
X	case Small_straight: return wide==True ? "     small     " : "  small  ";
X	case Four_of_a_kind: return wide==True ? "    4o'kind    " : " 4o'kind ";
X	case Asm_straight:	 return wide==True ? "    straight   " : " straight";
X	case Straight:		 return wide==True ? "    straight   " : " straight";
X	case All_of_a_kind:	 return wide==True ? "    5o'kind    " : " 5o'kind ";
X	default:			 return wide==True ? "       ?       " : "    ?    ";
X	}
X}
X
X/*
X**	draw1die: draw die number d
X*/
Xdraw1die(d)
Xint		d;
X{
X	register int	row, w, s;
X	static int		showing[NDICE] = {	CLEARDIE, CLEARDIE, CLEARDIE,
X	/* XXX: assumes NDICE==5 */			CLEARDIE, CLEARDIE };
X
X	s = dice.d_face[d];
X
X	/*
X	**	If this die face is already showing, there's nothing more to do.
X	*/
X	if(showing[d] == s)
X		return;
X	showing[d] = s;
X
X	/*
X	**	Draw die and refresh it.  Two possible orientations per die.
X	*/
X	if(graphics != Nographics)
X		wstandout(die[d]);
X	w = 2 * s;
X	if(randint(128) > 64)
X		++w;
X	for(row = 0;row < DIELINES;++row) {
X		wmove(die[d], row, 0);
X		waddstr(die[d], dieface[w][row]);
X	}
X	WREFRESH(die[d]);
X
X	/*
X	**	Update die face name.
X	*/
X	wmove(dfacewin, 0, d * DIEWID);
X	waddstr(dfacewin, facename(s));
X	WREFRESH(dfacewin);
X}
X
X/*
X**	outc: write a single character
X*/
Xstatic
Xoutc(c)
Xchar	c;
X{
X	return write(_tty_ch, &c, 1);
X}
X
X/*
X**	mybeep: send a number of beeps
X*/
Xmybeep(num)
X{
X	while(num-- > 0) {
X		(void) outc('\007');
X		if(num > 0) {
X			static struct timeval	tv = { 0L, 250000L };
X			(void) select(32, NOSEL, NOSEL, NOSEL, &tv);
X		}
X	}
X}
X
X/*
X**	neutral: return cursor to a neutral position
X*/
Xneutral()
X{
X	wmove(promptwin, 0, 0);
X	WREFRESH(promptwin);
X}
X
X/*
X**	redraw: cause the screen to be redrawn
X*/
Xredraw()
X{
X	/*
X	**	Reinitialize the terminal for curses mode.
X	*/
X	tputs(TI, 1, outc);
X	tputs(VS, 1, outc);
X
X	/*
X	**	Ensure that the screen redraws.
X	*/
X	touchwin(curscr);
X	clearok(curscr, TRUE);
X
X	/*
X	**	Since we're redrawing, we have to assume that the screen got
X	**	messed up.  This could mean that the screen is in standout
X	**	mode when curses thinks it isn't, or vice versa.  We try to
X	**	synchronize by sending the standout-end sequence and by
X	**	drawing a non-graphics character at position 0,0.
X	*/
X	if(graphics != Nographics) {
X		int	y, x;
X
X		tputs(SE, 1, outc);
X		standend();
X		getyx(stdscr, y, x);
X		mvaddch(0, 0, '*');
X		REFRESH();
X		mvaddch(0, 0, ' ');
X		move(y, x);
X	}
X
X	/*
X	**	Refresh the screen.
X	*/
X	REFRESH();
X}
X
X/*
X**	sigttin: handle SIGTTIN signal
X*/
Xstatic int
Xsigttin()
X{
X	static int	bgreads	= 0;
X
X	/*
X	**	We shouldn't get this signal, but I have seen a burst of
X	**	noise cause a screwup where the shell thought cubes was
X	**	suspended, but cubes thought it wasn't.  Very bad.  We
X	**	try to handle this situation here.
X	*/
X	if(quiet == False) {
X		(void) kill(0, SIGTSTP);	/* suspend process group */
X		return;
X	}
X
X	/*
X	**	Reaching here can only happen if we are erroneously doing
X	**	reads while in the background.  This would be a problem with
X	**	the code.  What to do?  Let's ignore it a few times then
X	**	exit prematurely.  Sorry, folks.
X	*/
X	if(bgreads++ > 5)
X		cleanup(1, "too many background reads");
X}
X
X/*
X**	suspend: catch suspend signal
X*/
Xstatic int
Xsuspend()
X{
X	if(quiet == False)
X		beginquiet();
X}
X
X/*
X**	unsuspend: catch continue signal
X*/
Xstatic int
Xunsuspend()
X{
X	if(quiet == False)
X		return;
X
X	/*
X	**	If we got put in the background (erroneously) by the user,
X	**	then kill the process group as if output attempted from the
X	**	background with "stty tstp" set.
X	*/
X	if(inbackground() == True) {
X		(void) killpg(mypgrp, SIGTTOU);
X		return;
X	}
X
X	endquiet();
X}
X
X/*
X**	inbackground: returns True if process is in background
X**		side effect: sets mypgrp
X*/
Xstatic boolean
Xinbackground()
X{
X	int		ttypgrp;
X
X	mypgrp = getpgrp(0);
X
X	if(ioctl(_tty_ch, TIOCGPGRP, (char *)&ttypgrp) < 0) {
X		perror("ioctl: TIOCGPGRP");
X		return True;	/* error */
X	}
X
X	return ttypgrp != mypgrp ? True : False;
X}
X
Xstatic int	ttyflags;	/* set in beginquiet, used in endquiet */
X
X/*
X**	beginquiet: enter quiet mode
X*/
Xstatic int
Xbeginquiet()
X{
X	quiet = True;
X	ttyflags = _tty.sg_flags;
X	endwin();
X	if(graphics != Nographics)
X		tputs(SE, 1, outc);
X	tputs(VE, 1, outc);
X	tputs(TE, 1, outc);
X	tputs(CL, LINES, outc);
X}
X
X/*
X**	endquiet: leave quiet mode
X*/
Xstatic int
Xendquiet()
X{
X	quiet = False;
X
X	/*
X	**	Return terminal settings to what we want.
X	*/
X	savetty();
X	_tty.sg_flags = ttyflags;
X	(void) ioctl(_tty_ch, TIOCSETN, (char *)&_tty);
X	redraw();
X	neutral();
X}
END_OF_FILE
if test 46584 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
echo shar: End of archive 1 \(of 8\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0