ChangeLog update
[tetrinet.git] / tty.c
blobf4f635d666f4ca379c5a88386cc0de1f4f8be57f
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Text terminal I/O routines.
5 */
7 #define _GNU_SOURCE /* strsignal() - FIXME!!! --pasky */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <ctype.h>
14 #include <curses.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <sys/time.h>
18 #include "tetrinet.h"
19 #include "tetris.h"
20 #include "io.h"
22 /*************************************************************************/
24 #define MY_HLINE (fancy ? ACS_HLINE : '-')
25 #define MY_VLINE (fancy ? ACS_VLINE : '|')
26 #define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
27 #define MY_URCORNER (fancy ? ACS_URCORNER : '+')
28 #define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
29 #define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
31 #define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
32 #define MY_BOLD (fancy ? A_BOLD : 0)
34 /*************************************************************************/
35 /******************************* Input stuff *****************************/
36 /*************************************************************************/
38 /* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
39 * waiting. Return -2 if we run out of time with no input.
42 static int wait_for_input(int msec)
44 fd_set fds;
45 struct timeval tv;
46 int c;
47 static int escape = 0;
49 FD_ZERO(&fds);
50 FD_SET(0, &fds);
51 FD_SET(server_sock, &fds);
52 tv.tv_sec = msec/1000;
53 tv.tv_usec = (msec*1000) % 1000000;
54 while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
55 if (errno != EINTR)
56 perror("Warning: select() failed");
58 if (FD_ISSET(0, &fds)) {
59 c = getch();
60 if (!escape && c == 27) { /* Escape */
61 escape = 1;
62 c = wait_for_input(1000);
63 escape = 0;
64 if (c < 0)
65 return 27;
66 else
67 return c;
69 if (c == KEY_UP)
70 return K_UP;
71 else if (c == KEY_DOWN)
72 return K_DOWN;
73 else if (c == KEY_LEFT)
74 return K_LEFT;
75 else if (c == KEY_RIGHT)
76 return K_RIGHT;
77 else if (c == KEY_F(1) || c == ('1'|0x80) || (escape && c == '1'))
78 return K_F1;
79 else if (c == KEY_F(2) || c == ('2'|0x80) || (escape && c == '2'))
80 return K_F2;
81 else if (c == KEY_F(3) || c == ('3'|0x80) || (escape && c == '3'))
82 return K_F3;
83 else if (c == KEY_F(4) || c == ('4'|0x80) || (escape && c == '4'))
84 return K_F4;
85 else if (c == KEY_F(5) || c == ('5'|0x80) || (escape && c == '5'))
86 return K_F5;
87 else if (c == KEY_F(6) || c == ('6'|0x80) || (escape && c == '6'))
88 return K_F6;
89 else if (c == KEY_F(7) || c == ('7'|0x80) || (escape && c == '7'))
90 return K_F7;
91 else if (c == KEY_F(8) || c == ('8'|0x80) || (escape && c == '8'))
92 return K_F8;
93 else if (c == KEY_F(9) || c == ('9'|0x80) || (escape && c == '9'))
94 return K_F9;
95 else if (c == KEY_F(10) || c == ('0'|0x80) || (escape && c == '0'))
96 return K_F10;
97 else if (c == KEY_F(11))
98 return K_F11;
99 else if (c == KEY_F(12))
100 return K_F12;
101 else if (c == KEY_BACKSPACE)
102 return 8;
103 else if (c >= 0x0100)
104 return K_INVALID;
105 else if (c == 7) /* ^G */
106 return 27; /* Escape */
107 else
108 return c;
109 } /* if (FD_ISSET(0, &fds)) */
110 else if (FD_ISSET(server_sock, &fds))
111 return -1;
112 else
113 return -2; /* out of time */
116 /*************************************************************************/
117 /****************************** Output stuff *****************************/
118 /*************************************************************************/
120 /* Size of the screen */
121 static int scrwidth, scrheight;
123 /* Is color available? */
124 static int has_color;
126 /*************************************************************************/
128 /* Text buffers: */
130 typedef struct {
131 int x, y, width, height;
132 int line;
133 WINDOW *win; /* NULL if not currently displayed */
134 char **text;
135 } TextBuffer;
137 static TextBuffer plinebuf, gmsgbuf, attdefbuf;
139 /*************************************************************************/
141 /* Window for typing in-game text, and its coordinates: */
143 static WINDOW *gmsg_inputwin;
144 static int gmsg_inputpos, gmsg_inputheight;
146 /*************************************************************************/
147 /*************************************************************************/
149 /* Clean up the screen on exit. */
151 static void screen_cleanup()
153 wmove(stdscr, scrheight-1, 0);
154 wrefresh(stdscr);
155 endwin();
156 printf("\n");
159 /*************************************************************************/
161 /* Little signal handler that just does an exit(1) (thereby getting our
162 * cleanup routine called), except for TSTP, which does a clean suspend.
165 static void (*old_tstp)(int sig);
167 static void sighandler(int sig)
169 if (sig != SIGTSTP) {
170 endwin();
171 if (sig != SIGINT)
172 fprintf(stderr, "%s\n", strsignal(sig));
173 exit(1);
175 endwin();
176 signal(SIGTSTP, old_tstp);
177 raise(SIGTSTP);
178 doupdate();
179 signal(SIGTSTP, sighandler);
182 /*************************************************************************/
183 /*************************************************************************/
185 #define MAXCOLORS 256
187 static int colors[MAXCOLORS][2] = { {-1,-1} };
189 /* Return a color attribute value. */
191 static long getcolor(int fg, int bg)
193 int i;
195 if (colors[0][0] < 0) {
196 start_color();
197 memset(colors, -1, sizeof(colors));
198 colors[0][0] = COLOR_WHITE;
199 colors[0][1] = COLOR_BLACK;
201 if (fg == COLOR_WHITE && bg == COLOR_BLACK)
202 return COLOR_PAIR(0);
203 for (i = 1; i < MAXCOLORS; i++) {
204 if (colors[i][0] == fg && colors[i][1] == bg)
205 return COLOR_PAIR(i);
207 for (i = 1; i < MAXCOLORS; i++) {
208 if (colors[i][0] < 0) {
209 if (init_pair(i, fg, bg) == ERR)
210 continue;
211 colors[i][0] = fg;
212 colors[i][1] = bg;
213 return COLOR_PAIR(i);
216 return -1;
219 /*************************************************************************/
220 /*************************************************************************/
222 /* Set up the screen stuff. */
224 static void screen_setup(void)
226 /* Avoid messy keyfield signals while we're setting up */
227 signal(SIGINT, SIG_IGN);
228 signal(SIGQUIT, SIG_IGN);
229 signal(SIGTSTP, SIG_IGN);
231 initscr();
232 cbreak();
233 noecho();
234 nodelay(stdscr, TRUE);
235 keypad(stdscr, TRUE);
236 leaveok(stdscr, TRUE);
237 if ((has_color = has_colors()))
238 start_color();
239 getmaxyx(stdscr, scrheight, scrwidth);
240 scrwidth--; /* Don't draw in last column--this can cause scroll */
242 /* Cancel all this when we exit. */
243 atexit(screen_cleanup);
245 /* Catch signals so we can exit cleanly. */
246 signal(SIGINT, sighandler);
247 signal(SIGQUIT, sighandler);
248 signal(SIGTERM, sighandler);
249 signal(SIGHUP, sighandler);
250 signal(SIGSEGV, sighandler);
251 signal(SIGABRT, sighandler);
252 signal(SIGIOT, sighandler);
253 signal(SIGTRAP, sighandler);
254 signal(SIGBUS, sighandler);
255 signal(SIGFPE, sighandler);
256 signal(SIGUSR1, sighandler);
257 signal(SIGUSR2, sighandler);
258 signal(SIGALRM, sighandler);
259 #ifdef SIGSTKFLT
260 signal(SIGSTKFLT, sighandler);
261 #endif
262 signal(SIGTSTP, sighandler);
263 signal(SIGXCPU, sighandler);
264 signal(SIGXFSZ, sighandler);
265 signal(SIGVTALRM, sighandler);
267 /* Broken pipes don't want to bother us at all. */
268 signal(SIGPIPE, SIG_IGN);
271 /*************************************************************************/
273 /* Redraw everything on the screen. */
275 static void screen_refresh(void)
277 if (gmsg_inputwin)
278 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
279 if (plinebuf.win)
280 touchline(stdscr, plinebuf.y, plinebuf.height);
281 if (gmsgbuf.win)
282 touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
283 if (attdefbuf.win)
284 touchline(stdscr, attdefbuf.y, attdefbuf.height);
285 wnoutrefresh(stdscr);
286 doupdate();
289 /*************************************************************************/
291 /* Like screen_refresh(), but clear the screen first. */
293 static void screen_redraw(void)
295 clearok(stdscr, TRUE);
296 screen_refresh();
299 /*************************************************************************/
300 /************************* Text buffer routines **************************/
301 /*************************************************************************/
303 /* Put a line of text in a text buffer. */
305 static void outline(TextBuffer *buf, const char *s)
307 if (buf->line == buf->height) {
308 if (buf->win)
309 scroll(buf->win);
310 memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
311 buf->line--;
313 if (buf->win)
314 mvwaddstr(buf->win, buf->line, 0, s);
315 if (s != buf->text[buf->line]) /* check for restoring display */
316 buf->text[buf->line] = strdup(s);
317 buf->line++;
320 static void draw_text(int bufnum, const char *s)
322 char str[1024]; /* hopefully scrwidth < 1024 */
323 const char *t;
324 int indent = 0;
325 int x = 0, y = 0;
326 TextBuffer *buf;
328 switch (bufnum) {
329 case BUFFER_PLINE: buf = &plinebuf; break;
330 case BUFFER_GMSG: buf = &gmsgbuf; break;
331 case BUFFER_ATTDEF: buf = &attdefbuf; break;
332 default: return;
334 if (!buf->text)
335 return;
336 if (buf->win) {
337 getyx(stdscr, y, x);
338 attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
340 while (*s && isspace(*s))
341 s++;
342 while (strlen(s) > buf->width - indent) {
343 t = s + buf->width - indent;
344 while (t >= s && !isspace(*t))
345 t--;
346 while (t >= s && isspace(*t))
347 t--;
348 t++;
349 if (t < s)
350 t = s + buf->width - indent;
351 if (indent > 0)
352 sprintf(str, "%*s", indent, "");
353 strncpy(str+indent, s, t-s);
354 str[t-s+indent] = 0;
355 outline(buf, str);
356 indent = 2;
357 while (isspace(*t))
358 t++;
359 s = t;
361 if (indent > 0)
362 sprintf(str, "%*s", indent, "");
363 strcpy(str+indent, s);
364 outline(buf, str);
365 if (buf->win) {
366 move(y, x);
367 screen_refresh();
371 /*************************************************************************/
373 /* Clear the contents of a text buffer. */
375 static void clear_text(int bufnum)
377 TextBuffer *buf;
378 int i;
380 switch (bufnum) {
381 case BUFFER_PLINE: buf = &plinebuf; break;
382 case BUFFER_GMSG: buf = &gmsgbuf; break;
383 case BUFFER_ATTDEF: buf = &attdefbuf; break;
384 default: return;
386 if (buf->text) {
387 for (i = 0; i < buf->height; i++) {
388 if (buf->text[i]) {
389 free(buf->text[i]);
390 buf->text[i] = NULL;
393 buf->line = 0;
395 if (buf->win) {
396 werase(buf->win);
397 screen_refresh();
401 /*************************************************************************/
403 /* Restore the contents of the given text buffer. */
405 static void restore_text(TextBuffer *buf)
407 buf->line = 0;
408 while (buf->line < buf->height && buf->text[buf->line])
409 outline(buf, buf->text[buf->line]);
412 /*************************************************************************/
414 /* Open a window for the given text buffer. */
416 static void open_textwin(TextBuffer *buf)
418 if (buf->height <= 0 || buf->width <= 0) {
419 char str[256];
420 move(scrheight-1, 0);
421 snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
422 buf->width, buf->height);
423 addstr(str);
424 exit(1);
426 if (!buf->win) {
427 buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
428 scrollok(buf->win, TRUE);
430 if (!buf->text)
431 buf->text = calloc(buf->height, sizeof(char *));
432 else
433 restore_text(buf);
436 /*************************************************************************/
438 /* Close the window for the given text buffer, if it's open. */
440 static void close_textwin(TextBuffer *buf)
442 if (buf->win) {
443 delwin(buf->win);
444 buf->win = NULL;
448 /*************************************************************************/
449 /************************ Field drawing routines *************************/
450 /*************************************************************************/
452 /* Are we on a wide screen (>=92 columns)? */
453 static int wide_screen = 0;
455 /* Field display X/Y coordinates. */
456 static const int own_coord[2] = {0,0};
457 static int other_coord[5][2] = /* Recomputed based on screen width */
458 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
460 /* Position of the status window. */
461 static const int status_coord[2] = {28,25};
462 static const int next_coord[2] = {40,24};
463 static const int alt_status_coord[2] = {28,2};
464 static const int alt_next_coord[2] = {29,8};
466 /* Position of the attacks/defenses window. */
467 static const int attdef_coord[2] = {28,28};
468 static const int alt_attdef_coord[2] = {28,24};
470 /* Position of the text window. X coordinate is ignored. */
471 static const int field_text_coord[2] = {0,47};
473 /* Information for drawing blocks. Color attributes are added to blocks in
474 * the setup_fields() routine. */
475 static int tile_chars[15] =
476 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
478 /* Are we redrawing the entire display? */
479 static int field_redraw = 0;
481 /*************************************************************************/
482 /*************************************************************************/
484 /* Set up the field display. */
486 static void draw_own_field(void);
487 static void draw_other_field(int player);
488 static void draw_status(void);
489 static void draw_specials(void);
490 static void draw_gmsg_input(const char *s, int pos);
492 static void setup_fields(void)
494 int i, j, x, y, base, delta, attdefbot;
495 char buf[32];
497 if (!(tile_chars[0] & A_ATTRIBUTES)) {
498 for (i = 1; i < 15; i++)
499 tile_chars[i] |= A_BOLD;
500 tile_chars[1] |= getcolor(COLOR_BLUE, COLOR_BLACK);
501 tile_chars[2] |= getcolor(COLOR_YELLOW, COLOR_BLACK);
502 tile_chars[3] |= getcolor(COLOR_GREEN, COLOR_BLACK);
503 tile_chars[4] |= getcolor(COLOR_MAGENTA, COLOR_BLACK);
504 tile_chars[5] |= getcolor(COLOR_RED, COLOR_BLACK);
507 field_redraw = 1;
508 leaveok(stdscr, TRUE);
509 close_textwin(&plinebuf);
510 clear();
511 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
513 if (scrwidth >= 92) {
514 wide_screen = 1;
515 base = 41;
516 } else {
517 base = 28;
519 delta = (scrwidth - base) / 3;
520 base += 2 + (delta - (FIELD_WIDTH+5)) / 2;
521 other_coord[0][0] = base;
522 other_coord[1][0] = base + delta;
523 other_coord[2][0] = base + delta*2;
524 other_coord[3][0] = base + delta;
525 other_coord[4][0] = base + delta*2;
527 attdefbot = field_text_coord[1] - 1;
528 if (scrheight - field_text_coord[1] > 3) {
529 move(field_text_coord[1], 0);
530 hline(MY_HLINE2, scrwidth);
531 attdefbot--;
532 if (scrheight - field_text_coord[1] > 5) {
533 move(scrheight-2, 0);
534 hline(MY_HLINE2, scrwidth);
535 attrset(MY_BOLD);
536 move(scrheight-1, 0);
537 addstr("F1=Show Fields F2=Partyline F3=Winlist");
538 move(scrheight-1, scrwidth-8);
539 addstr("F10=Quit");
540 attrset(A_NORMAL);
541 gmsgbuf.y = field_text_coord[1]+1;
542 gmsgbuf.height = scrheight - field_text_coord[1] - 3;
543 } else {
544 gmsgbuf.y = field_text_coord[1]+1;
545 gmsgbuf.height = scrheight - field_text_coord[1] - 1;
547 } else {
548 gmsgbuf.y = field_text_coord[1];
549 gmsgbuf.height = scrheight - field_text_coord[1];
551 gmsgbuf.x = field_text_coord[0];
552 gmsgbuf.width = scrwidth;
553 open_textwin(&gmsgbuf);
555 x = own_coord[0];
556 y = own_coord[1];
557 sprintf(buf, "%d", my_playernum);
558 mvaddstr(y, x+FIELD_WIDTH*2+2, buf);
559 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
560 mvaddch(y+i, x+FIELD_WIDTH*2+2, players[my_playernum-1][i-2]);
561 move(y, x);
562 vline(MY_VLINE, FIELD_HEIGHT*2);
563 move(y, x+FIELD_WIDTH*2+1);
564 vline(MY_VLINE, FIELD_HEIGHT*2);
565 move(y+FIELD_HEIGHT*2, x);
566 addch(MY_LLCORNER);
567 hline(MY_HLINE, FIELD_WIDTH*2);
568 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
569 addch(MY_LRCORNER);
570 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
571 draw_own_field();
572 draw_specials();
574 for (j = 0; j < 5; j++) {
575 x = other_coord[j][0];
576 y = other_coord[j][1];
577 move(y, x);
578 vline(MY_VLINE, FIELD_HEIGHT);
579 move(y, x+FIELD_WIDTH+1);
580 vline(MY_VLINE, FIELD_HEIGHT);
581 move(y+FIELD_HEIGHT, x);
582 addch(MY_LLCORNER);
583 hline(MY_HLINE, FIELD_WIDTH);
584 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
585 addch(MY_LRCORNER);
586 if (j+1 >= my_playernum) {
587 sprintf(buf, "%d", j+2);
588 mvaddstr(y, x+FIELD_WIDTH+2, buf);
589 if (players[j+1]) {
590 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
591 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j+1][i]);
593 draw_other_field(j+2);
594 } else {
595 sprintf(buf, "%d", j+1);
596 mvaddstr(y, x+FIELD_WIDTH+2, buf);
597 if (players[j]) {
598 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
599 mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j][i]);
601 draw_other_field(j+1);
605 if (wide_screen) {
606 x = alt_status_coord[0];
607 y = alt_status_coord[1];
608 mvaddstr(y, x, "Lines:");
609 mvaddstr(y+1, x, "Level:");
610 x = alt_next_coord[0];
611 y = alt_next_coord[1];
612 mvaddstr(y-2, x-1, "Next piece:");
613 move(y-1, x-1);
614 addch(MY_ULCORNER);
615 hline(MY_HLINE, 8);
616 mvaddch(y-1, x+8, MY_URCORNER);
617 move(y, x-1);
618 vline(MY_VLINE, 8);
619 move(y, x+8);
620 vline(MY_VLINE, 8);
621 move(y+8, x-1);
622 addch(MY_LLCORNER);
623 hline(MY_HLINE, 8);
624 mvaddch(y+8, x+8, MY_LRCORNER);
625 } else {
626 x = status_coord[0];
627 y = status_coord[1];
628 mvaddstr(y-1, x, "Next piece:");
629 mvaddstr(y, x, "Lines:");
630 mvaddstr(y+1, x, "Level:");
632 if (playing_game)
633 draw_status();
635 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
636 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
637 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
638 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
639 open_textwin(&attdefbuf);
641 if (gmsg_inputwin) {
642 delwin(gmsg_inputwin);
643 gmsg_inputwin = NULL;
644 draw_gmsg_input(NULL, -1);
647 screen_refresh();
648 field_redraw = 0;
651 /*************************************************************************/
653 /* Display the player's own field. */
655 static void draw_own_field(void)
657 int x, y, x0, y0;
658 Field *f = &fields[my_playernum-1];
659 int shadow[4] = { -1, -1, -1, -1 };
661 if (dispmode != MODE_FIELDS)
662 return;
664 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
665 if (playing_game && cast_shadow) {
666 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
667 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
668 int i, j;
670 for (j = 0; j < 4; j++) {
671 if (y+j < 0) {
672 shape += 4;
673 continue;
675 for (i = 0; i < 4; i++) {
676 if (*shape++)
677 shadow[i] = y + j;
682 x0 = own_coord[0]+1;
683 y0 = own_coord[1];
684 for (y = 0; y < 22; y++) {
685 for (x = 0; x < 12; x++) {
686 int c = tile_chars[(int) (*f)[y][x]];
688 if (playing_game && cast_shadow) {
689 PieceData *piece = &piecedata[current_piece][current_rotation];
690 int piece_x = current_x - piece->hot_x;
692 if (x >= piece_x && x <= piece_x + 3
693 && shadow[(x - piece_x)] >= 0
694 && shadow[(x - piece_x)] < y
695 && ((c & 0x7f) == ' ')) {
696 c = (c & (~0x7f)) | '.'
697 | getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
701 mvaddch(y0+y*2, x0+x*2, c);
702 addch(c);
703 mvaddch(y0+y*2+1, x0+x*2, c);
704 addch(c);
707 if (gmsg_inputwin) {
708 delwin(gmsg_inputwin);
709 gmsg_inputwin = NULL;
710 draw_gmsg_input(NULL, -1);
712 if (!field_redraw)
713 screen_refresh();
716 /*************************************************************************/
718 /* Display another player's field. */
720 static void draw_other_field(int player)
722 int x, y, x0, y0;
723 Field *f;
725 if (dispmode != MODE_FIELDS)
726 return;
727 f = &fields[player-1];
728 if (player > my_playernum)
729 player--;
730 player--;
731 x0 = other_coord[player][0]+1;
732 y0 = other_coord[player][1];
733 for (y = 0; y < 22; y++) {
734 move(y0+y, x0);
735 for (x = 0; x < 12; x++) {
736 addch(tile_chars[(int) (*f)[y][x]]);
739 if (gmsg_inputwin) {
740 delwin(gmsg_inputwin);
741 gmsg_inputwin = NULL;
742 draw_gmsg_input(NULL, -1);
744 if (!field_redraw)
745 screen_refresh();
748 /*************************************************************************/
750 /* Display the current game status (level, lines, next piece). */
752 static void draw_status(void)
754 int x, y, i, j;
755 char buf[32], shape[4][4];
757 x = wide_screen ? alt_status_coord[0] : status_coord[0];
758 y = wide_screen ? alt_status_coord[1] : status_coord[1];
759 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
760 mvaddstr(y, x+7, buf);
761 sprintf(buf, "%d", levels[my_playernum]);
762 mvaddstr(y+1, x+7, buf);
763 x = wide_screen ? alt_next_coord[0] : next_coord[0];
764 y = wide_screen ? alt_next_coord[1] : next_coord[1];
765 if (get_shape(next_piece, 0, shape) == 0) {
766 for (j = 0; j < 4; j++) {
767 if (!wide_screen)
768 move(y+j, x);
769 for (i = 0; i < 4; i++) {
770 if (wide_screen) {
771 move(y+j*2, x+i*2);
772 addch(tile_chars[(int) shape[j][i]]);
773 addch(tile_chars[(int) shape[j][i]]);
774 move(y+j*2+1, x+i*2);
775 addch(tile_chars[(int) shape[j][i]]);
776 addch(tile_chars[(int) shape[j][i]]);
777 } else
778 addch(tile_chars[(int) shape[j][i]]);
784 /*************************************************************************/
786 /* Display the special inventory and description of the current special. */
788 static const char *descs[] = {
789 " ",
790 "Add Line ",
791 "Clear Line ",
792 "Nuke Field ",
793 "Clear Random Blocks ",
794 "Switch Fields ",
795 "Clear Special Blocks",
796 "Block Gravity ",
797 "Blockquake ",
798 "Block Bomb "
801 static void draw_specials(void)
803 int x, y, i;
805 if (dispmode != MODE_FIELDS)
806 return;
807 x = own_coord[0];
808 y = own_coord[1]+45;
809 mvaddstr(y, x, descs[specials[0]+1]);
810 move(y+1, x+10);
811 i = 0;
812 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
813 addch(tile_chars[specials[i]+6]);
814 i++;
815 x++;
817 while (x < attdef_coord[0]-1) {
818 addch(tile_chars[0]);
819 x++;
821 if (!field_redraw)
822 screen_refresh();
825 /*************************************************************************/
827 /* Display an attack/defense message. */
829 static const char *msgs[][2] = {
830 { "cs1", "1 Line Added to All" },
831 { "cs2", "2 Lines Added to All" },
832 { "cs4", "4 Lines Added to All" },
833 { "a", "Add Line" },
834 { "c", "Clear Line" },
835 { "n", "Nuke Field" },
836 { "r", "Clear Random Blocks" },
837 { "s", "Switch Fields" },
838 { "b", "Clear Special Blocks" },
839 { "g", "Block Gravity" },
840 { "q", "Blockquake" },
841 { "o", "Block Bomb" },
842 { NULL }
845 static void draw_attdef(const char *type, int from, int to)
847 int i, width;
848 char buf[512];
850 width = other_coord[4][0] - attdef_coord[0] - 1;
851 for (i = 0; msgs[i][0]; i++) {
852 if (strcmp(type, msgs[i][0]) == 0)
853 break;
855 if (!msgs[i][0])
856 return;
857 strcpy(buf, msgs[i][1]);
858 if (to != 0)
859 sprintf(buf+strlen(buf), " on %s", players[to-1]);
860 if (from == 0)
861 sprintf(buf+strlen(buf), " by Server");
862 else
863 sprintf(buf+strlen(buf), " by %s", players[from-1]);
864 draw_text(BUFFER_ATTDEF, buf);
867 /*************************************************************************/
869 /* Display the in-game text window. */
871 static void draw_gmsg_input(const char *s, int pos)
873 static int start = 0; /* Start of displayed part of input line */
874 static const char *last_s;
875 static int last_pos;
877 if (s)
878 last_s = s;
879 else
880 s = last_s;
881 if (pos >= 0)
882 last_pos = pos;
883 else
884 pos = last_pos;
886 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
888 if (!gmsg_inputwin) {
889 gmsg_inputpos = scrheight/2 - 1;
890 gmsg_inputheight = 3;
891 gmsg_inputwin =
892 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
893 werase(gmsg_inputwin);
894 leaveok(gmsg_inputwin, FALSE);
895 leaveok(stdscr, FALSE);
896 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
899 if (strlen(s) < scrwidth-7) {
900 start = 0;
901 mvwaddstr(gmsg_inputwin, 1, 6, s);
902 wmove(gmsg_inputwin, 1, 6+strlen(s));
903 move(gmsg_inputpos+1, 6+strlen(s));
904 wclrtoeol(gmsg_inputwin);
905 wmove(gmsg_inputwin, 1, 6+pos);
906 move(gmsg_inputpos+1, 6+pos);
907 } else {
908 if (pos < start+8) {
909 start = pos-8;
910 if (start < 0)
911 start = 0;
912 } else if (pos > start + scrwidth-15) {
913 start = pos - (scrwidth-15);
914 if (start > strlen(s) - (scrwidth-7))
915 start = strlen(s) - (scrwidth-7);
917 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
918 wmove(gmsg_inputwin, 1, 6 + (pos-start));
919 move(gmsg_inputpos+1, 6 + (pos-start));
921 screen_refresh();
924 /*************************************************************************/
926 /* Clear the in-game text window. */
928 static void clear_gmsg_input(void)
930 if (gmsg_inputwin) {
931 delwin(gmsg_inputwin);
932 gmsg_inputwin = NULL;
933 leaveok(stdscr, TRUE);
934 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
935 setup_fields();
936 screen_refresh();
940 /*************************************************************************/
941 /*************************** Partyline display ***************************/
942 /*************************************************************************/
944 static void setup_partyline(void)
946 close_textwin(&gmsgbuf);
947 close_textwin(&attdefbuf);
948 clear();
950 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
952 plinebuf.x = plinebuf.y = 0;
953 plinebuf.width = scrwidth;
954 plinebuf.height = scrheight-4;
955 open_textwin(&plinebuf);
957 move(scrheight-4, 0);
958 hline(MY_HLINE, scrwidth);
959 move(scrheight-3, 0);
960 addstr("> ");
962 move(scrheight-2, 0);
963 hline(MY_HLINE2, scrwidth);
964 attrset(MY_BOLD);
965 move(scrheight-1, 0);
966 addstr("F1=Show Fields F2=Partyline F3=Winlist");
967 move(scrheight-1, scrwidth-8);
968 addstr("F10=Quit");
969 attrset(A_NORMAL);
971 move(scrheight-3, 2);
972 leaveok(stdscr, FALSE);
973 screen_refresh();
976 /*************************************************************************/
978 static void draw_partyline_input(const char *s, int pos)
980 static int start = 0; /* Start of displayed part of input line */
982 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
983 if (strlen(s) < scrwidth-3) {
984 start = 0;
985 mvaddstr(scrheight-3, 2, s);
986 move(scrheight-3, 2+strlen(s));
987 clrtoeol();
988 move(scrheight-3, 2+pos);
989 } else {
990 if (pos < start+8) {
991 start = pos-8;
992 if (start < 0)
993 start = 0;
994 } else if (pos > start + scrwidth-11) {
995 start = pos - (scrwidth-11);
996 if (start > strlen(s) - (scrwidth-3))
997 start = strlen(s) - (scrwidth-3);
999 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
1000 move(scrheight-3, 2 + (pos-start));
1002 screen_refresh();
1005 /*************************************************************************/
1006 /**************************** Winlist display ****************************/
1007 /*************************************************************************/
1009 static void setup_winlist(void)
1011 int i, x;
1012 char buf[32];
1014 leaveok(stdscr, TRUE);
1015 close_textwin(&plinebuf);
1016 clear();
1017 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
1019 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
1020 x = scrwidth/2 - strlen(winlist[i].name);
1021 if (x < 0)
1022 x = 0;
1023 if (winlist[i].team) {
1024 if (x < 4)
1025 x = 4;
1026 mvaddstr(i*2, x-4, "<T>");
1028 mvaddstr(i*2, x, winlist[i].name);
1029 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
1030 if (winlist[i].games) {
1031 int avg100 = winlist[i].points*100 / winlist[i].games;
1032 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1033 " %d.%02d",avg100/100, avg100%100);
1035 x += strlen(winlist[i].name) + 2;
1036 if (x > scrwidth - strlen(buf))
1037 x = scrwidth - strlen(buf);
1038 mvaddstr(i*2, x, buf);
1041 move(scrheight-2, 0);
1042 hline(MY_HLINE2, scrwidth);
1043 attrset(MY_BOLD);
1044 move(scrheight-1, 0);
1045 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1046 move(scrheight-1, scrwidth-8);
1047 addstr("F10=Quit");
1048 attrset(A_NORMAL);
1050 screen_refresh();
1053 /*************************************************************************/
1054 /************************** Interface declaration ************************/
1055 /*************************************************************************/
1057 Interface tty_interface = {
1059 wait_for_input,
1061 screen_setup,
1062 screen_refresh,
1063 screen_redraw,
1065 draw_text,
1066 clear_text,
1068 setup_fields,
1069 draw_own_field,
1070 draw_other_field,
1071 draw_status,
1072 draw_specials,
1073 draw_attdef,
1074 draw_gmsg_input,
1075 clear_gmsg_input,
1077 setup_partyline,
1078 draw_partyline_input,
1080 setup_winlist
1083 /*************************************************************************/