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
define_esc_key(int ch)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
initscreen(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
cleanupscreen(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
filltoeol(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
mvaddselstr(int y,int x0,int item,int curitem,size_t width,const char * str)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)
mvselprintw(int y,int x0,int item,int curitem,size_t width,const char * fmt,...)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
up(int * posp,int * scrollp)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
down(int * posp,int * scrollp,int max,int visible)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)
oops(int y,int x,const char * fmt,...)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
scrollarea_add(const char * text)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
draw_scroll(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
Signal(const char * fmt,struct ship * ship,...)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
Msg(const char * fmt,...)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
prompt(const char * p,struct ship * ship)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
endprompt(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
display_hide_prompt(void)462 display_hide_prompt(void)
463 {
464           sc_hideprompt = true;
465           draw_scroll();
466           wrefresh(scroll_w);
467 }
468 
469 void
display_reshow_prompt(void)470 display_reshow_prompt(void)
471 {
472           sc_hideprompt = false;
473           draw_scroll();
474           wrefresh(scroll_w);
475 }
476 
477 
478 int
sgetch(const char * p,struct ship * ship,int flag)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
sgetstr(const char * pr,char * buf,int n)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
display_force_full_redraw(void)549 display_force_full_redraw(void)
550 {
551           clear();
552 }
553 
554 void
display_redraw(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
draw_view(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
draw_turn(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
draw_stat(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
draw_slot(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
draw_board(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
display_set_obp(int which,bool show)767 display_set_obp(int which, bool show)
768 {
769           obp[which] = show;
770 }
771 
772 void
display_set_dbp(int which,bool show)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
display_scroll_pageup(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
display_scroll_pagedown(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
centerview(void)810 centerview(void)
811 {
812           viewrow = mf->row - VIEW_Y / 2;
813           viewcol = mf->col - VIEW_X / 2;
814 }
815 
816 void
upview(void)817 upview(void)
818 {
819           viewrow -= VIEW_Y / 3;
820 }
821 
822 void
downview(void)823 downview(void)
824 {
825           viewrow += VIEW_Y / 3;
826 }
827 
828 void
leftview(void)829 leftview(void)
830 {
831           viewcol -= VIEW_X / 5;
832 }
833 
834 void
rightview(void)835 rightview(void)
836 {
837           viewcol += VIEW_X / 5;
838 }
839 
840 /* Called from newturn()... rename? */
841 void
display_adjust_view(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
nextload(int load)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
loadbychar(int ch)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 *
loadstr(int load)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
displayshiplist(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
pickship(void)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
pickload(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
startgame(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 *
absdirectionname(int dir)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 *
windname(int wind)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 *
nationalityname(int nationality)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
drawpicker(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
pickscenario(int initpos)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
startup_getstr(int y,int x,char * buf,size_t max)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
changename(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
checkforgames(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
drawstartmenus(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
startup(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