Added support for pieces casting 'shadow'. This feature is controlled by -shadow...
[tetrinet.git] / tty.c
blob0562042df529af71f928da0c1d3d4f463a1ec719
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];
661 int shadow[4] = { -1, -1, -1, -1 };
663 if (dispmode != MODE_FIELDS)
664 return;
666 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
667 if (playing_game && cast_shadow) {
668 int x = current_x - piecedata[current_piece][current_rotation].hot_x;
669 int y = current_y - piecedata[current_piece][current_rotation].hot_y;
670 char *shape = (char *) piecedata[current_piece][current_rotation].shape;
671 int i, j;
673 for (j = 0; j < 4; j++) {
674 if (y+j < 0) {
675 shape += 4;
676 continue;
678 for (i = 0; i < 4; i++) {
679 if (*shape++)
680 shadow[i] = y + j;
685 x0 = own_coord[0]+1;
686 y0 = own_coord[1];
687 for (y = 0; y < 22; y++) {
688 for (x = 0; x < 12; x++) {
689 int c = tile_chars[(*f)[y][x]];
691 if (playing_game && cast_shadow) {
692 PieceData *piece = &piecedata[current_piece][current_rotation];
693 int piece_x = current_x - piece->hot_x;
695 if (x >= piece_x && x <= piece_x + 3
696 && shadow[(x - piece_x)] >= 0
697 && shadow[(x - piece_x)] < y
698 && ((c & 0x7f) == ' ')) {
699 c = (c & (~0x7f)) | '.'
700 | getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
704 mvaddch(y0+y*2, x0+x*2, c);
705 addch(c);
706 mvaddch(y0+y*2+1, x0+x*2, c);
707 addch(c);
710 if (gmsg_inputwin) {
711 delwin(gmsg_inputwin);
712 gmsg_inputwin = NULL;
713 draw_gmsg_input(NULL, -1);
715 if (!field_redraw)
716 screen_refresh();
719 /*************************************************************************/
721 /* Display another player's field. */
723 static void draw_other_field(int player)
725 int x, y, x0, y0;
726 Field *f;
728 if (dispmode != MODE_FIELDS)
729 return;
730 f = &fields[player-1];
731 if (player > my_playernum)
732 player--;
733 player--;
734 x0 = other_coord[player][0]+1;
735 y0 = other_coord[player][1];
736 for (y = 0; y < 22; y++) {
737 move(y0+y, x0);
738 for (x = 0; x < 12; x++) {
739 addch(tile_chars[(*f)[y][x]]);
742 if (gmsg_inputwin) {
743 delwin(gmsg_inputwin);
744 gmsg_inputwin = NULL;
745 draw_gmsg_input(NULL, -1);
747 if (!field_redraw)
748 screen_refresh();
751 /*************************************************************************/
753 /* Display the current game status (level, lines, next piece). */
755 static void draw_status(void)
757 int x, y, i, j;
758 char buf[32], shape[4][4];
760 x = wide_screen ? alt_status_coord[0] : status_coord[0];
761 y = wide_screen ? alt_status_coord[1] : status_coord[1];
762 sprintf(buf, "%d", lines>99999 ? 99999 : lines);
763 mvaddstr(y, x+7, buf);
764 sprintf(buf, "%d", levels[my_playernum]);
765 mvaddstr(y+1, x+7, buf);
766 x = wide_screen ? alt_next_coord[0] : next_coord[0];
767 y = wide_screen ? alt_next_coord[1] : next_coord[1];
768 if (get_shape(next_piece, 0, shape) == 0) {
769 for (j = 0; j < 4; j++) {
770 if (!wide_screen)
771 move(y+j, x);
772 for (i = 0; i < 4; i++) {
773 if (wide_screen) {
774 move(y+j*2, x+i*2);
775 addch(tile_chars[shape[j][i]]);
776 addch(tile_chars[shape[j][i]]);
777 move(y+j*2+1, x+i*2);
778 addch(tile_chars[shape[j][i]]);
779 addch(tile_chars[shape[j][i]]);
780 } else
781 addch(tile_chars[shape[j][i]]);
787 /*************************************************************************/
789 /* Display the special inventory and description of the current special. */
791 static const char *descs[] = {
792 " ",
793 "Add Line ",
794 "Clear Line ",
795 "Nuke Field ",
796 "Clear Random Blocks ",
797 "Switch Fields ",
798 "Clear Special Blocks",
799 "Block Gravity ",
800 "Blockquake ",
801 "Block Bomb "
804 static void draw_specials(void)
806 int x, y, i;
808 if (dispmode != MODE_FIELDS)
809 return;
810 x = own_coord[0];
811 y = own_coord[1]+45;
812 mvaddstr(y, x, descs[specials[0]+1]);
813 move(y+1, x+10);
814 i = 0;
815 while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
816 addch(tile_chars[specials[i]+6]);
817 i++;
818 x++;
820 while (x < attdef_coord[0]-1) {
821 addch(tile_chars[0]);
822 x++;
824 if (!field_redraw)
825 screen_refresh();
828 /*************************************************************************/
830 /* Display an attack/defense message. */
832 static const char *msgs[][2] = {
833 { "cs1", "1 Line Added to All" },
834 { "cs2", "2 Lines Added to All" },
835 { "cs4", "4 Lines Added to All" },
836 { "a", "Add Line" },
837 { "c", "Clear Line" },
838 { "n", "Nuke Field" },
839 { "r", "Clear Random Blocks" },
840 { "s", "Switch Fields" },
841 { "b", "Clear Special Blocks" },
842 { "g", "Block Gravity" },
843 { "q", "Blockquake" },
844 { "o", "Block Bomb" },
845 { NULL }
848 static void draw_attdef(const char *type, int from, int to)
850 int i, width;
851 char *s, buf[512];
853 width = other_coord[4][0] - attdef_coord[0] - 1;
854 for (i = 0; msgs[i][0]; i++) {
855 if (strcmp(type, msgs[i][0]) == 0)
856 break;
858 if (!msgs[i][0])
859 return;
860 strcpy(buf, msgs[i][1]);
861 if (to != 0)
862 sprintf(buf+strlen(buf), " on %s", players[to-1]);
863 if (from == 0)
864 sprintf(buf+strlen(buf), " by Server");
865 else
866 sprintf(buf+strlen(buf), " by %s", players[from-1]);
867 draw_text(BUFFER_ATTDEF, buf);
870 /*************************************************************************/
872 /* Display the in-game text window. */
874 static void draw_gmsg_input(const char *s, int pos)
876 static int start = 0; /* Start of displayed part of input line */
877 static const char *last_s;
878 static int last_pos;
880 if (s)
881 last_s = s;
882 else
883 s = last_s;
884 if (pos >= 0)
885 last_pos = pos;
886 else
887 pos = last_pos;
889 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
891 if (!gmsg_inputwin) {
892 gmsg_inputpos = scrheight/2 - 1;
893 gmsg_inputheight = 3;
894 gmsg_inputwin =
895 subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
896 werase(gmsg_inputwin);
897 leaveok(gmsg_inputwin, FALSE);
898 leaveok(stdscr, FALSE);
899 mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
902 if (strlen(s) < scrwidth-7) {
903 start = 0;
904 mvwaddstr(gmsg_inputwin, 1, 6, s);
905 wmove(gmsg_inputwin, 1, 6+strlen(s));
906 move(gmsg_inputpos+1, 6+strlen(s));
907 wclrtoeol(gmsg_inputwin);
908 wmove(gmsg_inputwin, 1, 6+pos);
909 move(gmsg_inputpos+1, 6+pos);
910 } else {
911 if (pos < start+8) {
912 start = pos-8;
913 if (start < 0)
914 start = 0;
915 } else if (pos > start + scrwidth-15) {
916 start = pos - (scrwidth-15);
917 if (start > strlen(s) - (scrwidth-7))
918 start = strlen(s) - (scrwidth-7);
920 mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
921 wmove(gmsg_inputwin, 1, 6 + (pos-start));
922 move(gmsg_inputpos+1, 6 + (pos-start));
924 screen_refresh();
927 /*************************************************************************/
929 /* Clear the in-game text window. */
931 static void clear_gmsg_input(void)
933 if (gmsg_inputwin) {
934 delwin(gmsg_inputwin);
935 gmsg_inputwin = NULL;
936 leaveok(stdscr, TRUE);
937 touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
938 setup_fields();
939 screen_refresh();
943 /*************************************************************************/
944 /*************************** Partyline display ***************************/
945 /*************************************************************************/
947 static void setup_partyline(void)
949 int i;
951 close_textwin(&gmsgbuf);
952 close_textwin(&attdefbuf);
953 clear();
955 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
957 plinebuf.x = plinebuf.y = 0;
958 plinebuf.width = scrwidth;
959 plinebuf.height = scrheight-4;
960 open_textwin(&plinebuf);
962 move(scrheight-4, 0);
963 hline(MY_HLINE, scrwidth);
964 move(scrheight-3, 0);
965 addstr("> ");
967 move(scrheight-2, 0);
968 hline(MY_HLINE2, scrwidth);
969 attrset(MY_BOLD);
970 move(scrheight-1, 0);
971 addstr("F1=Show Fields F2=Partyline F3=Winlist");
972 move(scrheight-1, scrwidth-8);
973 addstr("F10=Quit");
974 attrset(A_NORMAL);
976 move(scrheight-3, 2);
977 leaveok(stdscr, FALSE);
978 screen_refresh();
981 /*************************************************************************/
983 static void draw_partyline_input(const char *s, int pos)
985 static int start = 0; /* Start of displayed part of input line */
987 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
988 if (strlen(s) < scrwidth-3) {
989 start = 0;
990 mvaddstr(scrheight-3, 2, s);
991 move(scrheight-3, 2+strlen(s));
992 clrtoeol();
993 move(scrheight-3, 2+pos);
994 } else {
995 if (pos < start+8) {
996 start = pos-8;
997 if (start < 0)
998 start = 0;
999 } else if (pos > start + scrwidth-11) {
1000 start = pos - (scrwidth-11);
1001 if (start > strlen(s) - (scrwidth-3))
1002 start = strlen(s) - (scrwidth-3);
1004 mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
1005 move(scrheight-3, 2 + (pos-start));
1007 screen_refresh();
1010 /*************************************************************************/
1011 /**************************** Winlist display ****************************/
1012 /*************************************************************************/
1014 static void setup_winlist(void)
1016 int i, x;
1017 char buf[32];
1019 leaveok(stdscr, TRUE);
1020 close_textwin(&plinebuf);
1021 clear();
1022 attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
1024 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
1025 x = scrwidth/2 - strlen(winlist[i].name);
1026 if (x < 0)
1027 x = 0;
1028 if (winlist[i].team) {
1029 if (x < 4)
1030 x = 4;
1031 mvaddstr(i*2, x-4, "<T>");
1033 mvaddstr(i*2, x, winlist[i].name);
1034 snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
1035 if (winlist[i].games) {
1036 int avg100 = winlist[i].points*100 / winlist[i].games;
1037 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
1038 " %d.%02d",avg100/100, avg100%100);
1040 x += strlen(winlist[i].name) + 2;
1041 if (x > scrwidth - strlen(buf))
1042 x = scrwidth - strlen(buf);
1043 mvaddstr(i*2, x, buf);
1046 move(scrheight-2, 0);
1047 hline(MY_HLINE2, scrwidth);
1048 attrset(MY_BOLD);
1049 move(scrheight-1, 0);
1050 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1051 move(scrheight-1, scrwidth-8);
1052 addstr("F10=Quit");
1053 attrset(A_NORMAL);
1055 screen_refresh();
1058 /*************************************************************************/
1059 /************************** Interface declaration ************************/
1060 /*************************************************************************/
1062 Interface tty_interface = {
1064 wait_for_input,
1066 screen_setup,
1067 screen_refresh,
1068 screen_redraw,
1070 draw_text,
1071 clear_text,
1073 setup_fields,
1074 draw_own_field,
1075 draw_other_field,
1076 draw_status,
1077 draw_specials,
1078 draw_attdef,
1079 draw_gmsg_input,
1080 clear_gmsg_input,
1082 setup_partyline,
1083 draw_partyline_input,
1085 setup_winlist
1088 /*************************************************************************/