ChangeLog update
[tetrinet/clach04.git] / tty.c
blob8c055f58338450c82466bd9b9020aa0129edad9c
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] = {1,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] = {29,25};
462 static const int next_coord[2] = {41,24};
463 static const int alt_status_coord[2] = {29,2};
464 static const int alt_next_coord[2] = {30,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-1, buf);
559 for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
560 mvaddch(y+i, x-1, players[my_playernum-1][i-2]);
561 if (teams[my_playernum-1] != '\0') {
562 mvaddstr(y, x+FIELD_WIDTH*2+2, "T");
563 for (i = 2; i < FIELD_HEIGHT*2 && teams[my_playernum-1][i-2]; i++)
564 mvaddch(y+i, x+FIELD_WIDTH*2+2, teams[my_playernum-1][i-2]);
566 move(y, x);
567 vline(MY_VLINE, FIELD_HEIGHT*2);
568 move(y, x+FIELD_WIDTH*2+1);
569 vline(MY_VLINE, FIELD_HEIGHT*2);
570 move(y+FIELD_HEIGHT*2, x);
571 addch(MY_LLCORNER);
572 hline(MY_HLINE, FIELD_WIDTH*2);
573 move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
574 addch(MY_LRCORNER);
575 mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
576 draw_own_field();
577 draw_specials();
579 for (j = 0; j < 5; j++) {
580 x = other_coord[j][0];
581 y = other_coord[j][1];
582 move(y, x);
583 vline(MY_VLINE, FIELD_HEIGHT);
584 move(y, x+FIELD_WIDTH+1);
585 vline(MY_VLINE, FIELD_HEIGHT);
586 move(y+FIELD_HEIGHT, x);
587 addch(MY_LLCORNER);
588 hline(MY_HLINE, FIELD_WIDTH);
589 move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
590 addch(MY_LRCORNER);
591 if (j+1 >= my_playernum) {
592 sprintf(buf, "%d", j+2);
593 mvaddstr(y, x-1, buf);
594 if (players[j+1]) {
595 for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
596 mvaddch(y+i+2, x-1, players[j+1][i]);
597 if (teams[j+1] != '\0') {
598 mvaddstr(y, x+FIELD_WIDTH+2, "T");
599 for (i = 0; i < FIELD_HEIGHT-2 && teams[j+1][i]; i++)
600 mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j+1][i]);
603 draw_other_field(j+2);
604 } else {
605 sprintf(buf, "%d", j+1);
606 mvaddstr(y, x-1, buf);
607 if (players[j]) {
608 for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
609 mvaddch(y+i+2, x-1, players[j][i]);
610 if (teams[j] != '\0') {
611 mvaddstr(y, x+FIELD_WIDTH+2, "T");
612 for (i = 0; i < FIELD_HEIGHT-2 && teams[j][i]; i++)
613 mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j][i]);
616 draw_other_field(j+1);
620 if (wide_screen) {
621 x = alt_status_coord[0];
622 y = alt_status_coord[1];
623 mvaddstr(y, x, "Lines:");
624 mvaddstr(y+1, x, "Level:");
625 x = alt_next_coord[0];
626 y = alt_next_coord[1];
627 mvaddstr(y-2, x-1, "Next piece:");
628 move(y-1, x-1);
629 addch(MY_ULCORNER);
630 hline(MY_HLINE, 8);
631 mvaddch(y-1, x+8, MY_URCORNER);
632 move(y, x-1);
633 vline(MY_VLINE, 8);
634 move(y, x+8);
635 vline(MY_VLINE, 8);
636 move(y+8, x-1);
637 addch(MY_LLCORNER);
638 hline(MY_HLINE, 8);
639 mvaddch(y+8, x+8, MY_LRCORNER);
640 } else {
641 x = status_coord[0];
642 y = status_coord[1];
643 mvaddstr(y-1, x, "Next piece:");
644 mvaddstr(y, x, "Lines:");
645 mvaddstr(y+1, x, "Level:");
647 if (playing_game)
648 draw_status();
650 attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
651 attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
652 attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
653 attdefbuf.height = (attdefbot+1) - attdefbuf.y;
654 open_textwin(&attdefbuf);
656 if (gmsg_inputwin) {
657 delwin(gmsg_inputwin);
658 gmsg_inputwin = NULL;
659 draw_gmsg_input(NULL, -1);
662 screen_refresh();
663 field_redraw = 0;
666 /*************************************************************************/
668 /* Display the player's own field. */
670 static void draw_own_field(void)
672 int x, y, x0, y0;
673 Field *f = &fields[my_playernum-1];
674 int shadow[4] = { -1, -1, -1, -1 };
676 if (dispmode != MODE_FIELDS)
677 return;
679 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
680 if (playing_game && cast_shadow) {
681 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
682 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
683 int i, j;
685 for (j = 0; j < 4; j++) {
686 if (y+j < 0) {
687 shape += 4;
688 continue;
690 for (i = 0; i < 4; i++) {
691 if (*shape++)
692 shadow[i] = y + j;
697 x0 = own_coord[0]+1;
698 y0 = own_coord[1];
699 for (y = 0; y < 22; y++) {
700 for (x = 0; x < 12; x++) {
701 int c = tile_chars[(int) (*f)[y][x]];
703 if (playing_game && cast_shadow) {
704 PieceData *piece = &piecedata[current_piece][current_rotation];
705 int piece_x = current_x - piece->hot_x;
707 if (x >= piece_x && x <= piece_x + 3
708 && shadow[(x - piece_x)] >= 0
709 && shadow[(x - piece_x)] < y
710 && ((c & 0x7f) == ' ')) {
711 c = (c & (~0x7f)) | '.'
712 | getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
716 mvaddch(y0+y*2, x0+x*2, c);
717 addch(c);
718 mvaddch(y0+y*2+1, x0+x*2, c);
719 addch(c);
722 if (gmsg_inputwin) {
723 delwin(gmsg_inputwin);
724 gmsg_inputwin = NULL;
725 draw_gmsg_input(NULL, -1);
727 if (!field_redraw)
728 screen_refresh();
731 /*************************************************************************/
733 /* Display another player's field. */
735 static void draw_other_field(int player)
737 int x, y, x0, y0;
738 Field *f;
740 if (dispmode != MODE_FIELDS)
741 return;
742 f = &fields[player-1];
743 if (player > my_playernum)
744 player--;
745 player--;
746 x0 = other_coord[player][0]+1;
747 y0 = other_coord[player][1];
748 for (y = 0; y < 22; y++) {
749 move(y0+y, x0);
750 for (x = 0; x < 12; x++) {
751 addch(tile_chars[(int) (*f)[y][x]]);
754 if (gmsg_inputwin) {
755 delwin(gmsg_inputwin);
756 gmsg_inputwin = NULL;
757 draw_gmsg_input(NULL, -1);
759 if (!field_redraw)
760 screen_refresh();
763 /*************************************************************************/
765 /* Display the current game status (level, lines, next piece). */
767 static void draw_status(void)
769 int x, y, i, j;
770 char buf[32], shape[4][4];
772 x = wide_screen ? alt_status_coord[0] : status_coord[0];
773 y = wide_screen ? alt_status_coord[1] : status_coord[1];
774 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
775 mvaddstr(y, x+7, buf);
776 sprintf(buf, "%d", levels[my_playernum]);
777 mvaddstr(y+1, x+7, buf);
778 x = wide_screen ? alt_next_coord[0] : next_coord[0];
779 y = wide_screen ? alt_next_coord[1] : next_coord[1];
780 if (get_shape(next_piece, 0, shape) == 0) {
781 for (j = 0; j < 4; j++) {
782 if (!wide_screen)
783 move(y+j, x);
784 for (i = 0; i < 4; i++) {
785 if (wide_screen) {
786 move(y+j*2, x+i*2);
787 addch(tile_chars[(int) shape[j][i]]);
788 addch(tile_chars[(int) shape[j][i]]);
789 move(y+j*2+1, x+i*2);
790 addch(tile_chars[(int) shape[j][i]]);
791 addch(tile_chars[(int) shape[j][i]]);
792 } else
793 addch(tile_chars[(int) shape[j][i]]);
799 /*************************************************************************/
801 /* Display the special inventory and description of the current special. */
803 static const char *descs[] = {
804 " ",
805 "Add Line ",
806 "Clear Line ",
807 "Nuke Field ",
808 "Clear Random Blocks ",
809 "Switch Fields ",
810 "Clear Special Blocks",
811 "Block Gravity ",
812 "Blockquake ",
813 "Block Bomb "
816 static void draw_specials(void)
818 int x, y, i;
820 if (dispmode != MODE_FIELDS)
821 return;
822 x = own_coord[0];
823 y = own_coord[1]+45;
824 mvaddstr(y, x, descs[specials[0]+1]);
825 move(y+1, x+10);
826 i = 0;
827 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
828 addch(tile_chars[specials[i]+6]);
829 i++;
830 x++;
832 while (x < attdef_coord[0]-1) {
833 addch(tile_chars[0]);
834 x++;
836 if (!field_redraw)
837 screen_refresh();
840 /*************************************************************************/
842 /* Display an attack/defense message. */
844 static const char *msgs[][2] = {
845 { "cs1", "1 Line Added to All" },
846 { "cs2", "2 Lines Added to All" },
847 { "cs4", "4 Lines Added to All" },
848 { "a", "Add Line" },
849 { "c", "Clear Line" },
850 { "n", "Nuke Field" },
851 { "r", "Clear Random Blocks" },
852 { "s", "Switch Fields" },
853 { "b", "Clear Special Blocks" },
854 { "g", "Block Gravity" },
855 { "q", "Blockquake" },
856 { "o", "Block Bomb" },
857 { NULL }
860 static void draw_attdef(const char *type, int from, int to)
862 int i, width;
863 char buf[512];
865 width = other_coord[4][0] - attdef_coord[0] - 1;
866 for (i = 0; msgs[i][0]; i++) {
867 if (strcmp(type, msgs[i][0]) == 0)
868 break;
870 if (!msgs[i][0])
871 return;
872 strcpy(buf, msgs[i][1]);
873 if (to != 0)
874 sprintf(buf+strlen(buf), " on %s", players[to-1]);
875 if (from == 0)
876 sprintf(buf+strlen(buf), " by Server");
877 else
878 sprintf(buf+strlen(buf), " by %s", players[from-1]);
879 draw_text(BUFFER_ATTDEF, buf);
882 /*************************************************************************/
884 /* Display the in-game text window. */
886 static void draw_gmsg_input(const char *s, int pos)
888 static int start = 0; /* Start of displayed part of input line */
889 static const char *last_s;
890 static int last_pos;
892 if (s)
893 last_s = s;
894 else
895 s = last_s;
896 if (pos >= 0)
897 last_pos = pos;
898 else
899 pos = last_pos;
901 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
903 if (!gmsg_inputwin) {
904 gmsg_inputpos = scrheight/2 - 1;
905 gmsg_inputheight = 3;
906 gmsg_inputwin =
907 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
908 werase(gmsg_inputwin);
909 leaveok(gmsg_inputwin, FALSE);
910 leaveok(stdscr, FALSE);
911 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
914 if (strlen(s) < scrwidth-7) {
915 start = 0;
916 mvwaddstr(gmsg_inputwin, 1, 6, s);
917 wmove(gmsg_inputwin, 1, 6+strlen(s));
918 move(gmsg_inputpos+1, 6+strlen(s));
919 wclrtoeol(gmsg_inputwin);
920 wmove(gmsg_inputwin, 1, 6+pos);
921 move(gmsg_inputpos+1, 6+pos);
922 } else {
923 if (pos < start+8) {
924 start = pos-8;
925 if (start < 0)
926 start = 0;
927 } else if (pos > start + scrwidth-15) {
928 start = pos - (scrwidth-15);
929 if (start > strlen(s) - (scrwidth-7))
930 start = strlen(s) - (scrwidth-7);
932 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
933 wmove(gmsg_inputwin, 1, 6 + (pos-start));
934 move(gmsg_inputpos+1, 6 + (pos-start));
936 screen_refresh();
939 /*************************************************************************/
941 /* Clear the in-game text window. */
943 static void clear_gmsg_input(void)
945 if (gmsg_inputwin) {
946 delwin(gmsg_inputwin);
947 gmsg_inputwin = NULL;
948 leaveok(stdscr, TRUE);
949 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
950 setup_fields();
951 screen_refresh();
955 /*************************************************************************/
956 /*************************** Partyline display ***************************/
957 /*************************************************************************/
959 static void setup_partyline(void)
961 close_textwin(&gmsgbuf);
962 close_textwin(&attdefbuf);
963 clear();
965 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
967 plinebuf.x = plinebuf.y = 0;
968 plinebuf.width = scrwidth;
969 plinebuf.height = scrheight-4;
970 open_textwin(&plinebuf);
972 move(scrheight-4, 0);
973 hline(MY_HLINE, scrwidth);
974 move(scrheight-3, 0);
975 addstr("> ");
977 move(scrheight-2, 0);
978 hline(MY_HLINE2, scrwidth);
979 attrset(MY_BOLD);
980 move(scrheight-1, 0);
981 addstr("F1=Show Fields F2=Partyline F3=Winlist");
982 move(scrheight-1, scrwidth-8);
983 addstr("F10=Quit");
984 attrset(A_NORMAL);
986 move(scrheight-3, 2);
987 leaveok(stdscr, FALSE);
988 screen_refresh();
991 /*************************************************************************/
993 static void draw_partyline_input(const char *s, int pos)
995 static int start = 0; /* Start of displayed part of input line */
997 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
998 if (strlen(s) < scrwidth-3) {
999 start = 0;
1000 mvaddstr(scrheight-3, 2, s);
1001 move(scrheight-3, 2+strlen(s));
1002 clrtoeol();
1003 move(scrheight-3, 2+pos);
1004 } else {
1005 if (pos < start+8) {
1006 start = pos-8;
1007 if (start < 0)
1008 start = 0;
1009 } else if (pos > start + scrwidth-11) {
1010 start = pos - (scrwidth-11);
1011 if (start > strlen(s) - (scrwidth-3))
1012 start = strlen(s) - (scrwidth-3);
1014 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
1015 move(scrheight-3, 2 + (pos-start));
1017 screen_refresh();
1020 /*************************************************************************/
1021 /**************************** Winlist display ****************************/
1022 /*************************************************************************/
1024 static void setup_winlist(void)
1026 int i, x;
1027 char buf[32];
1029 leaveok(stdscr, TRUE);
1030 close_textwin(&plinebuf);
1031 clear();
1032 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
1034 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
1035 x = scrwidth/2 - strlen(winlist[i].name);
1036 if (x < 0)
1037 x = 0;
1038 if (winlist[i].team) {
1039 if (x < 4)
1040 x = 4;
1041 mvaddstr(i*2, x-4, "<T>");
1043 mvaddstr(i*2, x, winlist[i].name);
1044 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
1045 if (winlist[i].games) {
1046 int avg100 = winlist[i].points*100 / winlist[i].games;
1047 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1048 " %d.%02d",avg100/100, avg100%100);
1050 x += strlen(winlist[i].name) + 2;
1051 if (x > scrwidth - strlen(buf))
1052 x = scrwidth - strlen(buf);
1053 mvaddstr(i*2, x, buf);
1056 move(scrheight-2, 0);
1057 hline(MY_HLINE2, scrwidth);
1058 attrset(MY_BOLD);
1059 move(scrheight-1, 0);
1060 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1061 move(scrheight-1, scrwidth-8);
1062 addstr("F10=Quit");
1063 attrset(A_NORMAL);
1065 screen_refresh();
1068 /*************************************************************************/
1069 /************************** Interface declaration ************************/
1070 /*************************************************************************/
1072 Interface tty_interface = {
1074 wait_for_input,
1076 screen_setup,
1077 screen_refresh,
1078 screen_redraw,
1080 draw_text,
1081 clear_text,
1083 setup_fields,
1084 draw_own_field,
1085 draw_other_field,
1086 draw_status,
1087 draw_specials,
1088 draw_attdef,
1089 draw_gmsg_input,
1090 clear_gmsg_input,
1092 setup_partyline,
1093 draw_partyline_input,
1095 setup_winlist
1098 /*************************************************************************/