Initial revision
[tetrinet.git] / tty.c
blobb5561418b23818740330301a7c63c687c9d7c8bf
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Text terminal I/O routines.
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <curses.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <sys/time.h>
16 #include "tetrinet.h"
17 #include "tetris.h"
18 #include "io.h"
20 /*************************************************************************/
22 #define MY_HLINE (fancy ? ACS_HLINE : '-')
23 #define MY_VLINE (fancy ? ACS_VLINE : '|')
24 #define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
25 #define MY_URCORNER (fancy ? ACS_URCORNER : '+')
26 #define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
27 #define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
29 #define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
30 #define MY_BOLD (fancy ? A_BOLD : 0)
32 /*************************************************************************/
33 /******************************* Input stuff *****************************/
34 /*************************************************************************/
36 /* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
37 * waiting. Return -2 if we run out of time with no input.
40 static int wait_for_input(int msec)
42 fd_set fds;
43 struct timeval tv;
44 int c;
45 static int escape = 0;
47 FD_ZERO(&fds);
48 FD_SET(0, &fds);
49 FD_SET(server_sock, &fds);
50 tv.tv_sec = msec/1000;
51 tv.tv_usec = (msec*1000) % 1000000;
52 while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
53 if (errno != EINTR)
54 perror("Warning: select() failed");
56 if (FD_ISSET(0, &fds)) {
57 c = getch();
58 if (!escape && c == 27) { /* Escape */
59 escape = 1;
60 c = wait_for_input(1000);
61 escape = 0;
62 if (c < 0)
63 return 27;
64 else
65 return c;
67 if (c == KEY_UP)
68 return K_UP;
69 else if (c == KEY_DOWN)
70 return K_DOWN;
71 else if (c == KEY_LEFT)
72 return K_LEFT;
73 else if (c == KEY_RIGHT)
74 return K_RIGHT;
75 else if (c == KEY_F(1) || c == ('1'|0x80) || (escape && c == '1'))
76 return K_F1;
77 else if (c == KEY_F(2) || c == ('2'|0x80) || (escape && c == '2'))
78 return K_F2;
79 else if (c == KEY_F(3) || c == ('3'|0x80) || (escape && c == '3'))
80 return K_F3;
81 else if (c == KEY_F(4) || c == ('4'|0x80) || (escape && c == '4'))
82 return K_F4;
83 else if (c == KEY_F(5) || c == ('5'|0x80) || (escape && c == '5'))
84 return K_F5;
85 else if (c == KEY_F(6) || c == ('6'|0x80) || (escape && c == '6'))
86 return K_F6;
87 else if (c == KEY_F(7) || c == ('7'|0x80) || (escape && c == '7'))
88 return K_F7;
89 else if (c == KEY_F(8) || c == ('8'|0x80) || (escape && c == '8'))
90 return K_F8;
91 else if (c == KEY_F(9) || c == ('9'|0x80) || (escape && c == '9'))
92 return K_F9;
93 else if (c == KEY_F(10) || c == ('0'|0x80) || (escape && c == '0'))
94 return K_F10;
95 else if (c == KEY_F(11))
96 return K_F11;
97 else if (c == KEY_F(12))
98 return K_F12;
99 else if (c == KEY_BACKSPACE)
100 return 8;
101 else if (c >= 0x0100)
102 return K_INVALID;
103 else if (c == 7) /* ^G */
104 return 27; /* Escape */
105 else
106 return c;
107 } /* if (FD_ISSET(0, &fds)) */
108 else if (FD_ISSET(server_sock, &fds))
109 return -1;
110 else
111 return -2; /* out of time */
114 /*************************************************************************/
115 /****************************** Output stuff *****************************/
116 /*************************************************************************/
118 /* Size of the screen */
119 static int scrwidth, scrheight;
121 /* Is color available? */
122 static int has_color;
124 /*************************************************************************/
126 /* Text buffers: */
128 typedef struct {
129 int x, y, width, height;
130 int line;
131 WINDOW *win; /* NULL if not currently displayed */
132 char **text;
133 } TextBuffer;
135 static TextBuffer plinebuf, gmsgbuf, attdefbuf;
137 /*************************************************************************/
139 /* Window for typing in-game text, and its coordinates: */
141 static WINDOW *gmsg_inputwin;
142 static int gmsg_inputpos, gmsg_inputheight;
144 /*************************************************************************/
145 /*************************************************************************/
147 /* Clean up the screen on exit. */
149 static void screen_cleanup()
151 wmove(stdscr, scrheight-1, 0);
152 wrefresh(stdscr);
153 endwin();
154 printf("\n");
157 /*************************************************************************/
159 /* Little signal handler that just does an exit(1) (thereby getting our
160 * cleanup routine called), except for TSTP, which does a clean suspend.
163 static void (*old_tstp)(int sig);
165 static void sighandler(int sig)
167 if (sig != SIGTSTP) {
168 endwin();
169 if (sig != SIGINT)
170 fprintf(stderr, "%s\n", strsignal(sig));
171 exit(1);
173 endwin();
174 signal(SIGTSTP, old_tstp);
175 raise(SIGTSTP);
176 doupdate();
177 signal(SIGTSTP, sighandler);
180 /*************************************************************************/
181 /*************************************************************************/
183 #define MAXCOLORS 256
185 static int colors[MAXCOLORS][2] = { {-1,-1} };
187 /* Return a color attribute value. */
189 static long getcolor(int fg, int bg)
191 int i;
193 if (colors[0][0] < 0) {
194 start_color();
195 memset(colors, -1, sizeof(colors));
196 colors[0][0] = COLOR_WHITE;
197 colors[0][1] = COLOR_BLACK;
199 if (fg == COLOR_WHITE && bg == COLOR_BLACK)
200 return COLOR_PAIR(0);
201 for (i = 1; i < MAXCOLORS; i++) {
202 if (colors[i][0] == fg && colors[i][1] == bg)
203 return COLOR_PAIR(i);
205 for (i = 1; i < MAXCOLORS; i++) {
206 if (colors[i][0] < 0) {
207 if (init_pair(i, fg, bg) == ERR)
208 continue;
209 colors[i][0] = fg;
210 colors[i][1] = bg;
211 return COLOR_PAIR(i);
214 return -1;
217 /*************************************************************************/
218 /*************************************************************************/
220 /* Set up the screen stuff. */
222 static void screen_setup(void)
224 /* Avoid messy keyfield signals while we're setting up */
225 signal(SIGINT, SIG_IGN);
226 signal(SIGQUIT, SIG_IGN);
227 signal(SIGTSTP, SIG_IGN);
229 initscr();
230 cbreak();
231 noecho();
232 nodelay(stdscr, TRUE);
233 keypad(stdscr, TRUE);
234 leaveok(stdscr, TRUE);
235 if ((has_color = has_colors()))
236 start_color();
237 getmaxyx(stdscr, scrheight, scrwidth);
238 scrwidth--; /* Don't draw in last column--this can cause scroll */
240 /* Cancel all this when we exit. */
241 atexit(screen_cleanup);
243 /* Catch signals so we can exit cleanly. */
244 signal(SIGINT, sighandler);
245 signal(SIGQUIT, sighandler);
246 signal(SIGTERM, sighandler);
247 signal(SIGHUP, sighandler);
248 signal(SIGSEGV, sighandler);
249 signal(SIGABRT, sighandler);
250 signal(SIGIOT, sighandler);
251 signal(SIGTRAP, sighandler);
252 signal(SIGBUS, sighandler);
253 signal(SIGFPE, sighandler);
254 signal(SIGUSR1, sighandler);
255 signal(SIGUSR2, sighandler);
256 signal(SIGALRM, sighandler);
257 #ifdef SIGSTKFLT
258 signal(SIGSTKFLT, sighandler);
259 #endif
260 signal(SIGTSTP, sighandler);
261 signal(SIGXCPU, sighandler);
262 signal(SIGXFSZ, sighandler);
263 signal(SIGVTALRM, sighandler);
265 /* Broken pipes don't want to bother us at all. */
266 signal(SIGPIPE, SIG_IGN);
269 /*************************************************************************/
271 /* Redraw everything on the screen. */
273 static void screen_refresh(void)
275 if (gmsg_inputwin)
276 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
277 if (plinebuf.win)
278 touchline(stdscr, plinebuf.y, plinebuf.height);
279 if (gmsgbuf.win)
280 touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
281 if (attdefbuf.win)
282 touchline(stdscr, attdefbuf.y, attdefbuf.height);
283 wnoutrefresh(stdscr);
284 doupdate();
287 /*************************************************************************/
289 /* Like screen_refresh(), but clear the screen first. */
291 static void screen_redraw(void)
293 clearok(stdscr, TRUE);
294 screen_refresh();
297 /*************************************************************************/
298 /************************* Text buffer routines **************************/
299 /*************************************************************************/
301 /* Put a line of text in a text buffer. */
303 static void outline(TextBuffer *buf, const char *s)
305 if (buf->line == buf->height) {
306 if (buf->win)
307 scroll(buf->win);
308 memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
309 buf->line--;
311 if (buf->win)
312 mvwaddstr(buf->win, buf->line, 0, s);
313 if (s != buf->text[buf->line]) /* check for restoring display */
314 buf->text[buf->line] = strdup(s);
315 buf->line++;
318 static void draw_text(int bufnum, const char *s)
320 char str[1024]; /* hopefully scrwidth < 1024 */
321 const char *t;
322 int indent = 0;
323 int x, y;
324 TextBuffer *buf;
326 switch (bufnum) {
327 case BUFFER_PLINE: buf = &plinebuf; break;
328 case BUFFER_GMSG: buf = &gmsgbuf; break;
329 case BUFFER_ATTDEF: buf = &attdefbuf; break;
330 default: return;
332 if (!buf->text)
333 return;
334 if (buf->win) {
335 getyx(stdscr, y, x);
336 attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
338 while (*s && isspace(*s))
339 s++;
340 while (strlen(s) > buf->width - indent) {
341 t = s + buf->width - indent;
342 while (t >= s && !isspace(*t))
343 t--;
344 while (t >= s && isspace(*t))
345 t--;
346 t++;
347 if (t < s)
348 t = s + buf->width - indent;
349 if (indent > 0)
350 sprintf(str, "%*s", indent, "");
351 strncpy(str+indent, s, t-s);
352 str[t-s+indent] = 0;
353 outline(buf, str);
354 indent = 2;
355 while (isspace(*t))
356 t++;
357 s = t;
359 if (indent > 0)
360 sprintf(str, "%*s", indent, "");
361 strcpy(str+indent, s);
362 outline(buf, str);
363 if (buf->win) {
364 move(y, x);
365 screen_refresh();
369 /*************************************************************************/
371 /* Clear the contents of a text buffer. */
373 static void clear_text(int bufnum)
375 TextBuffer *buf;
376 int i;
378 switch (bufnum) {
379 case BUFFER_PLINE: buf = &plinebuf; break;
380 case BUFFER_GMSG: buf = &gmsgbuf; break;
381 case BUFFER_ATTDEF: buf = &attdefbuf; break;
382 default: return;
384 if (buf->text) {
385 for (i = 0; i < buf->height; i++) {
386 if (buf->text[i]) {
387 free(buf->text[i]);
388 buf->text[i] = NULL;
391 buf->line = 0;
393 if (buf->win) {
394 werase(buf->win);
395 screen_refresh();
399 /*************************************************************************/
401 /* Restore the contents of the given text buffer. */
403 static void restore_text(TextBuffer *buf)
405 buf->line = 0;
406 while (buf->line < buf->height && buf->text[buf->line])
407 outline(buf, buf->text[buf->line]);
410 /*************************************************************************/
412 /* Open a window for the given text buffer. */
414 static void open_textwin(TextBuffer *buf)
416 if (buf->height <= 0 || buf->width <= 0) {
417 char str[256];
418 move(scrheight-1, 0);
419 snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
420 buf->width, buf->height);
421 addstr(str);
422 exit(1);
424 if (!buf->win) {
425 buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
426 scrollok(buf->win, TRUE);
428 if (!buf->text)
429 buf->text = calloc(buf->height, sizeof(char *));
430 else
431 restore_text(buf);
434 /*************************************************************************/
436 /* Close the window for the given text buffer, if it's open. */
438 static void close_textwin(TextBuffer *buf)
440 if (buf->win) {
441 delwin(buf->win);
442 buf->win = NULL;
446 /*************************************************************************/
447 /************************ Field drawing routines *************************/
448 /*************************************************************************/
450 /* Are we on a wide screen (>=92 columns)? */
451 static int wide_screen = 0;
453 /* Field display X/Y coordinates. */
454 static const int own_coord[2] = {0,0};
455 static int other_coord[5][2] = /* Recomputed based on screen width */
456 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
458 /* Position of the status window. */
459 static const int status_coord[2] = {28,25};
460 static const int next_coord[2] = {40,24};
461 static const int alt_status_coord[2] = {28,2};
462 static const int alt_next_coord[2] = {29,8};
464 /* Position of the attacks/defenses window. */
465 static const int attdef_coord[2] = {28,28};
466 static const int alt_attdef_coord[2] = {28,24};
468 /* Position of the text window. X coordinate is ignored. */
469 static const int field_text_coord[2] = {0,47};
471 /* Information for drawing blocks. Color attributes are added to blocks in
472 * the setup_fields() routine. */
473 static int tile_chars[15] =
474 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
476 static int attdef_size;
477 static int attdef_line;
478 static char **attdef_text;
480 /* Are we redrawing the entire display? */
481 static int field_redraw = 0;
483 /*************************************************************************/
484 /*************************************************************************/
486 /* Set up the field display. */
488 static void draw_own_field(void);
489 static void draw_other_field(int player);
490 static void draw_status(void);
491 static void draw_specials(void);
492 static void draw_gmsg_input(const char *s, int pos);
494 static void setup_fields(void)
496 int i, j, x, y, base, delta, attdefbot;
497 char buf[32];
499 if (!(tile_chars[0] & A_ATTRIBUTES)) {
500 for (i = 1; i < 15; i++)
501 tile_chars[i] |= A_BOLD;
502 tile_chars[1] |= getcolor(COLOR_BLUE, COLOR_BLACK);
503 tile_chars[2] |= getcolor(COLOR_YELLOW, COLOR_BLACK);
504 tile_chars[3] |= getcolor(COLOR_GREEN, COLOR_BLACK);
505 tile_chars[4] |= getcolor(COLOR_MAGENTA, COLOR_BLACK);
506 tile_chars[5] |= getcolor(COLOR_RED, COLOR_BLACK);
509 field_redraw = 1;
510 leaveok(stdscr, TRUE);
511 close_textwin(&plinebuf);
512 clear();
513 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
515 if (scrwidth >= 92) {
516 wide_screen = 1;
517 base = 41;
518 } else {
519 base = 28;
521 delta = (scrwidth - base) / 3;
522 base += 2 + (delta - (FIELD_WIDTH+5)) / 2;
523 other_coord[0][0] = base;
524 other_coord[1][0] = base + delta;
525 other_coord[2][0] = base + delta*2;
526 other_coord[3][0] = base + delta;
527 other_coord[4][0] = base + delta*2;
529 attdefbot = field_text_coord[1] - 1;
530 if (scrheight - field_text_coord[1] > 3) {
531 move(field_text_coord[1], 0);
532 hline(MY_HLINE2, scrwidth);
533 attdefbot--;
534 if (scrheight - field_text_coord[1] > 5) {
535 move(scrheight-2, 0);
536 hline(MY_HLINE2, scrwidth);
537 attrset(MY_BOLD);
538 move(scrheight-1, 0);
539 addstr("F1=Show Fields F2=Partyline F3=Winlist");
540 move(scrheight-1, scrwidth-8);
541 addstr("F10=Quit");
542 attrset(A_NORMAL);
543 gmsgbuf.y = field_text_coord[1]+1;
544 gmsgbuf.height = scrheight - field_text_coord[1] - 3;
545 } else {
546 gmsgbuf.y = field_text_coord[1]+1;
547 gmsgbuf.height = scrheight - field_text_coord[1] - 1;
549 } else {
550 gmsgbuf.y = field_text_coord[1];
551 gmsgbuf.height = scrheight - field_text_coord[1];
553 gmsgbuf.x = field_text_coord[0];
554 gmsgbuf.width = scrwidth;
555 open_textwin(&gmsgbuf);
557 x = own_coord[0];
558 y = own_coord[1];
559 sprintf(buf, "%d", my_playernum);
560 mvaddstr(y, x+FIELD_WIDTH*2+2, buf);
561 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
562 mvaddch(y+i, x+FIELD_WIDTH*2+2, players[my_playernum-1][i-2]);
563 move(y, x);
564 vline(MY_VLINE, FIELD_HEIGHT*2);
565 move(y, x+FIELD_WIDTH*2+1);
566 vline(MY_VLINE, FIELD_HEIGHT*2);
567 move(y+FIELD_HEIGHT*2, x);
568 addch(MY_LLCORNER);
569 hline(MY_HLINE, FIELD_WIDTH*2);
570 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
571 addch(MY_LRCORNER);
572 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
573 draw_own_field();
574 draw_specials();
576 for (j = 0; j < 5; j++) {
577 x = other_coord[j][0];
578 y = other_coord[j][1];
579 move(y, x);
580 vline(MY_VLINE, FIELD_HEIGHT);
581 move(y, x+FIELD_WIDTH+1);
582 vline(MY_VLINE, FIELD_HEIGHT);
583 move(y+FIELD_HEIGHT, x);
584 addch(MY_LLCORNER);
585 hline(MY_HLINE, FIELD_WIDTH);
586 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
587 addch(MY_LRCORNER);
588 if (j+1 >= my_playernum) {
589 sprintf(buf, "%d", j+2);
590 mvaddstr(y, x+FIELD_WIDTH+2, buf);
591 if (players[j+1]) {
592 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
593 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j+1][i]);
595 draw_other_field(j+2);
596 } else {
597 sprintf(buf, "%d", j+1);
598 mvaddstr(y, x+FIELD_WIDTH+2, buf);
599 if (players[j]) {
600 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
601 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j][i]);
603 draw_other_field(j+1);
607 if (wide_screen) {
608 x = alt_status_coord[0];
609 y = alt_status_coord[1];
610 mvaddstr(y, x, "Lines:");
611 mvaddstr(y+1, x, "Level:");
612 x = alt_next_coord[0];
613 y = alt_next_coord[1];
614 mvaddstr(y-2, x-1, "Next piece:");
615 move(y-1, x-1);
616 addch(MY_ULCORNER);
617 hline(MY_HLINE, 8);
618 mvaddch(y-1, x+8, MY_URCORNER);
619 move(y, x-1);
620 vline(MY_VLINE, 8);
621 move(y, x+8);
622 vline(MY_VLINE, 8);
623 move(y+8, x-1);
624 addch(MY_LLCORNER);
625 hline(MY_HLINE, 8);
626 mvaddch(y+8, x+8, MY_LRCORNER);
627 } else {
628 x = status_coord[0];
629 y = status_coord[1];
630 mvaddstr(y-1, x, "Next piece:");
631 mvaddstr(y, x, "Lines:");
632 mvaddstr(y+1, x, "Level:");
634 if (playing_game)
635 draw_status();
637 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
638 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
639 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
640 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
641 open_textwin(&attdefbuf);
643 if (gmsg_inputwin) {
644 delwin(gmsg_inputwin);
645 gmsg_inputwin = NULL;
646 draw_gmsg_input(NULL, -1);
649 screen_refresh();
650 field_redraw = 0;
653 /*************************************************************************/
655 /* Display the player's own field. */
657 static void draw_own_field(void)
659 int x, y, x0, y0;
660 Field *f = &fields[my_playernum-1];
662 if (dispmode != MODE_FIELDS)
663 return;
664 x0 = own_coord[0]+1;
665 y0 = own_coord[1];
666 for (y = 0; y < 22; y++) {
667 for (x = 0; x < 12; x++) {
668 int c = tile_chars[(*f)[y][x]];
669 mvaddch(y0+y*2, x0+x*2, c);
670 addch(c);
671 mvaddch(y0+y*2+1, x0+x*2, c);
672 addch(c);
675 if (gmsg_inputwin) {
676 delwin(gmsg_inputwin);
677 gmsg_inputwin = NULL;
678 draw_gmsg_input(NULL, -1);
680 if (!field_redraw)
681 screen_refresh();
684 /*************************************************************************/
686 /* Display another player's field. */
688 static void draw_other_field(int player)
690 int x, y, x0, y0;
691 Field *f;
693 if (dispmode != MODE_FIELDS)
694 return;
695 f = &fields[player-1];
696 if (player > my_playernum)
697 player--;
698 player--;
699 x0 = other_coord[player][0]+1;
700 y0 = other_coord[player][1];
701 for (y = 0; y < 22; y++) {
702 move(y0+y, x0);
703 for (x = 0; x < 12; x++)
704 addch(tile_chars[(*f)[y][x]]);
706 if (gmsg_inputwin) {
707 delwin(gmsg_inputwin);
708 gmsg_inputwin = NULL;
709 draw_gmsg_input(NULL, -1);
711 if (!field_redraw)
712 screen_refresh();
715 /*************************************************************************/
717 /* Display the current game status (level, lines, next piece). */
719 static void draw_status(void)
721 int x, y, i, j;
722 char buf[32], shape[4][4];
724 x = wide_screen ? alt_status_coord[0] : status_coord[0];
725 y = wide_screen ? alt_status_coord[1] : status_coord[1];
726 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
727 mvaddstr(y, x+7, buf);
728 sprintf(buf, "%d", levels[my_playernum]);
729 mvaddstr(y+1, x+7, buf);
730 x = wide_screen ? alt_next_coord[0] : next_coord[0];
731 y = wide_screen ? alt_next_coord[1] : next_coord[1];
732 if (get_shape(next_piece, 0, shape) == 0) {
733 for (j = 0; j < 4; j++) {
734 if (!wide_screen)
735 move(y+j, x);
736 for (i = 0; i < 4; i++) {
737 if (wide_screen) {
738 move(y+j*2, x+i*2);
739 addch(tile_chars[shape[j][i]]);
740 addch(tile_chars[shape[j][i]]);
741 move(y+j*2+1, x+i*2);
742 addch(tile_chars[shape[j][i]]);
743 addch(tile_chars[shape[j][i]]);
744 } else
745 addch(tile_chars[shape[j][i]]);
751 /*************************************************************************/
753 /* Display the special inventory and description of the current special. */
755 static const char *descs[] = {
756 " ",
757 "Add Line ",
758 "Clear Line ",
759 "Nuke Field ",
760 "Clear Random Blocks ",
761 "Switch Fields ",
762 "Clear Special Blocks",
763 "Block Gravity ",
764 "Blockquake ",
765 "Block Bomb "
768 static void draw_specials(void)
770 int x, y, i;
772 if (dispmode != MODE_FIELDS)
773 return;
774 x = own_coord[0];
775 y = own_coord[1]+45;
776 mvaddstr(y, x, descs[specials[0]+1]);
777 move(y+1, x+10);
778 i = 0;
779 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
780 addch(tile_chars[specials[i]+6]);
781 i++;
782 x++;
784 while (x < attdef_coord[0]-1) {
785 addch(tile_chars[0]);
786 x++;
788 if (!field_redraw)
789 screen_refresh();
792 /*************************************************************************/
794 /* Display an attack/defense message. */
796 static const char *msgs[][2] = {
797 { "cs1", "1 Line Added to All" },
798 { "cs2", "2 Lines Added to All" },
799 { "cs4", "4 Lines Added to All" },
800 { "a", "Add Line" },
801 { "c", "Clear Line" },
802 { "n", "Nuke Field" },
803 { "r", "Clear Random Blocks" },
804 { "s", "Switch Fields" },
805 { "b", "Clear Special Blocks" },
806 { "g", "Block Gravity" },
807 { "q", "Blockquake" },
808 { "o", "Block Bomb" },
809 { NULL }
812 static void draw_attdef(const char *type, int from, int to)
814 int i, width;
815 char *s, buf[512];
817 width = other_coord[4][0] - attdef_coord[0] - 1;
818 for (i = 0; msgs[i][0]; i++) {
819 if (strcmp(type, msgs[i][0]) == 0)
820 break;
822 if (!msgs[i][0])
823 return;
824 strcpy(buf, msgs[i][1]);
825 if (to != 0)
826 sprintf(buf+strlen(buf), " on %s", players[to-1]);
827 if (from == 0)
828 sprintf(buf+strlen(buf), " by Server");
829 else
830 sprintf(buf+strlen(buf), " by %s", players[from-1]);
831 draw_text(BUFFER_ATTDEF, buf);
834 /*************************************************************************/
836 /* Display the in-game text window. */
838 static void draw_gmsg_input(const char *s, int pos)
840 static int start = 0; /* Start of displayed part of input line */
841 static const char *last_s;
842 static int last_pos;
844 if (s)
845 last_s = s;
846 else
847 s = last_s;
848 if (pos >= 0)
849 last_pos = pos;
850 else
851 pos = last_pos;
853 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
855 if (!gmsg_inputwin) {
856 gmsg_inputpos = scrheight/2 - 1;
857 gmsg_inputheight = 3;
858 gmsg_inputwin =
859 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
860 werase(gmsg_inputwin);
861 leaveok(gmsg_inputwin, FALSE);
862 leaveok(stdscr, FALSE);
863 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
866 if (strlen(s) < scrwidth-7) {
867 start = 0;
868 mvwaddstr(gmsg_inputwin, 1, 6, s);
869 wmove(gmsg_inputwin, 1, 6+strlen(s));
870 move(gmsg_inputpos+1, 6+strlen(s));
871 wclrtoeol(gmsg_inputwin);
872 wmove(gmsg_inputwin, 1, 6+pos);
873 move(gmsg_inputpos+1, 6+pos);
874 } else {
875 if (pos < start+8) {
876 start = pos-8;
877 if (start < 0)
878 start = 0;
879 } else if (pos > start + scrwidth-15) {
880 start = pos - (scrwidth-15);
881 if (start > strlen(s) - (scrwidth-7))
882 start = strlen(s) - (scrwidth-7);
884 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
885 wmove(gmsg_inputwin, 1, 6 + (pos-start));
886 move(gmsg_inputpos+1, 6 + (pos-start));
888 screen_refresh();
891 /*************************************************************************/
893 /* Clear the in-game text window. */
895 static void clear_gmsg_input(void)
897 if (gmsg_inputwin) {
898 delwin(gmsg_inputwin);
899 gmsg_inputwin = NULL;
900 leaveok(stdscr, TRUE);
901 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
902 setup_fields();
903 screen_refresh();
907 /*************************************************************************/
908 /*************************** Partyline display ***************************/
909 /*************************************************************************/
911 static void setup_partyline(void)
913 int i;
915 close_textwin(&gmsgbuf);
916 close_textwin(&attdefbuf);
917 clear();
919 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
921 plinebuf.x = plinebuf.y = 0;
922 plinebuf.width = scrwidth;
923 plinebuf.height = scrheight-4;
924 open_textwin(&plinebuf);
926 move(scrheight-4, 0);
927 hline(MY_HLINE, scrwidth);
928 move(scrheight-3, 0);
929 addstr("> ");
931 move(scrheight-2, 0);
932 hline(MY_HLINE2, scrwidth);
933 attrset(MY_BOLD);
934 move(scrheight-1, 0);
935 addstr("F1=Show Fields F2=Partyline F3=Winlist");
936 move(scrheight-1, scrwidth-8);
937 addstr("F10=Quit");
938 attrset(A_NORMAL);
940 move(scrheight-3, 2);
941 leaveok(stdscr, FALSE);
942 screen_refresh();
945 /*************************************************************************/
947 static void draw_partyline_input(const char *s, int pos)
949 static int start = 0; /* Start of displayed part of input line */
951 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
952 if (strlen(s) < scrwidth-3) {
953 start = 0;
954 mvaddstr(scrheight-3, 2, s);
955 move(scrheight-3, 2+strlen(s));
956 clrtoeol();
957 move(scrheight-3, 2+pos);
958 } else {
959 if (pos < start+8) {
960 start = pos-8;
961 if (start < 0)
962 start = 0;
963 } else if (pos > start + scrwidth-11) {
964 start = pos - (scrwidth-11);
965 if (start > strlen(s) - (scrwidth-3))
966 start = strlen(s) - (scrwidth-3);
968 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
969 move(scrheight-3, 2 + (pos-start));
971 screen_refresh();
974 /*************************************************************************/
975 /**************************** Winlist display ****************************/
976 /*************************************************************************/
978 static void setup_winlist(void)
980 int i, x;
981 char buf[32];
983 leaveok(stdscr, TRUE);
984 close_textwin(&plinebuf);
985 clear();
986 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
988 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
989 x = scrwidth/2 - strlen(winlist[i].name);
990 if (x < 0)
991 x = 0;
992 if (winlist[i].team) {
993 if (x < 4)
994 x = 4;
995 mvaddstr(i*2, x-4, "<T>");
997 mvaddstr(i*2, x, winlist[i].name);
998 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
999 if (winlist[i].games) {
1000 int avg100 = winlist[i].points*100 / winlist[i].games;
1001 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1002 " %d.%02d",avg100/100, avg100%100);
1004 x += strlen(winlist[i].name) + 2;
1005 if (x > scrwidth - strlen(buf))
1006 x = scrwidth - strlen(buf);
1007 mvaddstr(i*2, x, buf);
1010 move(scrheight-2, 0);
1011 hline(MY_HLINE2, scrwidth);
1012 attrset(MY_BOLD);
1013 move(scrheight-1, 0);
1014 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1015 move(scrheight-1, scrwidth-8);
1016 addstr("F10=Quit");
1017 attrset(A_NORMAL);
1019 screen_refresh();
1022 /*************************************************************************/
1023 /************************** Interface declaration ************************/
1024 /*************************************************************************/
1026 Interface tty_interface = {
1028 wait_for_input,
1030 screen_setup,
1031 screen_refresh,
1032 screen_redraw,
1034 draw_text,
1035 clear_text,
1037 setup_fields,
1038 draw_own_field,
1039 draw_other_field,
1040 draw_status,
1041 draw_specials,
1042 draw_attdef,
1043 draw_gmsg_input,
1044 clear_gmsg_input,
1046 setup_partyline,
1047 draw_partyline_input,
1049 setup_winlist
1052 /*************************************************************************/