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