Home | History | Annotate | Line # | Download | only in sail
      1 /*	$NetBSD: pl_7.c,v 1.42 2011/08/26 06:18:18 dholland Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1983, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)pl_7.c	8.1 (Berkeley) 5/31/93";
     36 #else
     37 __RCSID("$NetBSD: pl_7.c,v 1.42 2011/08/26 06:18:18 dholland Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include <curses.h>
     42 #include <err.h>
     43 #include <errno.h>
     44 #include <signal.h>
     45 #include <stdarg.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <string.h>
     49 #include <unistd.h>
     50 #include "array.h"
     51 #include "extern.h"
     52 #include "player.h"
     53 #include "display.h"
     54 
     55 /*
     56  * Use values above KEY_MAX for custom keycodes. (blymn@ says this is ok)
     57  */
     58 #define KEY_ESC(ch) (KEY_MAX+10+ch)
     59 
     60 
     61 /*
     62  * Display interface
     63  */
     64 
     65 static void draw_view(void);
     66 static void draw_turn(void);
     67 static void draw_stat(void);
     68 static void draw_slot(void);
     69 static void draw_board(void);
     70 
     71 static struct stringarray *sc_lines;
     72 static unsigned sc_scrollup;
     73 static bool sc_hasprompt;
     74 static bool sc_hideprompt;
     75 static const char *sc_prompt;
     76 static const char *sc_buf;
     77 
     78 static WINDOW *view_w;
     79 static WINDOW *turn_w;
     80 static WINDOW *stat_w;
     81 static WINDOW *slot_w;
     82 static WINDOW *scroll_w;
     83 
     84 static bool obp[3];
     85 static bool dbp[3];
     86 
     87 int done_curses;
     88 static bool ingame;
     89 int loaded, fired, changed, repaired;
     90 int dont_adjust;
     91 static int viewrow, viewcol;
     92 char movebuf[sizeof SHIP(0)->file->movebuf];
     93 int player;
     94 struct ship *ms;		/* memorial structure, &cc->ship[player] */
     95 struct File *mf;		/* ms->file */
     96 struct shipspecs *mc;		/* ms->specs */
     97 
     98 ////////////////////////////////////////////////////////////
     99 // overall initialization
    100 
    101 static
    102 void
    103 define_esc_key(int ch)
    104 {
    105 	char seq[3] = { '\x1b', ch, 0 };
    106 
    107 	define_key(seq, KEY_ESC(ch));
    108 }
    109 
    110 void
    111 initscreen(void)
    112 {
    113 	int ch;
    114 
    115 	sc_lines = stringarray_create();
    116 	if (sc_lines == NULL) {
    117 		err(1, "malloc");
    118 	}
    119 
    120 	if (signal(SIGTSTP, SIG_DFL) == SIG_ERR) {
    121 		err(1, "signal(SIGTSTP)");
    122 	}
    123 
    124 	if (initscr() == NULL) {
    125 		errx(1, "Can't sail on this terminal.");
    126 	}
    127 	if (STAT_R >= COLS || SCROLL_Y <= 0) {
    128 		errx(1, "Window/terminal not large enough.");
    129 	}
    130 
    131 	view_w = newwin(VIEW_Y, VIEW_X, VIEW_T, VIEW_L);
    132 	slot_w = newwin(SLOT_Y, SLOT_X, SLOT_T, SLOT_L);
    133 	scroll_w = newwin(SCROLL_Y, SCROLL_X, SCROLL_T, SCROLL_L);
    134 	stat_w = newwin(STAT_Y, STAT_X, STAT_T, STAT_L);
    135 	turn_w = newwin(TURN_Y, TURN_X, TURN_T, TURN_L);
    136 
    137 	if (view_w == NULL ||
    138 	    slot_w == NULL ||
    139 	    scroll_w == NULL ||
    140 	    stat_w == NULL ||
    141 	    turn_w == NULL) {
    142 		endwin();
    143 		errx(1, "Curses initialization failed.");
    144 	}
    145 
    146 	leaveok(view_w, 1);
    147 	leaveok(slot_w, 1);
    148 	leaveok(stat_w, 1);
    149 	leaveok(turn_w, 1);
    150 	noecho();
    151 	cbreak();
    152 
    153 	/*
    154 	 * Define esc-x keys
    155 	 */
    156 #if 0
    157 	for (ch = 0; ch < 127; ch++) {
    158 		if (ch != '[' && ch != 'O') {
    159 			define_esc_key(ch);
    160 		}
    161 	}
    162 #else
    163 	(void)ch;
    164 	(void)define_esc_key;
    165 #endif
    166 
    167 	keypad(stdscr, 1);
    168 	keypad(view_w, 1);
    169 	keypad(slot_w, 1);
    170 	keypad(scroll_w, 1);
    171 	keypad(stat_w, 1);
    172 	keypad(turn_w, 1);
    173 
    174 	done_curses++;
    175 }
    176 
    177 void
    178 cleanupscreen(void)
    179 {
    180 	/* alarm already turned off */
    181 	if (done_curses) {
    182 		if (ingame) {
    183 			wmove(scroll_w, SCROLL_Y - 1, 0);
    184 			wclrtoeol(scroll_w);
    185 			display_redraw();
    186 		} else {
    187 			move(LINES-1, 0);
    188 			clrtoeol();
    189 		}
    190 		endwin();
    191 	}
    192 }
    193 
    194 ////////////////////////////////////////////////////////////
    195 // curses utility functions
    196 
    197 /*
    198  * fill to eol with spaces
    199  * (useful with A_REVERSE since cleartoeol() does not clear to reversed)
    200  */
    201 static void
    202 filltoeol(void)
    203 {
    204 	int x;
    205 
    206 	for (x = getcurx(stdscr); x < COLS; x++) {
    207 		addch(' ');
    208 	}
    209 }
    210 
    211 /*
    212  * Add a maybe-selected string.
    213  *
    214  * Place strings starting at (Y0, X0); this is string ITEM; CURITEM
    215  * is the selected one; WIDTH is the total width. STR is the string.
    216  */
    217 static void
    218 mvaddselstr(int y, int x0, int item, int curitem,
    219 	    size_t width, const char *str)
    220 {
    221 	size_t i, len;
    222 
    223 	len = strlen(str);
    224 
    225 	move(y, x0);
    226 	if (curitem == item) {
    227 		attron(A_REVERSE);
    228 	}
    229 	addstr(str);
    230 	if (curitem == item) {
    231 		for (i=len; i<width; i++) {
    232 			addch(' ');
    233 		}
    234 		attroff(A_REVERSE);
    235 	}
    236 }
    237 
    238 /*
    239  * Likewise but a printf.
    240  */
    241 static void __printflike(6, 7)
    242 mvselprintw(int y, int x0, int item, int curitem,
    243 	    size_t width, const char *fmt, ...)
    244 {
    245 	va_list ap;
    246 	size_t x;
    247 
    248 	move(y, x0);
    249 	if (curitem == item) {
    250 		attron(A_REVERSE);
    251 	}
    252 	va_start(ap, fmt);
    253 	vwprintw(stdscr, fmt, ap);
    254 	va_end(ap);
    255 	if (curitem == item) {
    256 		for (x = getcurx(stdscr); x < x0 + width; x++) {
    257 			addch(' ');
    258 		}
    259 		attroff(A_REVERSE);
    260 	}
    261 }
    262 
    263 /*
    264  * Move up by 1, scrolling if needed.
    265  */
    266 static void
    267 up(int *posp, int *scrollp)
    268 {
    269 	if (*posp > 0) {
    270 		(*posp)--;
    271 	}
    272 	if (scrollp != NULL) {
    273 		if (*posp < *scrollp) {
    274 			*scrollp = *posp;
    275 		}
    276 	}
    277 }
    278 
    279 /*
    280  * Move down by 1, scrolling if needed. MAX is the total number of
    281  * items; VISIBLE is the number that can be visible at once.
    282  */
    283 static void
    284 down(int *posp, int *scrollp, int max, int visible)
    285 {
    286 	if (max > 0 && *posp < max - 1) {
    287 		(*posp)++;
    288 	}
    289 	if (scrollp != NULL) {
    290 		if (*posp > *scrollp + visible - 1) {
    291 			*scrollp = *posp - visible + 1;
    292 		}
    293 	}
    294 }
    295 
    296 /*
    297  * Complain briefly.
    298  */
    299 static void __printflike(3, 4)
    300 oops(int y, int x, const char *fmt, ...)
    301 {
    302 	int oy, ox;
    303 	va_list ap;
    304 
    305 	oy = getcury(stdscr);
    306 	ox = getcurx(stdscr);
    307 	move(y, x);
    308 	va_start(ap, fmt);
    309 	vwprintw(stdscr, fmt, ap);
    310 	va_end(ap);
    311 	move(oy, ox);
    312 	wrefresh(stdscr);
    313 	sleep(1);
    314 }
    315 
    316 ////////////////////////////////////////////////////////////
    317 // scrolling message area
    318 
    319 static void
    320 scrollarea_add(const char *text)
    321 {
    322 	char *copy;
    323 	int errsave;
    324 
    325 	copy = strdup(text);
    326 	if (copy == NULL) {
    327 		goto nomem;
    328 	}
    329 	if (stringarray_add(sc_lines, copy, NULL)) {
    330 		goto nomem;
    331 	}
    332 	return;
    333 
    334 nomem:
    335 	/*
    336 	 * XXX this should use leave(), but that won't
    337 	 * currently work right.
    338 	 */
    339 	errsave = errno;
    340 #if 0
    341 	leave(LEAVE_MALLOC);
    342 #else
    343 	cleanupscreen();
    344 	sync_close(!hasdriver);
    345 	errno = errsave;
    346 	err(1, "malloc");
    347 #endif
    348 }
    349 
    350 static void
    351 draw_scroll(void)
    352 {
    353 	unsigned total_lines;
    354 	unsigned visible_lines;
    355 	unsigned index_of_top;
    356 	unsigned index_of_y;
    357 	unsigned y;
    358 	unsigned cursorx;
    359 
    360 	werase(scroll_w);
    361 
    362 	/* XXX: SCROLL_Y and whatnot should be unsigned too */
    363 	visible_lines = SCROLL_Y - 1;
    364 
    365 	total_lines = stringarray_num(sc_lines);
    366 	if (total_lines > visible_lines) {
    367 		index_of_top = total_lines - visible_lines;
    368 	} else {
    369 		index_of_top = 0;
    370 	}
    371 	if (index_of_top < sc_scrollup) {
    372 		index_of_top = 0;
    373 	} else {
    374 		index_of_top -= sc_scrollup;
    375 	}
    376 
    377 	for (y = 0; y < visible_lines; y++) {
    378 		index_of_y = index_of_top + y;
    379 		if (index_of_y >= total_lines) {
    380 			break;
    381 		}
    382 		wmove(scroll_w, y, 0);
    383 		waddstr(scroll_w, stringarray_get(sc_lines, index_of_y));
    384 	}
    385 	if (sc_hasprompt && !sc_hideprompt) {
    386 		wmove(scroll_w, SCROLL_Y-1, 0);
    387 		waddstr(scroll_w, sc_prompt);
    388 		waddstr(scroll_w, sc_buf);
    389 		cursorx = strlen(sc_prompt) + strlen(sc_buf);
    390 		wmove(scroll_w, SCROLL_Y-1, cursorx);
    391 	}
    392 	else {
    393 		wmove(scroll_w, SCROLL_Y-1, 0);
    394 	}
    395 }
    396 
    397 /*VARARGS2*/
    398 void
    399 Signal(const char *fmt, struct ship *ship, ...)
    400 {
    401 	va_list ap;
    402 	char format[BUFSIZ];
    403 	char buf[BUFSIZ];
    404 
    405 	if (!done_curses)
    406 		return;
    407 	va_start(ap, ship);
    408 	if (*fmt == '\a') {
    409 		beep();
    410 		fmt++;
    411 	}
    412 	fmtship(format, sizeof(format), fmt, ship);
    413 	vsnprintf(buf, sizeof(buf), format, ap);
    414 	va_end(ap);
    415 	scrollarea_add(buf);
    416 }
    417 
    418 /*VARARGS2*/
    419 void
    420 Msg(const char *fmt, ...)
    421 {
    422 	va_list ap;
    423 	char buf[BUFSIZ];
    424 
    425 	if (!done_curses)
    426 		return;
    427 	va_start(ap, fmt);
    428 	if (*fmt == '\a') {
    429 		beep();
    430 		fmt++;
    431 	}
    432 	vsnprintf(buf, sizeof(buf), fmt, ap);
    433 	va_end(ap);
    434 	scrollarea_add(buf);
    435 }
    436 
    437 static void
    438 prompt(const char *p, struct ship *ship)
    439 {
    440 	static char buf[BUFSIZ];
    441 
    442 	fmtship(buf, sizeof(buf), p, ship);
    443 	sc_prompt = buf;
    444 	sc_buf = "";
    445 	sc_hasprompt = true;
    446 }
    447 
    448 static void
    449 endprompt(void)
    450 {
    451 	sc_prompt = NULL;
    452 	sc_buf = NULL;
    453 	sc_hasprompt = false;
    454 }
    455 
    456 /*
    457  * Next two functions called from newturn() to poke display. Shouldn't
    458  * exist... XXX
    459  */
    460 
    461 void
    462 display_hide_prompt(void)
    463 {
    464 	sc_hideprompt = true;
    465 	draw_scroll();
    466 	wrefresh(scroll_w);
    467 }
    468 
    469 void
    470 display_reshow_prompt(void)
    471 {
    472 	sc_hideprompt = false;
    473 	draw_scroll();
    474 	wrefresh(scroll_w);
    475 }
    476 
    477 
    478 int
    479 sgetch(const char *p, struct ship *ship, int flag)
    480 {
    481 	int c;
    482 	char input[2];
    483 
    484 	prompt(p, ship);
    485 	input[0] = '\0';
    486 	input[1] = '\0';
    487 	sc_buf = input;
    488 	blockalarm();
    489 	draw_scroll();
    490 	wrefresh(scroll_w);
    491 	fflush(stdout);
    492 	unblockalarm();
    493 	while ((c = wgetch(scroll_w)) == EOF)
    494 		;
    495 	if (flag && c >= ' ' && c < 0x7f) {
    496 		blockalarm();
    497 		input[0] = c;
    498 		draw_scroll();
    499 		wrefresh(scroll_w);
    500 		fflush(stdout);
    501 		unblockalarm();
    502 	}
    503 	endprompt();
    504 	return c;
    505 }
    506 
    507 void
    508 sgetstr(const char *pr, char *buf, int n)
    509 {
    510 	int c;
    511 	char *p = buf;
    512 
    513 	prompt(pr, (struct ship *)0);
    514 	sc_buf = buf;
    515 	for (;;) {
    516 		*p = 0;
    517 		blockalarm();
    518 		draw_scroll();
    519 		wrefresh(scroll_w);
    520 		fflush(stdout);
    521 		unblockalarm();
    522 		while ((c = wgetch(scroll_w)) == EOF)
    523 			;
    524 		switch (c) {
    525 		case '\n':
    526 		case '\r':
    527 			endprompt();
    528 			return;
    529 		case '\b':
    530 			if (p > buf) {
    531 				/*waddstr(scroll_w, "\b \b");*/
    532 				p--;
    533 			}
    534 			break;
    535 		default:
    536 			if (c >= ' ' && c < 0x7f && p < buf + n - 1) {
    537 				*p++ = c;
    538 				/*waddch(scroll_w, c);*/
    539 			} else
    540 				beep();
    541 		}
    542 	}
    543 }
    544 
    545 ////////////////////////////////////////////////////////////
    546 // drawing of other panes
    547 
    548 void
    549 display_force_full_redraw(void)
    550 {
    551 	clear();
    552 }
    553 
    554 void
    555 display_redraw(void)
    556 {
    557 	draw_board();
    558 	draw_view();
    559 	draw_turn();
    560 	draw_stat();
    561 	draw_slot();
    562 	draw_scroll();
    563 	/* move the cursor */
    564 	wrefresh(scroll_w);
    565 	/* paranoia */
    566 	fflush(stdout);
    567 }
    568 
    569 static void
    570 draw_view(void)
    571 {
    572 	struct ship *sp;
    573 
    574 	werase(view_w);
    575 	foreachship(sp) {
    576 		if (sp->file->dir
    577 		    && sp->file->row > viewrow
    578 		    && sp->file->row < viewrow + VIEW_Y
    579 		    && sp->file->col > viewcol
    580 		    && sp->file->col < viewcol + VIEW_X) {
    581 			wmove(view_w, sp->file->row - viewrow,
    582 				sp->file->col - viewcol);
    583 			waddch(view_w, colours(sp));
    584 			wmove(view_w,
    585 				sternrow(sp) - viewrow,
    586 				sterncol(sp) - viewcol);
    587 			waddch(view_w, sterncolour(sp));
    588 		}
    589 	}
    590 	wrefresh(view_w);
    591 }
    592 
    593 static void
    594 draw_turn(void)
    595 {
    596 	wmove(turn_w, 0, 0);
    597 	wprintw(turn_w, "%cTurn %d", dont_adjust?'*':'-', turn);
    598 	wrefresh(turn_w);
    599 }
    600 
    601 static void
    602 draw_stat(void)
    603 {
    604 	wmove(stat_w, STAT_1, 0);
    605 	wprintw(stat_w, "Points  %3d\n", mf->points);
    606 	wprintw(stat_w, "Fouls    %2d\n", fouled(ms));
    607 	wprintw(stat_w, "Grapples %2d\n", grappled(ms));
    608 
    609 	wmove(stat_w, STAT_2, 0);
    610 	wprintw(stat_w, "    0 %c(%c)\n",
    611 		maxmove(ms, winddir + 3, -1) + '0',
    612 		maxmove(ms, winddir + 3, 1) + '0');
    613 	waddstr(stat_w, "   \\|/\n");
    614 	wprintw(stat_w, "   -^-%c(%c)\n",
    615 		maxmove(ms, winddir + 2, -1) + '0',
    616 		maxmove(ms, winddir + 2, 1) + '0');
    617 	waddstr(stat_w, "   /|\\\n");
    618 	wprintw(stat_w, "    | %c(%c)\n",
    619 		maxmove(ms, winddir + 1, -1) + '0',
    620 		maxmove(ms, winddir + 1, 1) + '0');
    621 	wprintw(stat_w, "   %c(%c)\n",
    622 		maxmove(ms, winddir, -1) + '0',
    623 		maxmove(ms, winddir, 1) + '0');
    624 
    625 	wmove(stat_w, STAT_3, 0);
    626 	wprintw(stat_w, "Load  %c%c %c%c\n",
    627 		loadname[mf->loadL], readyname(mf->readyL),
    628 		loadname[mf->loadR], readyname(mf->readyR));
    629 	wprintw(stat_w, "Hull %2d\n", mc->hull);
    630 	wprintw(stat_w, "Crew %2d %2d %2d\n",
    631 		mc->crew1, mc->crew2, mc->crew3);
    632 	wprintw(stat_w, "Guns %2d %2d\n", mc->gunL, mc->gunR);
    633 	wprintw(stat_w, "Carr %2d %2d\n", mc->carL, mc->carR);
    634 	wprintw(stat_w, "Rigg %d %d %d ", mc->rig1, mc->rig2, mc->rig3);
    635 	if (mc->rig4 < 0)
    636 		waddch(stat_w, '-');
    637 	else
    638 		wprintw(stat_w, "%d", mc->rig4);
    639 	wrefresh(stat_w);
    640 }
    641 
    642 void
    643 draw_slot(void)
    644 {
    645 	int i;
    646 
    647 	if (!boarding(ms, 0)) {
    648 		mvwaddstr(slot_w, 0, 0, "   ");
    649 		mvwaddstr(slot_w, 1, 0, "   ");
    650 	} else {
    651 		wmove(slot_w, 0, 0);
    652 		for (i = 0; i < 3; i++) {
    653 			waddch(slot_w, obp[i] ? '1'+i : ' ');
    654 		}
    655 		mvwaddstr(slot_w, 1, 0, "OBP");
    656 	}
    657 	if (!boarding(ms, 1)) {
    658 		mvwaddstr(slot_w, 2, 0, "   ");
    659 		mvwaddstr(slot_w, 3, 0, "   ");
    660 	} else {
    661 		wmove(slot_w, 2, 0);
    662 		for (i = 0; i < 3; i++) {
    663 			waddch(slot_w, dbp[i] ? '1'+i : ' ');
    664 		}
    665 		mvwaddstr(slot_w, 3, 0, "DBP");
    666 	}
    667 
    668 	wmove(slot_w, SLOT_Y-4, 0);
    669 	if (mf->RH)
    670 		wprintw(slot_w, "%dRH", mf->RH);
    671 	else
    672 		waddstr(slot_w, "   ");
    673 	wmove(slot_w, SLOT_Y-3, 0);
    674 	if (mf->RG)
    675 		wprintw(slot_w, "%dRG", mf->RG);
    676 	else
    677 		waddstr(slot_w, "   ");
    678 	wmove(slot_w, SLOT_Y-2, 0);
    679 	if (mf->RR)
    680 		wprintw(slot_w, "%dRR", mf->RR);
    681 	else
    682 		waddstr(slot_w, "   ");
    683 
    684 #define Y	(SLOT_Y/2)
    685 	wmove(slot_w, 7, 1);
    686 	wprintw(slot_w,"%d", windspeed);
    687 	mvwaddch(slot_w, Y, 0, ' ');
    688 	mvwaddch(slot_w, Y, 2, ' ');
    689 	mvwaddch(slot_w, Y-1, 0, ' ');
    690 	mvwaddch(slot_w, Y-1, 1, ' ');
    691 	mvwaddch(slot_w, Y-1, 2, ' ');
    692 	mvwaddch(slot_w, Y+1, 0, ' ');
    693 	mvwaddch(slot_w, Y+1, 1, ' ');
    694 	mvwaddch(slot_w, Y+1, 2, ' ');
    695 	wmove(slot_w, Y - dr[winddir], 1 - dc[winddir]);
    696 	switch (winddir) {
    697 	case 1:
    698 	case 5:
    699 		waddch(slot_w, '|');
    700 		break;
    701 	case 2:
    702 	case 6:
    703 		waddch(slot_w, '/');
    704 		break;
    705 	case 3:
    706 	case 7:
    707 		waddch(slot_w, '-');
    708 		break;
    709 	case 4:
    710 	case 8:
    711 		waddch(slot_w, '\\');
    712 		break;
    713 	}
    714 	mvwaddch(slot_w, Y + dr[winddir], 1 + dc[winddir], '+');
    715 	wrefresh(slot_w);
    716 }
    717 
    718 void
    719 draw_board(void)
    720 {
    721 	int n;
    722 
    723 	erase();
    724 	werase(view_w);
    725 	werase(slot_w);
    726 	werase(scroll_w);
    727 	werase(stat_w);
    728 	werase(turn_w);
    729 
    730 	move(BOX_T, BOX_L);
    731 	for (n = 0; n < BOX_X; n++)
    732 		addch('-');
    733 	move(BOX_B, BOX_L);
    734 	for (n = 0; n < BOX_X; n++)
    735 		addch('-');
    736 	for (n = BOX_T+1; n < BOX_B; n++) {
    737 		mvaddch(n, BOX_L, '|');
    738 		mvaddch(n, BOX_R, '|');
    739 	}
    740 	mvaddch(BOX_T, BOX_L, '+');
    741 	mvaddch(BOX_T, BOX_R, '+');
    742 	mvaddch(BOX_B, BOX_L, '+');
    743 	mvaddch(BOX_B, BOX_R, '+');
    744 	refresh();
    745 
    746 #if 0
    747 #define WSaIM "Wooden Ships & Iron Men"
    748 	wmove(view_w, 2, (VIEW_X - sizeof WSaIM - 1) / 2);
    749 	waddstr(view_w, WSaIM);
    750 	wmove(view_w, 4, (VIEW_X - strlen(cc->name)) / 2);
    751 	waddstr(view_w, cc->name);
    752 	wrefresh(view_w);
    753 #endif
    754 
    755 	move(LINE_T, LINE_L);
    756 	printw("Class %d %s (%d guns) '%s' (%c%c)",
    757 		mc->class,
    758 		classname[mc->class],
    759 		mc->guns,
    760 		ms->shipname,
    761 		colours(ms),
    762 		sterncolour(ms));
    763 	refresh();
    764 }
    765 
    766 void
    767 display_set_obp(int which, bool show)
    768 {
    769 	obp[which] = show;
    770 }
    771 
    772 void
    773 display_set_dbp(int which, bool show)
    774 {
    775 	dbp[which] = show;
    776 }
    777 
    778 ////////////////////////////////////////////////////////////
    779 // external actions on the display
    780 
    781 void
    782 display_scroll_pageup(void)
    783 {
    784 	unsigned total_lines, visible_lines, limit;
    785 	unsigned pagesize = SCROLL_Y - 2;
    786 
    787 	total_lines = stringarray_num(sc_lines);
    788 	visible_lines = SCROLL_Y - 1;
    789 	limit = total_lines - visible_lines;
    790 
    791 	sc_scrollup += pagesize;
    792 	if (sc_scrollup > limit) {
    793 		sc_scrollup = limit;
    794 	}
    795 }
    796 
    797 void
    798 display_scroll_pagedown(void)
    799 {
    800 	unsigned pagesize = SCROLL_Y - 2;
    801 
    802 	if (sc_scrollup < pagesize) {
    803 		sc_scrollup = 0;
    804 	} else {
    805 		sc_scrollup -= pagesize;
    806 	}
    807 }
    808 
    809 void
    810 centerview(void)
    811 {
    812 	viewrow = mf->row - VIEW_Y / 2;
    813 	viewcol = mf->col - VIEW_X / 2;
    814 }
    815 
    816 void
    817 upview(void)
    818 {
    819 	viewrow -= VIEW_Y / 3;
    820 }
    821 
    822 void
    823 downview(void)
    824 {
    825 	viewrow += VIEW_Y / 3;
    826 }
    827 
    828 void
    829 leftview(void)
    830 {
    831 	viewcol -= VIEW_X / 5;
    832 }
    833 
    834 void
    835 rightview(void)
    836 {
    837 	viewcol += VIEW_X / 5;
    838 }
    839 
    840 /* Called from newturn()... rename? */
    841 void
    842 display_adjust_view(void)
    843 {
    844 	if (dont_adjust)
    845 		return;
    846 	if (mf->row < viewrow + VIEW_Y/4)
    847 		viewrow = mf->row - (VIEW_Y - VIEW_Y/4);
    848 	else if (mf->row > viewrow + (VIEW_Y - VIEW_Y/4))
    849 		viewrow = mf->row - VIEW_Y/4;
    850 	if (mf->col < viewcol + VIEW_X/8)
    851 		viewcol = mf->col - (VIEW_X - VIEW_X/8);
    852 	else if (mf->col > viewcol + (VIEW_X - VIEW_X/8))
    853 		viewcol = mf->col - VIEW_X/8;
    854 }
    855 
    856 ////////////////////////////////////////////////////////////
    857 // starting game
    858 
    859 static bool shipselected;
    860 static int loadpos;
    861 
    862 static int
    863 nextload(int load)
    864 {
    865 	switch (load) {
    866 	    case L_ROUND: return L_GRAPE;
    867 	    case L_GRAPE: return L_CHAIN;
    868 	    case L_CHAIN: return L_DOUBLE;
    869 	    case L_DOUBLE: return L_ROUND;
    870 	}
    871 	return L_ROUND;
    872 }
    873 
    874 static int
    875 loadbychar(int ch)
    876 {
    877 	switch (ch) {
    878 	    case 'r': return L_ROUND;
    879 	    case 'g': return L_GRAPE;
    880 	    case 'c': return L_CHAIN;
    881 	    case 'd': return L_DOUBLE;
    882 	}
    883 	return L_ROUND;
    884 }
    885 
    886 static const char *
    887 loadstr(int load)
    888 {
    889 	switch (load) {
    890 	    case L_ROUND: return "round";
    891 	    case L_GRAPE: return "grape";
    892 	    case L_CHAIN: return "chain";
    893 	    case L_DOUBLE: return "double";
    894 	}
    895 	return "???";
    896 }
    897 
    898 static void
    899 displayshiplist(void)
    900 {
    901 	struct ship *sp;
    902 	int which;
    903 
    904 	erase();
    905 
    906 	attron(A_BOLD);
    907 	mvaddstr(1, 4, cc->name);
    908 	attroff(A_BOLD);
    909 
    910 	which = 0;
    911 	foreachship(sp) {
    912 		mvselprintw(which + 3, 4, which, player, 60,
    913 			    "  %2d:  %-10s %-15s  (%-2d pts)   %s",
    914 			    sp->file->index,
    915 			    countryname[sp->nationality],
    916 			    sp->shipname,
    917 			    sp->specs->pts,
    918 			    saywhat(sp, 1));
    919 		which++;
    920 	}
    921 
    922 	if (!shipselected) {
    923 		mvaddstr(15, 4, "Choose your ship");
    924 		move(player + 3, 63);
    925 	} else {
    926 		mvselprintw(15, 4, 0, loadpos, 32,
    927 			    "Initial left broadside: %s", loadstr(mf->loadL));
    928 		mvselprintw(16, 4, 1, loadpos, 32,
    929 			    "Initial right broadside: %s", loadstr(mf->loadR));
    930 		mvselprintw(17, 4, 2, loadpos, 32, "Set sail");
    931 		move(loadpos+15, 35);
    932 	}
    933 
    934 	wrefresh(stdscr);
    935 }
    936 
    937 static int
    938 pickship(void)
    939 {
    940 	struct File *fp;
    941 	struct ship *sp;
    942 	bool done;
    943 	int ch;
    944 
    945 	for (;;) {
    946 		foreachship(sp)
    947 			if (sp->file->captain[0] == 0 && !sp->file->struck
    948 			    && sp->file->captured == 0)
    949 				break;
    950 		if (sp >= ls) {
    951 			return -1;
    952 		}
    953 		player = sp - SHIP(0);
    954 		if (randomize) {
    955 			/* nothing */
    956 		} else {
    957 			done = false;
    958 			while (!done) {
    959 				displayshiplist();
    960 
    961 				ch = getch();
    962 				switch (ch) {
    963 				    case 12 /*^L*/:
    964 					clear();
    965 					break;
    966 				    case '\r':
    967 				    case '\n':
    968 					done = true;
    969 					break;
    970 				    case 7 /*^G*/:
    971 				    case 8 /*^H*/:
    972 				    case 27 /*ESC*/:
    973 				    case 127 /*^?*/:
    974 					beep();
    975 					break;
    976 				    case 16 /*^P*/:
    977 				    case KEY_UP:
    978 					up(&player, NULL);
    979 					break;
    980 				    case 14 /*^N*/:
    981 				    case KEY_DOWN:
    982 					down(&player, NULL, cc->vessels,
    983 					     cc->vessels);
    984 					break;
    985 				    default:
    986 					beep();
    987 					break;
    988 				}
    989 			}
    990 		}
    991 		if (player < 0)
    992 			continue;
    993 		if (Sync() < 0)
    994 			leave(LEAVE_SYNC);
    995 		fp = SHIP(player)->file;
    996 		if (fp->captain[0] || fp->struck || fp->captured != 0)
    997 			oops(16, 4, "That ship is taken.");
    998 		else
    999 			break;
   1000 	}
   1001 	return 0;
   1002 }
   1003 
   1004 static void
   1005 pickload(void)
   1006 {
   1007 	bool done;
   1008 	int ch;
   1009 
   1010 	mf->loadL = L_ROUND;
   1011 	mf->loadR = L_ROUND;
   1012 
   1013 	loadpos = 0;
   1014 	done = false;
   1015 	while (!done) {
   1016 		displayshiplist();
   1017 
   1018 		ch = getch();
   1019 		switch (ch) {
   1020 		    case 12 /*^L*/:
   1021 			clear();
   1022 			break;
   1023 		    case 'r':
   1024 		    case 'g':
   1025 		    case 'c':
   1026 		    case 'd':
   1027 			switch (loadpos) {
   1028 			    case 0: mf->loadL = loadbychar(ch); break;
   1029 			    case 1: mf->loadR = loadbychar(ch); break;
   1030 			    case 2: beep(); break;
   1031 			}
   1032 			break;
   1033 		    case '\r':
   1034 		    case '\n':
   1035 			switch (loadpos) {
   1036 			    case 0: mf->loadL = nextload(mf->loadL); break;
   1037 			    case 1: mf->loadR = nextload(mf->loadR); break;
   1038 			    case 2: done = true; break;
   1039 			}
   1040 			break;
   1041 		    case 7 /*^G*/:
   1042 		    case 8 /*^H*/:
   1043 		    case 27 /*ESC*/:
   1044 		    case 127 /*^?*/:
   1045 			beep();
   1046 			break;
   1047 		    case 16 /*^P*/:
   1048 		    case KEY_UP:
   1049 			up(&loadpos, NULL);
   1050 			break;
   1051 		    case 14 /*^N*/:
   1052 		    case KEY_DOWN:
   1053 			down(&loadpos, NULL, 3, 3);
   1054 			break;
   1055 		    default:
   1056 			beep();
   1057 			break;
   1058 		}
   1059 	}
   1060 	mf->readyR = R_LOADED|R_INITIAL;
   1061 	mf->readyL = R_LOADED|R_INITIAL;
   1062 }
   1063 
   1064 static void
   1065 startgame(void)
   1066 {
   1067 
   1068 	ingame = true;
   1069 	shipselected = false;
   1070 
   1071 	pl_main_init();
   1072 
   1073 	hasdriver = sync_exists(game);
   1074 	if (sync_open() < 0) {
   1075 		oops(21, 10, "syncfile: %s", strerror(errno));
   1076 		pl_main_uninit();
   1077 		ingame = false;
   1078 		return;
   1079 	}
   1080 
   1081 	if (hasdriver) {
   1082 		mvaddstr(21, 10, "Synchronizing with the other players...");
   1083 		wrefresh(stdscr);
   1084 		fflush(stdout);
   1085 		if (Sync() < 0)
   1086 			leave(LEAVE_SYNC);
   1087 	} else {
   1088 		mvaddstr(21, 10, "Starting driver...");
   1089 		wrefresh(stdscr);
   1090 		fflush(stdout);
   1091 		startdriver();
   1092 	}
   1093 
   1094 	if (pickship() < 0) {
   1095 		oops(21, 10, "All ships taken in that scenario.");
   1096 		sync_close(0);
   1097 		people = 0;
   1098 		pl_main_uninit();
   1099 		ingame = false;
   1100 		return;
   1101 	}
   1102 	shipselected = true;
   1103 
   1104 	ms = SHIP(player);
   1105 	mf = ms->file;
   1106 	mc = ms->specs;
   1107 
   1108 	pickload();
   1109 
   1110 	pl_main();
   1111 	ingame = false;
   1112 }
   1113 
   1114 ////////////////////////////////////////////////////////////
   1115 // scenario picker
   1116 
   1117 static int pickerpos;
   1118 static int pickerscroll;
   1119 
   1120 static const char *
   1121 absdirectionname(int dir)
   1122 {
   1123 	switch (dir) {
   1124 	    case 1: return "South";
   1125 	    case 2: return "Southwest";
   1126 	    case 3: return "West";
   1127 	    case 4: return "Northwest";
   1128 	    case 5: return "North";
   1129 	    case 6: return "Northeast";
   1130 	    case 7: return "East";
   1131 	    case 8: return "Southeast";
   1132 	}
   1133 	return "?";
   1134 }
   1135 
   1136 static const char *
   1137 windname(int wind)
   1138 {
   1139 	switch (wind) {
   1140 	    case 0: return "calm";
   1141 	    case 1: return "light breeze";
   1142 	    case 2: return "moderate breeze";
   1143 	    case 3: return "fresh breeze";
   1144 	    case 4: return "strong breeze";
   1145 	    case 5: return "gale";
   1146 	    case 6: return "full gale";
   1147 	    case 7: return "hurricane";
   1148 	}
   1149 	return "???";
   1150 }
   1151 
   1152 static const char *
   1153 nationalityname(int nationality)
   1154 {
   1155 	switch (nationality) {
   1156 	    case N_A: return "a";
   1157 	    case N_B: return "b";
   1158 	    case N_S: return "s";
   1159 	    case N_F: return "f";
   1160 	    case N_J: return "j";
   1161 	    case N_D: return "d";
   1162 	    case N_K: return "k";
   1163 	    case N_O: return "o";
   1164 	}
   1165 	return "?";
   1166 }
   1167 
   1168 static void
   1169 drawpicker(void)
   1170 {
   1171 	int y, sc, i;
   1172 	struct ship *ship;
   1173 
   1174 	erase();
   1175 
   1176 	mvaddstr(0, 0, "## SHIPS  TITLE");
   1177 	for (y=1; y<LINES-11; y++) {
   1178 		sc = (y-1) + pickerscroll;
   1179 		if (sc < NSCENE) {
   1180 			mvselprintw(y, 0, sc, pickerpos, 56,
   1181 				    "%-2d %-5d  %s",
   1182 				    sc, scene[sc].vessels, scene[sc].name);
   1183 		}
   1184 	}
   1185 
   1186 	mvprintw(2, 60 + 2, "%s wind",
   1187 		 absdirectionname(scene[pickerpos].winddir));
   1188 	mvprintw(3, 60 + 2, "(%s)",
   1189 		 windname(scene[pickerpos].windspeed));
   1190 
   1191 	for (i=0; i<scene[pickerpos].vessels; i++) {
   1192 		ship = &scene[pickerpos].ship[i];
   1193 		mvprintw(LINES-10 + i, 0,
   1194 			 "(%s) %-16s %3d gun %s (%s crew) (%d pts)",
   1195 			 nationalityname(ship->nationality),
   1196 			 ship->shipname,
   1197 			 ship->specs->guns,
   1198 			 shortclassname[ship->specs->class],
   1199 			 qualname[ship->specs->qual],
   1200 			 ship->specs->pts);
   1201 	}
   1202 
   1203 	move(1 + pickerpos - pickerscroll, 55);
   1204 	wrefresh(stdscr);
   1205 }
   1206 
   1207 static int
   1208 pickscenario(int initpos)
   1209 {
   1210 	int ch;
   1211 
   1212 	pickerpos = initpos;
   1213 	if (pickerpos < 0) {
   1214 		pickerpos = 0;
   1215 	}
   1216 
   1217 	while (1) {
   1218 		drawpicker();
   1219 		ch = getch();
   1220 		switch (ch) {
   1221 		    case 12 /*^L*/:
   1222 			clear();
   1223 			break;
   1224 		    case '\r':
   1225 		    case '\n':
   1226 			return pickerpos;
   1227 		    case 7 /*^G*/:
   1228 		    case 8 /*^H*/:
   1229 		    case 27 /*ESC*/:
   1230 		    case 127 /*^?*/:
   1231 			return initpos;
   1232 		    case 16 /*^P*/:
   1233 		    case KEY_UP:
   1234 			up(&pickerpos, &pickerscroll);
   1235 			break;
   1236 		    case 14 /*^N*/:
   1237 		    case KEY_DOWN:
   1238 			down(&pickerpos, &pickerscroll, NSCENE, LINES-12);
   1239 			break;
   1240 		    default:
   1241 			beep();
   1242 			break;
   1243 		}
   1244 	}
   1245 	return pickerpos;
   1246 }
   1247 
   1248 ////////////////////////////////////////////////////////////
   1249 // setup menus
   1250 
   1251 #define MAINITEMS_NUM 5
   1252 #define STARTITEMS_NUM 4
   1253 #define OPTIONSITEMS_NUM 5
   1254 
   1255 static int mainpos;
   1256 static bool connected;
   1257 
   1258 static bool joinactive;
   1259 static int joinpos;
   1260 static int joinscroll;
   1261 static int joinable[NSCENE];
   1262 static int numjoinable;
   1263 
   1264 static bool startactive;
   1265 static int startpos;
   1266 static int startscenario;
   1267 
   1268 static bool optionsactive;
   1269 static int optionspos;
   1270 static char o_myname[MAXNAMESIZE];
   1271 static bool o_randomize;
   1272 static bool o_longfmt;
   1273 static bool o_nobells;
   1274 
   1275 
   1276 /*
   1277  * this and sgetstr() should share code
   1278  */
   1279 static void
   1280 startup_getstr(int y, int x, char *buf, size_t max)
   1281 {
   1282 	size_t pos = 0;
   1283 	int ch;
   1284 
   1285 	for (;;) {
   1286 		buf[pos] = 0;
   1287 		move(y, x);
   1288 		addstr(buf);
   1289 		clrtoeol();
   1290 		wrefresh(stdscr);
   1291 		fflush(stdout);
   1292 
   1293 		ch = getch();
   1294 		switch (ch) {
   1295 		case '\n':
   1296 		case '\r':
   1297 			return;
   1298 		case '\b':
   1299 			if (pos > 0) {
   1300 				/*waddstr(scroll_w, "\b \b");*/
   1301 				pos--;
   1302 			}
   1303 			break;
   1304 		default:
   1305 			if (ch >= ' ' && ch < 0x7f && pos < max - 1) {
   1306 				buf[pos++] = ch;
   1307 			} else {
   1308 				beep();
   1309 			}
   1310 		}
   1311 	}
   1312 }
   1313 
   1314 static void
   1315 changename(void)
   1316 {
   1317 	mvaddstr(LINES-2, COLS/2, "Enter your name:");
   1318 	startup_getstr(LINES-1, COLS/2, o_myname, sizeof(o_myname));
   1319 }
   1320 
   1321 static void
   1322 checkforgames(void)
   1323 {
   1324 	int i;
   1325 	int prev;
   1326 
   1327 	if (numjoinable > 0) {
   1328 		prev = joinable[joinpos];
   1329 	} else {
   1330 		prev = 0;
   1331 	}
   1332 
   1333 	numjoinable = 0;
   1334 	for (i = 0; i < NSCENE; i++) {
   1335 		if (!sync_exists(i)) {
   1336 			continue;
   1337 		}
   1338 		if (i < prev) {
   1339 			joinpos = numjoinable;
   1340 		}
   1341 		joinable[numjoinable++] = i;
   1342 	}
   1343 	if (joinpos > numjoinable) {
   1344 		joinpos = (numjoinable > 0) ? numjoinable - 1 : 0;
   1345 	}
   1346 	if (joinscroll > joinpos) {
   1347 		joinscroll = (joinpos > 0) ? joinpos - 1 : 0;
   1348 	}
   1349 }
   1350 
   1351 static void
   1352 drawstartmenus(void)
   1353 {
   1354 	const int mainy0 = 8;
   1355 	const int mainx0 = 12;
   1356 
   1357 	erase();
   1358 
   1359 	mvaddstr(5, 10, "Wooden Ships & Iron Men");
   1360 
   1361 	mvaddselstr(mainy0+0, mainx0, 0, mainpos, 17, "Join a game");
   1362 	mvaddselstr(mainy0+1, mainx0, 1, mainpos, 17, "Start a game");
   1363 	mvaddselstr(mainy0+2, mainx0, 2, mainpos, 17, "Options");
   1364 	mvaddselstr(mainy0+3, mainx0, 3, mainpos, 17, "Show high scores");
   1365 	mvaddselstr(mainy0+4, mainx0, 4, mainpos, 17, "Quit");
   1366 
   1367 	mvprintw(15, 10, "Captain %s", myname);
   1368 	if (connected) {
   1369 		mvaddstr(16, 10, "Connected via scratch files.");
   1370 	} else {
   1371 		mvaddstr(16, 10, "Not connected.");
   1372 	}
   1373 
   1374 	if (joinactive) {
   1375 		int y0, leavey = 0, i, sc;
   1376 
   1377 		mvaddstr(0, COLS/2, "## SHIPS  TITLE");
   1378 		y0 = 1;
   1379 		for (i = 0; i < numjoinable; i++) {
   1380 			if (i >= joinscroll && i < joinscroll + LINES-1) {
   1381 				move(y0 + i - joinscroll, COLS/2);
   1382 				if (i == joinpos) {
   1383 					attron(A_REVERSE);
   1384 				}
   1385 				sc = joinable[i];
   1386 				printw("%-2d %-5d  %s",
   1387 				       sc, scene[sc].vessels, scene[sc].name);
   1388 				if (i == joinpos) {
   1389 					filltoeol();
   1390 					attroff(A_REVERSE);
   1391 					leavey = y0 + i - joinscroll;
   1392 				}
   1393 			}
   1394 		}
   1395 		mvaddstr(19, 10, "(Esc to abort)");
   1396 		if (numjoinable > 0) {
   1397 			mvaddstr(18, 10, "Choose a game to join.");
   1398 			move(leavey, COLS-1);
   1399 		} else {
   1400 			mvaddstr(2, COLS/2, "No games.");
   1401 			mvaddstr(18, 10, "Press return to refresh.");
   1402 		}
   1403 
   1404 	} else if (startactive) {
   1405 		const char *name;
   1406 
   1407 		mvaddstr(18, 10, "Start a new game");
   1408 		mvaddstr(19, 10, "(Esc to abort)");
   1409 		mvaddstr(2, COLS/2, "New game");
   1410 
   1411 		name = (startscenario < 0) ?
   1412 			"not selected" : scene[startscenario].name;
   1413 
   1414 		mvselprintw(4, COLS/2, 0, startpos, COLS/2 - 1,
   1415 			    "Scenario: %s", name);
   1416 		mvaddselstr(5, COLS/2, 1, startpos, COLS/2 - 1,
   1417 			    "Visibility: local");
   1418 		mvaddselstr(6, COLS/2, 2, startpos, COLS/2 - 1,
   1419 			    "Password: unset");
   1420 		mvaddselstr(7, COLS/2, 3, startpos, COLS/2 - 1,
   1421 			    "Start game");
   1422 		move(4+startpos, COLS - 2);
   1423 
   1424 	} else if (optionsactive) {
   1425 		mvaddstr(18, 10, "Adjust options");
   1426 		mvaddstr(19, 10, "(Esc to abort)");
   1427 		mvaddstr(2, COLS/2, "Adjust options");
   1428 
   1429 		mvselprintw(4, COLS/2, 0, optionspos, COLS/2-1,
   1430 			    "Your name: %s", o_myname);
   1431 		mvselprintw(5, COLS/2, 1, optionspos, COLS/2-1,
   1432 			    "Auto-pick ships: %s", o_randomize ? "ON" : "off");
   1433 		mvselprintw(6, COLS/2, 2, optionspos, COLS/2-1,
   1434 			    "Usernames in scores: %s",
   1435 			    o_longfmt ? "ON" : "off");
   1436 		mvselprintw(7, COLS/2, 3, optionspos, COLS/2-1,
   1437 			    "Beeping: %s", o_nobells ? "OFF" : "on");
   1438 		mvselprintw(8, COLS/2, 4, optionspos, COLS/2-1,
   1439 			    "Apply changes");
   1440 		move(4+optionspos, COLS - 2);
   1441 
   1442 	} else {
   1443 		move(mainy0 + mainpos, mainx0 + 16);
   1444 	}
   1445 
   1446 	wrefresh(stdscr);
   1447 	fflush(stdout);
   1448 }
   1449 
   1450 void
   1451 startup(void)
   1452 {
   1453 	int ch;
   1454 
   1455 	connected = false;
   1456 	mainpos = 0;
   1457 
   1458 	joinactive = false;
   1459 	joinpos = 0;
   1460 	joinscroll = 0;
   1461 	numjoinable = 0;
   1462 
   1463 	startactive = false;
   1464 	startpos = 0;
   1465 	startscenario = -1;
   1466 
   1467 	optionsactive = false;
   1468 	optionspos = 0;
   1469 
   1470 	while (1) {
   1471 		if (joinactive) {
   1472 			checkforgames();
   1473 		}
   1474 		drawstartmenus();
   1475 		ch = getch();
   1476 		switch (ch) {
   1477 		    case 12 /*^L*/:
   1478 			clear();
   1479 			break;
   1480 		    case '\r':
   1481 		    case '\n':
   1482 			if (joinactive && numjoinable > 0) {
   1483 				game = joinable[joinpos];
   1484 				startgame();
   1485 				joinactive = false;
   1486 			} else if (startactive) {
   1487 				switch (startpos) {
   1488 				    case 0:
   1489 					startscenario = pickscenario(startscenario);
   1490 					startpos = 3;
   1491 					break;
   1492 				    case 1:
   1493 				    case 2:
   1494 					oops(21, 10, "That doesn't work yet.");
   1495 					break;
   1496 				    case 3:
   1497 					if (startscenario >= 0) {
   1498 						game = startscenario;
   1499 						/* can't do this here yet */
   1500 						/*startdriver();*/
   1501 						startgame();
   1502 						startactive = false;
   1503 						startscenario = -1;
   1504 					} else {
   1505 						oops(21, 10,
   1506 						     "Pick a scenario.");
   1507 					}
   1508 					break;
   1509 				}
   1510 			} else if (optionsactive) {
   1511 				switch (optionspos) {
   1512 				    case 0: changename(); break;
   1513 				    case 1: o_randomize = !o_randomize; break;
   1514 				    case 2: o_longfmt = !o_longfmt; break;
   1515 				    case 3: o_nobells = !o_nobells; break;
   1516 				    case 4:
   1517 					strlcpy(myname, o_myname,
   1518 						sizeof(myname));
   1519 					randomize = o_randomize;
   1520 					longfmt = o_longfmt;
   1521 					nobells = o_nobells;
   1522 					optionsactive = false;
   1523 					break;
   1524 				}
   1525 			} else {
   1526 				switch (mainpos) {
   1527 				    case 0: joinactive = true; break;
   1528 				    case 1: startactive = true; break;
   1529 				    case 2:
   1530 					strlcpy(o_myname, myname,
   1531 						sizeof(o_myname));
   1532 					o_randomize = randomize;
   1533 					o_longfmt = longfmt;
   1534 					o_nobells = nobells;
   1535 					optionsactive = true;
   1536 					break;
   1537 				    case 3: lo_curses(); break;
   1538 				    case 4: return;
   1539 				}
   1540 			}
   1541 			break;
   1542 		    case 7 /*^G*/:
   1543 		    case 8 /*^H*/:
   1544 		    case 27 /*ESC*/:
   1545 		    case 127 /*^?*/:
   1546 			if (joinactive) {
   1547 				joinactive = false;
   1548 			} else if (startactive) {
   1549 				startactive = false;
   1550 			} else if (optionsactive) {
   1551 				optionsactive = false;
   1552 			} else {
   1553 				/* nothing */
   1554 			}
   1555 			break;
   1556 		    case 16 /*^P*/:
   1557 		    case KEY_UP:
   1558 			if (joinactive) {
   1559 				up(&joinpos, &joinscroll);
   1560 			} else if (startactive) {
   1561 				up(&startpos, NULL);
   1562 			} else if (optionsactive) {
   1563 				up(&optionspos, NULL);
   1564 			} else {
   1565 				up(&mainpos, NULL);
   1566 			}
   1567 			break;
   1568 		    case 14 /*^N*/:
   1569 		    case KEY_DOWN:
   1570 			if (joinactive) {
   1571 				down(&joinpos, &joinscroll,
   1572 				     numjoinable, LINES-1);
   1573 			} else if (startactive) {
   1574 				down(&startpos, NULL,
   1575 				     STARTITEMS_NUM, STARTITEMS_NUM);
   1576 			} else if (optionsactive) {
   1577 				down(&optionspos, NULL,
   1578 				     OPTIONSITEMS_NUM, OPTIONSITEMS_NUM);
   1579 			} else {
   1580 				down(&mainpos, NULL,
   1581 				     MAINITEMS_NUM, MAINITEMS_NUM);
   1582 			}
   1583 			break;
   1584 		    default:
   1585 			beep();
   1586 			break;
   1587 		}
   1588 	}
   1589 }
   1590