1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Text terminal I/O routines.
21 /*************************************************************************/
23 #define MY_HLINE (fancy ? ACS_HLINE : '-')
24 #define MY_VLINE (fancy ? ACS_VLINE : '|')
25 #define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
26 #define MY_URCORNER (fancy ? ACS_URCORNER : '+')
27 #define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
28 #define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
30 #define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
31 #define MY_BOLD (fancy ? A_BOLD : 0)
33 /*************************************************************************/
34 /******************************* Input stuff *****************************/
35 /*************************************************************************/
37 /* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
38 * waiting. Return -2 if we run out of time with no input.
41 static int wait_for_input(int msec
)
46 static int escape
= 0;
50 FD_SET(server_sock
, &fds
);
51 tv
.tv_sec
= msec
/1000;
52 tv
.tv_usec
= (msec
*1000) % 1000000;
53 while (select(server_sock
+1, &fds
, NULL
, NULL
, msec
<0 ? NULL
: &tv
) < 0) {
55 perror("Warning: select() failed");
57 if (FD_ISSET(0, &fds
)) {
59 if (!escape
&& c
== 27) { /* Escape */
61 c
= wait_for_input(1000);
70 else if (c
== KEY_DOWN
)
72 else if (c
== KEY_LEFT
)
74 else if (c
== KEY_RIGHT
)
76 else if (c
== KEY_F(1) || c
== ('1'|0x80) || (escape
&& c
== '1'))
78 else if (c
== KEY_F(2) || c
== ('2'|0x80) || (escape
&& c
== '2'))
80 else if (c
== KEY_F(3) || c
== ('3'|0x80) || (escape
&& c
== '3'))
82 else if (c
== KEY_F(4) || c
== ('4'|0x80) || (escape
&& c
== '4'))
84 else if (c
== KEY_F(5) || c
== ('5'|0x80) || (escape
&& c
== '5'))
86 else if (c
== KEY_F(6) || c
== ('6'|0x80) || (escape
&& c
== '6'))
88 else if (c
== KEY_F(7) || c
== ('7'|0x80) || (escape
&& c
== '7'))
90 else if (c
== KEY_F(8) || c
== ('8'|0x80) || (escape
&& c
== '8'))
92 else if (c
== KEY_F(9) || c
== ('9'|0x80) || (escape
&& c
== '9'))
94 else if (c
== KEY_F(10) || c
== ('0'|0x80) || (escape
&& c
== '0'))
96 else if (c
== KEY_F(11))
98 else if (c
== KEY_F(12))
100 else if (c
== KEY_BACKSPACE
)
102 else if (c
>= 0x0100)
104 else if (c
== 7) /* ^G */
105 return 27; /* Escape */
108 } /* if (FD_ISSET(0, &fds)) */
109 else if (FD_ISSET(server_sock
, &fds
))
112 return -2; /* out of time */
115 /*************************************************************************/
116 /****************************** Output stuff *****************************/
117 /*************************************************************************/
119 /* Size of the screen */
120 static int scrwidth
, scrheight
;
122 /* Is color available? */
123 static int has_color
;
125 /*************************************************************************/
130 int x
, y
, width
, height
;
132 WINDOW
*win
; /* NULL if not currently displayed */
136 static TextBuffer plinebuf
, gmsgbuf
, attdefbuf
;
138 /*************************************************************************/
140 /* Window for typing in-game text, and its coordinates: */
142 static WINDOW
*gmsg_inputwin
;
143 static int gmsg_inputpos
, gmsg_inputheight
;
145 /*************************************************************************/
146 /*************************************************************************/
148 /* Clean up the screen on exit. */
150 static void screen_cleanup()
152 wmove(stdscr
, scrheight
-1, 0);
158 /*************************************************************************/
160 /* Little signal handler that just does an exit(1) (thereby getting our
161 * cleanup routine called), except for TSTP, which does a clean suspend.
164 static void (*old_tstp
)(int sig
);
166 static void sighandler(int sig
)
168 if (sig
!= SIGTSTP
) {
170 psignal(sig
, "tetrinet");
174 signal(SIGTSTP
, old_tstp
);
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
)
193 if (colors
[0][0] < 0) {
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
)
211 return COLOR_PAIR(i
);
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(SIGTSTP
, SIG_IGN
);
231 nodelay(stdscr
, TRUE
);
232 keypad(stdscr
, TRUE
);
233 leaveok(stdscr
, TRUE
);
234 if ((has_color
= has_colors()))
236 getmaxyx(stdscr
, scrheight
, scrwidth
);
237 scrwidth
--; /* Don't draw in last column--this can cause scroll */
239 /* don't start with fewer lines */
244 fprintf(stderr
, "You need at least 50 lines to play tetrinet.\n");
248 /* Cancel all this when we exit. */
249 atexit(screen_cleanup
);
251 /* Catch signals so we can exit cleanly. */
252 signal(SIGINT
, sighandler
);
253 signal(SIGTERM
, sighandler
);
254 signal(SIGHUP
, sighandler
);
255 signal(SIGUSR1
, sighandler
);
256 signal(SIGUSR2
, sighandler
);
257 signal(SIGALRM
, sighandler
);
258 signal(SIGTSTP
, sighandler
);
260 signal(SIGXCPU
, sighandler
);
263 signal(SIGXFSZ
, sighandler
);
266 /* Broken pipes don't want to bother us at all. */
267 signal(SIGPIPE
, SIG_IGN
);
270 /*************************************************************************/
272 /* Redraw everything on the screen. */
274 static void screen_refresh(void)
277 touchline(stdscr
, gmsg_inputpos
, gmsg_inputheight
);
279 touchline(stdscr
, plinebuf
.y
, plinebuf
.height
);
281 touchline(stdscr
, gmsgbuf
.y
, gmsgbuf
.height
);
283 touchline(stdscr
, attdefbuf
.y
, attdefbuf
.height
);
284 wnoutrefresh(stdscr
);
288 /*************************************************************************/
290 /* Like screen_refresh(), but clear the screen first. */
292 static void screen_redraw(void)
294 clearok(stdscr
, TRUE
);
298 /*************************************************************************/
299 /************************* Text buffer routines **************************/
300 /*************************************************************************/
302 /* Put a line of text in a text buffer. */
304 static void outline(TextBuffer
*buf
, const char *s
)
306 if (buf
->line
== buf
->height
) {
309 memmove(buf
->text
, buf
->text
+1, (buf
->height
-1) * sizeof(char *));
313 int i
, x
= 0, l
= strlen(s
);
315 for (i
= 0; i
< l
; i
++) {
316 unsigned char c
= s
[i
] - 1;
319 static const int cmap
[8] = {
320 COLOR_BLACK
, COLOR_RED
, COLOR_GREEN
, COLOR_YELLOW
,
321 COLOR_BLUE
, COLOR_MAGENTA
, COLOR_CYAN
, COLOR_WHITE
,
326 wattrset(buf
->win
, A_NORMAL
);
329 wattron(buf
->win
, A_BOLD
);
332 wattron(buf
->win
, A_STANDOUT
);
334 case TATTR_UNDERLINE
:
335 wattron(buf
->win
, A_UNDERLINE
);
338 assert(c
< TATTR_CMAX
);
339 wattron(buf
->win
, getcolor(c
== 0 ? COLOR_WHITE
: cmap
[c
% 8], COLOR_BLACK
)
340 | (A_BOLD
* (c
/ 8)));
345 mvwaddch(buf
->win
, buf
->line
, x
++, c
+ 1);
349 wattrset(buf
->win
, A_NORMAL
);
351 if (s
!= (const char *) buf
->text
[buf
->line
]) /* check for restoring display */
352 buf
->text
[buf
->line
] = strdup(s
);
356 static void draw_text(int bufnum
, const char *s
)
358 char str
[1024]; /* hopefully scrwidth < 1024 */
365 case BUFFER_PLINE
: buf
= &plinebuf
; break;
366 case BUFFER_GMSG
: buf
= &gmsgbuf
; break;
367 case BUFFER_ATTDEF
: buf
= &attdefbuf
; break;
374 attrset(getcolor(COLOR_WHITE
, COLOR_BLACK
));
376 while (*s
&& isspace(*s
))
378 while (strlen(s
) > buf
->width
- indent
) {
379 t
= s
+ buf
->width
- indent
;
380 while (t
>= s
&& !isspace(*t
))
382 while (t
>= s
&& isspace(*t
))
386 t
= s
+ buf
->width
- indent
;
388 sprintf(str
, "%*s", indent
, "");
389 strncpy(str
+indent
, s
, t
-s
);
398 sprintf(str
, "%*s", indent
, "");
399 strcpy(str
+indent
, s
);
407 /*************************************************************************/
409 /* Clear the contents of a text buffer. */
411 static void clear_text(int bufnum
)
417 case BUFFER_PLINE
: buf
= &plinebuf
; break;
418 case BUFFER_GMSG
: buf
= &gmsgbuf
; break;
419 case BUFFER_ATTDEF
: buf
= &attdefbuf
; break;
423 for (i
= 0; i
< buf
->height
; i
++) {
437 /*************************************************************************/
439 /* Restore the contents of the given text buffer. */
441 static void restore_text(TextBuffer
*buf
)
444 while (buf
->line
< buf
->height
&& buf
->text
[buf
->line
])
445 outline(buf
, buf
->text
[buf
->line
]);
448 /*************************************************************************/
450 /* Open a window for the given text buffer. */
452 static void open_textwin(TextBuffer
*buf
)
454 if (buf
->height
<= 0 || buf
->width
<= 0) {
456 move(scrheight
-1, 0);
457 snprintf(str
, sizeof(str
), "ERROR: bad textwin size (%d,%d)",
458 buf
->width
, buf
->height
);
463 buf
->win
= subwin(stdscr
, buf
->height
, buf
->width
, buf
->y
, buf
->x
);
464 scrollok(buf
->win
, TRUE
);
467 buf
->text
= calloc(buf
->height
, sizeof(char *));
472 /*************************************************************************/
474 /* Close the window for the given text buffer, if it's open. */
476 static void close_textwin(TextBuffer
*buf
)
484 /*************************************************************************/
485 /************************ Field drawing routines *************************/
486 /*************************************************************************/
488 /* Are we on a wide screen (>=92 columns)? */
489 static int wide_screen
= 0;
491 /* Field display X/Y coordinates. */
492 static const int own_coord
[2] = {1,0};
493 static int other_coord
[5][2] = /* Recomputed based on screen width */
494 { {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
496 /* Position of the status window. */
497 static const int status_coord
[2] = {29,25};
498 static const int next_coord
[2] = {41,24};
499 static const int alt_status_coord
[2] = {29,2};
500 static const int alt_next_coord
[2] = {30,8};
502 /* Position of the attacks/defenses window. */
503 static const int attdef_coord
[2] = {28,28};
504 static const int alt_attdef_coord
[2] = {28,24};
506 /* Position of the text window. X coordinate is ignored. */
507 static const int field_text_coord
[2] = {0,47};
509 /* Information for drawing blocks. Color attributes are added to blocks in
510 * the setup_fields() routine. */
511 static int tile_chars
[15] =
512 { ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
514 /* Are we redrawing the entire display? */
515 static int field_redraw
= 0;
517 /*************************************************************************/
518 /*************************************************************************/
520 /* Set up the field display. */
522 static void draw_own_field(void);
523 static void draw_other_field(int player
);
524 static void draw_status(void);
525 static void draw_specials(void);
526 static void draw_gmsg_input(const char *s
, int pos
);
528 static void setup_fields(void)
530 int i
, j
, x
, y
, base
, delta
, attdefbot
;
533 if (!(tile_chars
[0] & A_ATTRIBUTES
)) {
534 for (i
= 1; i
< 15; i
++)
535 tile_chars
[i
] |= A_BOLD
;
536 tile_chars
[1] |= getcolor(COLOR_BLUE
, COLOR_BLACK
);
537 tile_chars
[2] |= getcolor(COLOR_YELLOW
, COLOR_BLACK
);
538 tile_chars
[3] |= getcolor(COLOR_GREEN
, COLOR_BLACK
);
539 tile_chars
[4] |= getcolor(COLOR_MAGENTA
, COLOR_BLACK
);
540 tile_chars
[5] |= getcolor(COLOR_RED
, COLOR_BLACK
);
544 leaveok(stdscr
, TRUE
);
545 close_textwin(&plinebuf
);
547 attrset(getcolor(COLOR_WHITE
,COLOR_BLACK
));
549 if (scrwidth
>= 92) {
555 delta
= (scrwidth
- base
) / 3;
556 base
+= 2 + (delta
- (FIELD_WIDTH
+5)) / 2;
557 other_coord
[0][0] = base
;
558 other_coord
[1][0] = base
+ delta
;
559 other_coord
[2][0] = base
+ delta
*2;
560 other_coord
[3][0] = base
+ delta
;
561 other_coord
[4][0] = base
+ delta
*2;
563 attdefbot
= field_text_coord
[1] - 1;
564 if (scrheight
- field_text_coord
[1] > 3) {
565 move(field_text_coord
[1], 0);
566 hline(MY_HLINE2
, scrwidth
);
568 if (scrheight
- field_text_coord
[1] > 5) {
569 move(scrheight
-2, 0);
570 hline(MY_HLINE2
, scrwidth
);
572 move(scrheight
-1, 0);
573 addstr("F1=Show Fields F2=Partyline F3=Winlist");
574 move(scrheight
-1, scrwidth
-8);
577 gmsgbuf
.y
= field_text_coord
[1]+1;
578 gmsgbuf
.height
= scrheight
- field_text_coord
[1] - 3;
580 gmsgbuf
.y
= field_text_coord
[1]+1;
581 gmsgbuf
.height
= scrheight
- field_text_coord
[1] - 1;
584 gmsgbuf
.y
= field_text_coord
[1];
585 gmsgbuf
.height
= scrheight
- field_text_coord
[1];
587 gmsgbuf
.x
= field_text_coord
[0];
588 gmsgbuf
.width
= scrwidth
;
589 open_textwin(&gmsgbuf
);
593 sprintf(buf
, "%d", my_playernum
);
594 mvaddnstr(y
, x
-1, buf
, sizeof(buf
));
595 for (i
= 2; i
< FIELD_HEIGHT
*2 && players
[my_playernum
-1][i
-2]; i
++)
596 mvaddch(y
+i
, x
-1, players
[my_playernum
-1][i
-2]);
597 if (teams
[my_playernum
-1] != '\0') {
598 mvaddstr(y
, x
+FIELD_WIDTH
*2+2, "T");
599 for (i
= 2; i
< FIELD_HEIGHT
*2 && teams
[my_playernum
-1][i
-2]; i
++)
600 mvaddch(y
+i
, x
+FIELD_WIDTH
*2+2, teams
[my_playernum
-1][i
-2]);
603 vline(MY_VLINE
, FIELD_HEIGHT
*2);
604 move(y
, x
+FIELD_WIDTH
*2+1);
605 vline(MY_VLINE
, FIELD_HEIGHT
*2);
606 move(y
+FIELD_HEIGHT
*2, x
);
608 hline(MY_HLINE
, FIELD_WIDTH
*2);
609 move(y
+FIELD_HEIGHT
*2, x
+FIELD_WIDTH
*2+1);
611 mvaddstr(y
+FIELD_HEIGHT
*2+2, x
, "Specials:");
615 for (j
= 0; j
< 5; j
++) {
616 x
= other_coord
[j
][0];
617 y
= other_coord
[j
][1];
619 vline(MY_VLINE
, FIELD_HEIGHT
);
620 move(y
, x
+FIELD_WIDTH
+1);
621 vline(MY_VLINE
, FIELD_HEIGHT
);
622 move(y
+FIELD_HEIGHT
, x
);
624 hline(MY_HLINE
, FIELD_WIDTH
);
625 move(y
+FIELD_HEIGHT
, x
+FIELD_WIDTH
+1);
627 if (j
+1 >= my_playernum
) {
628 sprintf(buf
, "%d", j
+2);
629 mvaddnstr(y
, x
-1, buf
, sizeof(buf
));
631 for (i
= 0; i
< FIELD_HEIGHT
-2 && players
[j
+1][i
]; i
++)
632 mvaddch(y
+i
+2, x
-1, players
[j
+1][i
]);
633 if (teams
[j
+1] != '\0') {
634 mvaddstr(y
, x
+FIELD_WIDTH
+2, "T");
635 for (i
= 0; i
< FIELD_HEIGHT
-2 && teams
[j
+1][i
]; i
++)
636 mvaddch(y
+i
+2, x
+FIELD_WIDTH
+2, teams
[j
+1][i
]);
639 draw_other_field(j
+2);
641 sprintf(buf
, "%d", j
+1);
642 mvaddnstr(y
, x
-1, buf
, sizeof(buf
));
644 for (i
= 0; i
< FIELD_HEIGHT
-2 && players
[j
][i
]; i
++)
645 mvaddch(y
+i
+2, x
-1, players
[j
][i
]);
646 if (teams
[j
] != '\0') {
647 mvaddstr(y
, x
+FIELD_WIDTH
+2, "T");
648 for (i
= 0; i
< FIELD_HEIGHT
-2 && teams
[j
][i
]; i
++)
649 mvaddch(y
+i
+2, x
+FIELD_WIDTH
+2, teams
[j
][i
]);
652 draw_other_field(j
+1);
657 x
= alt_status_coord
[0];
658 y
= alt_status_coord
[1];
659 mvaddstr(y
, x
, "Lines:");
660 mvaddstr(y
+1, x
, "Level:");
661 x
= alt_next_coord
[0];
662 y
= alt_next_coord
[1];
663 mvaddstr(y
-2, x
-1, "Next piece:");
667 mvaddch(y
-1, x
+8, MY_URCORNER
);
675 mvaddch(y
+8, x
+8, MY_LRCORNER
);
679 mvaddstr(y
-1, x
, "Next piece:");
680 mvaddstr(y
, x
, "Lines:");
681 mvaddstr(y
+1, x
, "Level:");
686 attdefbuf
.x
= wide_screen
? alt_attdef_coord
[0] : attdef_coord
[0];
687 attdefbuf
.y
= wide_screen
? alt_attdef_coord
[1] : attdef_coord
[1];
688 attdefbuf
.width
= (other_coord
[3][0]-1) - attdefbuf
.x
;
689 attdefbuf
.height
= (attdefbot
+1) - attdefbuf
.y
;
690 open_textwin(&attdefbuf
);
693 delwin(gmsg_inputwin
);
694 gmsg_inputwin
= NULL
;
695 draw_gmsg_input(NULL
, -1);
703 /*************************************************************************/
705 /* Display the player's own field. */
707 static void draw_own_field(void)
710 Field
*f
= &fields
[my_playernum
-1];
711 int shadow
[4] = { -1, -1, -1, -1 };
713 if (dispmode
!= MODE_FIELDS
)
716 /* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
717 if (playing_game
&& cast_shadow
) {
718 int y
= current_y
- piecedata
[current_piece
][current_rotation
].hot_y
;
719 char *shape
= (char *) piecedata
[current_piece
][current_rotation
].shape
;
722 for (j
= 0; j
< 4; j
++) {
727 for (i
= 0; i
< 4; i
++) {
736 for (y
= 0; y
< 22; y
++) {
737 for (x
= 0; x
< 12; x
++) {
738 int c
= tile_chars
[(int) (*f
)[y
][x
]];
740 if (playing_game
&& cast_shadow
) {
741 PieceData
*piece
= &piecedata
[current_piece
][current_rotation
];
742 int piece_x
= current_x
- piece
->hot_x
;
744 if (x
>= piece_x
&& x
<= piece_x
+ 3
745 && shadow
[(x
- piece_x
)] >= 0
746 && shadow
[(x
- piece_x
)] < y
747 && ((c
& 0x7f) == ' ')) {
748 c
= (c
& (~0x7f)) | '.'
749 | getcolor(COLOR_BLACK
, COLOR_BLACK
) | A_BOLD
;
753 mvaddch(y0
+y
*2, x0
+x
*2, c
);
755 mvaddch(y0
+y
*2+1, x0
+x
*2, c
);
760 delwin(gmsg_inputwin
);
761 gmsg_inputwin
= NULL
;
762 draw_gmsg_input(NULL
, -1);
770 /*************************************************************************/
772 /* Display another player's field. */
774 static void draw_other_field(int player
)
779 if (dispmode
!= MODE_FIELDS
)
781 f
= &fields
[player
-1];
782 if (player
> my_playernum
)
785 x0
= other_coord
[player
][0]+1;
786 y0
= other_coord
[player
][1];
787 for (y
= 0; y
< 22; y
++) {
789 for (x
= 0; x
< 12; x
++) {
790 addch(tile_chars
[(int) (*f
)[y
][x
]]);
794 delwin(gmsg_inputwin
);
795 gmsg_inputwin
= NULL
;
796 draw_gmsg_input(NULL
, -1);
804 /*************************************************************************/
806 /* Display the current game status (level, lines, next piece). */
808 static void draw_status(void)
811 char buf
[32], shape
[4][4];
813 x
= wide_screen
? alt_status_coord
[0] : status_coord
[0];
814 y
= wide_screen
? alt_status_coord
[1] : status_coord
[1];
815 sprintf(buf
, "%d", lines
>99999 ? 99999 : lines
);
816 mvaddnstr(y
, x
+7, buf
, sizeof(buf
));
817 sprintf(buf
, "%d", levels
[my_playernum
]);
818 mvaddnstr(y
+1, x
+7, buf
, sizeof(buf
));
819 x
= wide_screen
? alt_next_coord
[0] : next_coord
[0];
820 y
= wide_screen
? alt_next_coord
[1] : next_coord
[1];
821 if (get_shape(next_piece
, 0, shape
) == 0) {
822 for (j
= 0; j
< 4; j
++) {
825 for (i
= 0; i
< 4; i
++) {
828 addch(tile_chars
[(int) shape
[j
][i
]]);
829 addch(tile_chars
[(int) shape
[j
][i
]]);
830 move(y
+j
*2+1, x
+i
*2);
831 addch(tile_chars
[(int) shape
[j
][i
]]);
832 addch(tile_chars
[(int) shape
[j
][i
]]);
834 addch(tile_chars
[(int) shape
[j
][i
]]);
840 /*************************************************************************/
842 /* Display the special inventory and description of the current special. */
844 static const char *descs
[] = {
849 "Clear Random Blocks ",
851 "Clear Special Blocks",
857 static void draw_specials(void)
861 if (dispmode
!= MODE_FIELDS
)
865 mvaddnstr(y
, x
, descs
[specials
[0]+1], sizeof(descs
[specials
[0]+1]));
868 while (i
< special_capacity
&& specials
[i
] >= 0 && x
< attdef_coord
[0]-1) {
869 addch(tile_chars
[specials
[i
]+6]);
873 while (x
< attdef_coord
[0]-1) {
874 addch(tile_chars
[0]);
883 /*************************************************************************/
885 /* Display an attack/defense message. */
887 static const char *msgs
[][2] = {
888 { "cs1", "1 Line Added to All" },
889 { "cs2", "2 Lines Added to All" },
890 { "cs4", "4 Lines Added to All" },
892 { "c", "Clear Line" },
893 { "n", "Nuke Field" },
894 { "r", "Clear Random Blocks" },
895 { "s", "Switch Fields" },
896 { "b", "Clear Special Blocks" },
897 { "g", "Block Gravity" },
898 { "q", "Blockquake" },
899 { "o", "Block Bomb" },
903 static void draw_attdef(const char *type
, int from
, int to
)
908 width
= other_coord
[4][0] - attdef_coord
[0] - 1;
909 for (i
= 0; msgs
[i
][0]; i
++) {
910 if (strcmp(type
, msgs
[i
][0]) == 0)
915 strcpy(buf
, msgs
[i
][1]);
917 sprintf(buf
+strlen(buf
), " on %s", players
[to
-1]);
919 sprintf(buf
+strlen(buf
), " by Server");
921 sprintf(buf
+strlen(buf
), " by %s", players
[from
-1]);
922 draw_text(BUFFER_ATTDEF
, buf
);
925 /*************************************************************************/
927 /* Display the in-game text window. */
929 static void draw_gmsg_input(const char *s
, int pos
)
931 static int start
= 0; /* Start of displayed part of input line */
932 static const char *last_s
;
944 attrset(getcolor(COLOR_WHITE
,COLOR_BLACK
));
946 if (!gmsg_inputwin
) {
947 gmsg_inputpos
= scrheight
/2 - 1;
948 gmsg_inputheight
= 3;
950 subwin(stdscr
, gmsg_inputheight
, scrwidth
, gmsg_inputpos
, 0);
951 werase(gmsg_inputwin
);
952 leaveok(gmsg_inputwin
, FALSE
);
953 leaveok(stdscr
, FALSE
);
954 mvwaddstr(gmsg_inputwin
, 1, 0, "Text>");
957 if (strlen(s
) < scrwidth
-7) {
959 mvwaddstr(gmsg_inputwin
, 1, 6, s
);
960 wmove(gmsg_inputwin
, 1, 6+strlen(s
));
961 move(gmsg_inputpos
+1, 6+strlen(s
));
962 wclrtoeol(gmsg_inputwin
);
963 wmove(gmsg_inputwin
, 1, 6+pos
);
964 move(gmsg_inputpos
+1, 6+pos
);
970 } else if (pos
> start
+ scrwidth
-15) {
971 start
= pos
- (scrwidth
-15);
972 if (start
> strlen(s
) - (scrwidth
-7))
973 start
= strlen(s
) - (scrwidth
-7);
975 mvwaddnstr(gmsg_inputwin
, 1, 6, s
+start
, scrwidth
-6);
976 wmove(gmsg_inputwin
, 1, 6 + (pos
-start
));
977 move(gmsg_inputpos
+1, 6 + (pos
-start
));
983 /*************************************************************************/
985 /* Clear the in-game text window. */
987 static void clear_gmsg_input(void)
990 delwin(gmsg_inputwin
);
991 gmsg_inputwin
= NULL
;
992 leaveok(stdscr
, TRUE
);
993 touchline(stdscr
, gmsg_inputpos
, gmsg_inputheight
);
1000 /*************************************************************************/
1001 /*************************** Partyline display ***************************/
1002 /*************************************************************************/
1004 static void setup_partyline(void)
1006 close_textwin(&gmsgbuf
);
1007 close_textwin(&attdefbuf
);
1010 attrset(getcolor(COLOR_WHITE
,COLOR_BLACK
));
1012 plinebuf
.x
= plinebuf
.y
= 0;
1013 plinebuf
.width
= scrwidth
;
1014 plinebuf
.height
= scrheight
-4;
1015 open_textwin(&plinebuf
);
1017 move(scrheight
-4, 0);
1018 hline(MY_HLINE
, scrwidth
);
1019 move(scrheight
-3, 0);
1022 move(scrheight
-2, 0);
1023 hline(MY_HLINE2
, scrwidth
);
1025 move(scrheight
-1, 0);
1026 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1027 move(scrheight
-1, scrwidth
-8);
1031 move(scrheight
-3, 2);
1032 leaveok(stdscr
, FALSE
);
1037 /*************************************************************************/
1039 static void draw_partyline_input(const char *s
, int pos
)
1041 static int start
= 0; /* Start of displayed part of input line */
1043 attrset(getcolor(COLOR_WHITE
,COLOR_BLACK
));
1044 if (strlen(s
) < scrwidth
-3) {
1046 mvaddnstr(scrheight
-3, 2, s
, sizeof(2));
1047 move(scrheight
-3, 2+strlen(s
));
1049 move(scrheight
-3, 2+pos
);
1051 if (pos
< start
+8) {
1055 } else if (pos
> start
+ scrwidth
-11) {
1056 start
= pos
- (scrwidth
-11);
1057 if (start
> strlen(s
) - (scrwidth
-3))
1058 start
= strlen(s
) - (scrwidth
-3);
1060 mvaddnstr(scrheight
-3, 2, s
+start
, scrwidth
-2);
1061 move(scrheight
-3, 2 + (pos
-start
));
1066 /*************************************************************************/
1067 /**************************** Winlist display ****************************/
1068 /*************************************************************************/
1070 static void setup_winlist(void)
1075 leaveok(stdscr
, TRUE
);
1076 close_textwin(&plinebuf
);
1078 attrset(getcolor(COLOR_WHITE
,COLOR_BLACK
));
1080 for (i
= 0; i
< MAXWINLIST
&& *winlist
[i
].name
; i
++) {
1081 x
= scrwidth
/2 - strlen(winlist
[i
].name
);
1084 if (winlist
[i
].team
) {
1087 mvaddstr(i
*2, x
-4, "<T>");
1089 mvaddnstr(i
*2, x
, winlist
[i
].name
, sizeof(winlist
[i
].name
));
1090 snprintf(buf
, sizeof(buf
), "%4d", winlist
[i
].points
);
1091 if (winlist
[i
].games
) {
1092 int avg100
= winlist
[i
].points
*100 / winlist
[i
].games
;
1093 snprintf(buf
+strlen(buf
), sizeof(buf
)-strlen(buf
),
1094 " %d.%02d",avg100
/100, avg100
%100);
1096 x
+= strlen(winlist
[i
].name
) + 2;
1097 if (x
> scrwidth
- strlen(buf
))
1098 x
= scrwidth
- strlen(buf
);
1099 mvaddnstr(i
*2, x
, buf
, sizeof(buf
));
1102 move(scrheight
-2, 0);
1103 hline(MY_HLINE2
, scrwidth
);
1105 move(scrheight
-1, 0);
1106 addstr("F1=Show Fields F2=Partyline F3=Winlist");
1107 move(scrheight
-1, scrwidth
-8);
1115 /*************************************************************************/
1116 /************************** Interface declaration ************************/
1117 /*************************************************************************/
1119 Interface tty_interface
= {
1140 draw_partyline_input
,
1145 /*************************************************************************/