tetrinet/tty.c

1101 lines
28 KiB
C

/* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
* This program is public domain.
*
* Text terminal I/O routines.
*/
#define _GNU_SOURCE /* strsignal() - FIXME!!! --pasky */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <curses.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include "tetrinet.h"
#include "tetris.h"
#include "io.h"
/*************************************************************************/
#define MY_HLINE (fancy ? ACS_HLINE : '-')
#define MY_VLINE (fancy ? ACS_VLINE : '|')
#define MY_ULCORNER (fancy ? ACS_ULCORNER : '+')
#define MY_URCORNER (fancy ? ACS_URCORNER : '+')
#define MY_LLCORNER (fancy ? ACS_LLCORNER : '+')
#define MY_LRCORNER (fancy ? ACS_LRCORNER : '+')
#define MY_HLINE2 (fancy ? (ACS_HLINE | A_BOLD) : '=')
#define MY_BOLD (fancy ? A_BOLD : 0)
/*************************************************************************/
/******************************* Input stuff *****************************/
/*************************************************************************/
/* Return either an ASCII code 0-255, a K_* value, or -1 if server input is
* waiting. Return -2 if we run out of time with no input.
*/
extern PieceData piecedata[7][4];
static int wait_for_input(int msec)
{
fd_set fds;
struct timeval tv;
int c;
static int escape = 0;
FD_ZERO(&fds);
FD_SET(0, &fds);
FD_SET(server_sock, &fds);
tv.tv_sec = msec/1000;
tv.tv_usec = (msec*1000) % 1000000;
while (select(server_sock+1, &fds, NULL, NULL, msec<0 ? NULL : &tv) < 0) {
if (errno != EINTR)
perror("Warning: select() failed");
}
if (FD_ISSET(0, &fds)) {
c = getch();
if (!escape && c == 27) { /* Escape */
escape = 1;
c = wait_for_input(1000);
escape = 0;
if (c < 0)
return 27;
else
return c;
}
if (c == KEY_UP)
return K_UP;
else if (c == KEY_DOWN)
return K_DOWN;
else if (c == KEY_LEFT)
return K_LEFT;
else if (c == KEY_RIGHT)
return K_RIGHT;
else if (c == KEY_F(1) || c == ('1'|0x80) || (escape && c == '1'))
return K_F1;
else if (c == KEY_F(2) || c == ('2'|0x80) || (escape && c == '2'))
return K_F2;
else if (c == KEY_F(3) || c == ('3'|0x80) || (escape && c == '3'))
return K_F3;
else if (c == KEY_F(4) || c == ('4'|0x80) || (escape && c == '4'))
return K_F4;
else if (c == KEY_F(5) || c == ('5'|0x80) || (escape && c == '5'))
return K_F5;
else if (c == KEY_F(6) || c == ('6'|0x80) || (escape && c == '6'))
return K_F6;
else if (c == KEY_F(7) || c == ('7'|0x80) || (escape && c == '7'))
return K_F7;
else if (c == KEY_F(8) || c == ('8'|0x80) || (escape && c == '8'))
return K_F8;
else if (c == KEY_F(9) || c == ('9'|0x80) || (escape && c == '9'))
return K_F9;
else if (c == KEY_F(10) || c == ('0'|0x80) || (escape && c == '0'))
return K_F10;
else if (c == KEY_F(11))
return K_F11;
else if (c == KEY_F(12))
return K_F12;
else if (c == KEY_BACKSPACE)
return 8;
else if (c >= 0x0100)
return K_INVALID;
else if (c == 7) /* ^G */
return 27; /* Escape */
else
return c;
} /* if (FD_ISSET(0, &fds)) */
else if (FD_ISSET(server_sock, &fds))
return -1;
else
return -2; /* out of time */
}
/*************************************************************************/
/****************************** Output stuff *****************************/
/*************************************************************************/
/* Size of the screen */
static int scrwidth, scrheight;
/* Is color available? */
static int has_color;
/*************************************************************************/
/* Text buffers: */
typedef struct {
int x, y, width, height;
int line;
WINDOW *win; /* NULL if not currently displayed */
char **text;
} TextBuffer;
static TextBuffer plinebuf, gmsgbuf, attdefbuf;
/*************************************************************************/
/* Window for typing in-game text, and its coordinates: */
static WINDOW *gmsg_inputwin;
static int gmsg_inputpos, gmsg_inputheight;
/*************************************************************************/
/*************************************************************************/
/* Clean up the screen on exit. */
static void screen_cleanup()
{
wmove(stdscr, scrheight-1, 0);
wrefresh(stdscr);
endwin();
printf("\n");
}
/*************************************************************************/
/* Little signal handler that just does an exit(1) (thereby getting our
* cleanup routine called), except for TSTP, which does a clean suspend.
*/
static void (*old_tstp)(int sig);
static void sighandler(int sig)
{
if (sig != SIGTSTP) {
endwin();
if (sig != SIGINT)
fprintf(stderr, "%s\n", strsignal(sig));
exit(1);
}
endwin();
signal(SIGTSTP, old_tstp);
raise(SIGTSTP);
doupdate();
signal(SIGTSTP, sighandler);
}
/*************************************************************************/
/*************************************************************************/
#define MAXCOLORS 256
static int colors[MAXCOLORS][2] = { {-1,-1} };
/* Return a color attribute value. */
static long getcolor(int fg, int bg)
{
int i;
if (colors[0][0] < 0) {
start_color();
memset(colors, -1, sizeof(colors));
colors[0][0] = COLOR_WHITE;
colors[0][1] = COLOR_BLACK;
}
if (fg == COLOR_WHITE && bg == COLOR_BLACK)
return COLOR_PAIR(0);
for (i = 1; i < MAXCOLORS; i++) {
if (colors[i][0] == fg && colors[i][1] == bg)
return COLOR_PAIR(i);
}
for (i = 1; i < MAXCOLORS; i++) {
if (colors[i][0] < 0) {
if (init_pair(i, fg, bg) == ERR)
continue;
colors[i][0] = fg;
colors[i][1] = bg;
return COLOR_PAIR(i);
}
}
return -1;
}
/*************************************************************************/
/*************************************************************************/
/* Set up the screen stuff. */
static void screen_setup(void)
{
/* Avoid messy keyfield signals while we're setting up */
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
initscr();
cbreak();
noecho();
nodelay(stdscr, TRUE);
keypad(stdscr, TRUE);
leaveok(stdscr, TRUE);
if ((has_color = has_colors()))
start_color();
getmaxyx(stdscr, scrheight, scrwidth);
scrwidth--; /* Don't draw in last column--this can cause scroll */
/* Cancel all this when we exit. */
atexit(screen_cleanup);
/* Catch signals so we can exit cleanly. */
signal(SIGINT, sighandler);
signal(SIGQUIT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGHUP, sighandler);
signal(SIGSEGV, sighandler);
signal(SIGABRT, sighandler);
signal(SIGIOT, sighandler);
signal(SIGTRAP, sighandler);
signal(SIGBUS, sighandler);
signal(SIGFPE, sighandler);
signal(SIGUSR1, sighandler);
signal(SIGUSR2, sighandler);
signal(SIGALRM, sighandler);
#ifdef SIGSTKFLT
signal(SIGSTKFLT, sighandler);
#endif
signal(SIGTSTP, sighandler);
signal(SIGXCPU, sighandler);
signal(SIGXFSZ, sighandler);
signal(SIGVTALRM, sighandler);
/* Broken pipes don't want to bother us at all. */
signal(SIGPIPE, SIG_IGN);
}
/*************************************************************************/
/* Redraw everything on the screen. */
static void screen_refresh(void)
{
if (gmsg_inputwin)
touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
if (plinebuf.win)
touchline(stdscr, plinebuf.y, plinebuf.height);
if (gmsgbuf.win)
touchline(stdscr, gmsgbuf.y, gmsgbuf.height);
if (attdefbuf.win)
touchline(stdscr, attdefbuf.y, attdefbuf.height);
wnoutrefresh(stdscr);
doupdate();
}
/*************************************************************************/
/* Like screen_refresh(), but clear the screen first. */
static void screen_redraw(void)
{
clearok(stdscr, TRUE);
screen_refresh();
}
/*************************************************************************/
/************************* Text buffer routines **************************/
/*************************************************************************/
/* Put a line of text in a text buffer. */
static void outline(TextBuffer *buf, const char *s)
{
if (buf->line == buf->height) {
if (buf->win)
scroll(buf->win);
memmove(buf->text, buf->text+1, (buf->height-1) * sizeof(char *));
buf->line--;
}
if (buf->win)
mvwaddstr(buf->win, buf->line, 0, s);
if (s != buf->text[buf->line]) /* check for restoring display */
buf->text[buf->line] = strdup(s);
buf->line++;
}
static void draw_text(int bufnum, const char *s)
{
char str[1024]; /* hopefully scrwidth < 1024 */
const char *t;
int indent = 0;
int x = 0, y = 0;
TextBuffer *buf;
switch (bufnum) {
case BUFFER_PLINE: buf = &plinebuf; break;
case BUFFER_GMSG: buf = &gmsgbuf; break;
case BUFFER_ATTDEF: buf = &attdefbuf; break;
default: return;
}
if (!buf->text)
return;
if (buf->win) {
getyx(stdscr, y, x);
attrset(getcolor(COLOR_WHITE, COLOR_BLACK));
}
while (*s && isspace(*s))
s++;
while (strlen(s) > buf->width - indent) {
t = s + buf->width - indent;
while (t >= s && !isspace(*t))
t--;
while (t >= s && isspace(*t))
t--;
t++;
if (t < s)
t = s + buf->width - indent;
if (indent > 0)
sprintf(str, "%*s", indent, "");
strncpy(str+indent, s, t-s);
str[t-s+indent] = 0;
outline(buf, str);
indent = 2;
while (isspace(*t))
t++;
s = t;
}
if (indent > 0)
sprintf(str, "%*s", indent, "");
strcpy(str+indent, s);
outline(buf, str);
if (buf->win) {
move(y, x);
screen_refresh();
}
}
/*************************************************************************/
/* Clear the contents of a text buffer. */
static void clear_text(int bufnum)
{
TextBuffer *buf;
int i;
switch (bufnum) {
case BUFFER_PLINE: buf = &plinebuf; break;
case BUFFER_GMSG: buf = &gmsgbuf; break;
case BUFFER_ATTDEF: buf = &attdefbuf; break;
default: return;
}
if (buf->text) {
for (i = 0; i < buf->height; i++) {
if (buf->text[i]) {
free(buf->text[i]);
buf->text[i] = NULL;
}
}
buf->line = 0;
}
if (buf->win) {
werase(buf->win);
screen_refresh();
}
}
/*************************************************************************/
/* Restore the contents of the given text buffer. */
static void restore_text(TextBuffer *buf)
{
buf->line = 0;
while (buf->line < buf->height && buf->text[buf->line])
outline(buf, buf->text[buf->line]);
}
/*************************************************************************/
/* Open a window for the given text buffer. */
static void open_textwin(TextBuffer *buf)
{
if (buf->height <= 0 || buf->width <= 0) {
char str[256];
move(scrheight-1, 0);
snprintf(str, sizeof(str), "ERROR: bad textwin size (%d,%d)",
buf->width, buf->height);
addstr(str);
exit(1);
}
if (!buf->win) {
buf->win = subwin(stdscr, buf->height, buf->width, buf->y, buf->x);
scrollok(buf->win, TRUE);
}
if (!buf->text)
buf->text = calloc(buf->height, sizeof(char *));
else
restore_text(buf);
}
/*************************************************************************/
/* Close the window for the given text buffer, if it's open. */
static void close_textwin(TextBuffer *buf)
{
if (buf->win) {
delwin(buf->win);
buf->win = NULL;
}
}
/*************************************************************************/
/************************ Field drawing routines *************************/
/*************************************************************************/
/* Are we on a wide screen (>=92 columns)? */
static int wide_screen = 0;
/* Field display X/Y coordinates. */
static const int own_coord[2] = {1,0};
static int other_coord[5][2] = /* Recomputed based on screen width */
{ {30,0}, {47,0}, {64,0}, {47,24}, {64,24} };
/* Position of the status window. */
static const int status_coord[2] = {29,25};
static const int next_coord[2] = {41,24};
static const int alt_status_coord[2] = {29,2};
static const int alt_next_coord[2] = {30,8};
/* Position of the attacks/defenses window. */
static const int attdef_coord[2] = {28,28};
static const int alt_attdef_coord[2] = {28,24};
/* Position of the text window. X coordinate is ignored. */
static const int field_text_coord[2] = {0,47};
/* Information for drawing blocks. Color attributes are added to blocks in
* the setup_fields() routine. */
static int tile_chars[15] =
{ ' ','#','#','#','#','#','a','c','n','r','s','b','g','q','o' };
/* Are we redrawing the entire display? */
static int field_redraw = 0;
/*************************************************************************/
/*************************************************************************/
/* Set up the field display. */
static void draw_own_field(void);
static void draw_other_field(int player);
static void draw_status(void);
static void draw_specials(void);
static void draw_gmsg_input(const char *s, int pos);
static void setup_fields(void)
{
int i, j, x, y, base, delta, attdefbot;
char buf[32];
if (!(tile_chars[0] & A_ATTRIBUTES)) {
for (i = 1; i < 15; i++)
tile_chars[i] |= A_BOLD;
tile_chars[1] |= getcolor(COLOR_BLUE, COLOR_BLACK);
tile_chars[2] |= getcolor(COLOR_YELLOW, COLOR_BLACK);
tile_chars[3] |= getcolor(COLOR_GREEN, COLOR_BLACK);
tile_chars[4] |= getcolor(COLOR_MAGENTA, COLOR_BLACK);
tile_chars[5] |= getcolor(COLOR_RED, COLOR_BLACK);
}
field_redraw = 1;
leaveok(stdscr, TRUE);
close_textwin(&plinebuf);
clear();
attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
if (scrwidth >= 92) {
wide_screen = 1;
base = 41;
} else {
base = 28;
}
delta = (scrwidth - base) / 3;
base += 2 + (delta - (FIELD_WIDTH+5)) / 2;
other_coord[0][0] = base;
other_coord[1][0] = base + delta;
other_coord[2][0] = base + delta*2;
other_coord[3][0] = base + delta;
other_coord[4][0] = base + delta*2;
attdefbot = field_text_coord[1] - 1;
if (scrheight - field_text_coord[1] > 3) {
move(field_text_coord[1], 0);
hline(MY_HLINE2, scrwidth);
attdefbot--;
if (scrheight - field_text_coord[1] > 5) {
move(scrheight-2, 0);
hline(MY_HLINE2, scrwidth);
attrset(MY_BOLD);
move(scrheight-1, 0);
addstr("F1=Show Fields F2=Partyline F3=Winlist");
move(scrheight-1, scrwidth-8);
addstr("F10=Quit");
attrset(A_NORMAL);
gmsgbuf.y = field_text_coord[1]+1;
gmsgbuf.height = scrheight - field_text_coord[1] - 3;
} else {
gmsgbuf.y = field_text_coord[1]+1;
gmsgbuf.height = scrheight - field_text_coord[1] - 1;
}
} else {
gmsgbuf.y = field_text_coord[1];
gmsgbuf.height = scrheight - field_text_coord[1];
}
gmsgbuf.x = field_text_coord[0];
gmsgbuf.width = scrwidth;
open_textwin(&gmsgbuf);
x = own_coord[0];
y = own_coord[1];
sprintf(buf, "%d", my_playernum);
mvaddstr(y, x-1, buf);
for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
mvaddch(y+i, x-1, players[my_playernum-1][i-2]);
if (teams[my_playernum-1] != '\0') {
mvaddstr(y, x+FIELD_WIDTH*2+2, "T");
for (i = 2; i < FIELD_HEIGHT*2 && teams[my_playernum-1][i-2]; i++)
mvaddch(y+i, x+FIELD_WIDTH*2+2, teams[my_playernum-1][i-2]);
}
move(y, x);
vline(MY_VLINE, FIELD_HEIGHT*2);
move(y, x+FIELD_WIDTH*2+1);
vline(MY_VLINE, FIELD_HEIGHT*2);
move(y+FIELD_HEIGHT*2, x);
addch(MY_LLCORNER);
hline(MY_HLINE, FIELD_WIDTH*2);
move(y+FIELD_HEIGHT*2, x+FIELD_WIDTH*2+1);
addch(MY_LRCORNER);
mvaddstr(y+FIELD_HEIGHT*2+2, x, "Specials:");
draw_own_field();
draw_specials();
for (j = 0; j < 5; j++) {
x = other_coord[j][0];
y = other_coord[j][1];
move(y, x);
vline(MY_VLINE, FIELD_HEIGHT);
move(y, x+FIELD_WIDTH+1);
vline(MY_VLINE, FIELD_HEIGHT);
move(y+FIELD_HEIGHT, x);
addch(MY_LLCORNER);
hline(MY_HLINE, FIELD_WIDTH);
move(y+FIELD_HEIGHT, x+FIELD_WIDTH+1);
addch(MY_LRCORNER);
if (j+1 >= my_playernum) {
sprintf(buf, "%d", j+2);
mvaddstr(y, x-1, buf);
if (players[j+1]) {
for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
mvaddch(y+i+2, x-1, players[j+1][i]);
if (teams[j+1] != '\0') {
mvaddstr(y, x+FIELD_WIDTH+2, "T");
for (i = 0; i < FIELD_HEIGHT-2 && teams[j+1][i]; i++)
mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j+1][i]);
}
}
draw_other_field(j+2);
} else {
sprintf(buf, "%d", j+1);
mvaddstr(y, x-1, buf);
if (players[j]) {
for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
mvaddch(y+i+2, x-1, players[j][i]);
if (teams[j] != '\0') {
mvaddstr(y, x+FIELD_WIDTH+2, "T");
for (i = 0; i < FIELD_HEIGHT-2 && teams[j][i]; i++)
mvaddch(y+i+2, x+FIELD_WIDTH+2, teams[j][i]);
}
}
draw_other_field(j+1);
}
}
if (wide_screen) {
x = alt_status_coord[0];
y = alt_status_coord[1];
mvaddstr(y, x, "Lines:");
mvaddstr(y+1, x, "Level:");
x = alt_next_coord[0];
y = alt_next_coord[1];
mvaddstr(y-2, x-1, "Next piece:");
move(y-1, x-1);
addch(MY_ULCORNER);
hline(MY_HLINE, 8);
mvaddch(y-1, x+8, MY_URCORNER);
move(y, x-1);
vline(MY_VLINE, 8);
move(y, x+8);
vline(MY_VLINE, 8);
move(y+8, x-1);
addch(MY_LLCORNER);
hline(MY_HLINE, 8);
mvaddch(y+8, x+8, MY_LRCORNER);
} else {
x = status_coord[0];
y = status_coord[1];
mvaddstr(y-1, x, "Next piece:");
mvaddstr(y, x, "Lines:");
mvaddstr(y+1, x, "Level:");
}
if (playing_game)
draw_status();
attdefbuf.x = wide_screen ? alt_attdef_coord[0] : attdef_coord[0];
attdefbuf.y = wide_screen ? alt_attdef_coord[1] : attdef_coord[1];
attdefbuf.width = (other_coord[3][0]-1) - attdefbuf.x;
attdefbuf.height = (attdefbot+1) - attdefbuf.y;
open_textwin(&attdefbuf);
if (gmsg_inputwin) {
delwin(gmsg_inputwin);
gmsg_inputwin = NULL;
draw_gmsg_input(NULL, -1);
}
screen_refresh();
field_redraw = 0;
}
/*************************************************************************/
/* Display the player's own field. */
static void draw_own_field(void)
{
int x, y, x0, y0;
Field *f = &fields[my_playernum-1];
int shadow[4] = { -1, -1, -1, -1 };
if (dispmode != MODE_FIELDS)
return;
/* XXX: Code duplication with tetris.c:draw_piece(). --pasky */
if (playing_game && cast_shadow) {
int y = current_y - piecedata[current_piece][current_rotation].hot_y;
char *shape = (char *) piecedata[current_piece][current_rotation].shape;
int i, j;
for (j = 0; j < 4; j++) {
if (y+j < 0) {
shape += 4;
continue;
}
for (i = 0; i < 4; i++) {
if (*shape++)
shadow[i] = y + j;
}
}
}
x0 = own_coord[0]+1;
y0 = own_coord[1];
for (y = 0; y < 22; y++) {
for (x = 0; x < 12; x++) {
int c = tile_chars[(int) (*f)[y][x]];
if (playing_game && cast_shadow) {
PieceData *piece = &piecedata[current_piece][current_rotation];
int piece_x = current_x - piece->hot_x;
if (x >= piece_x && x <= piece_x + 3
&& shadow[(x - piece_x)] >= 0
&& shadow[(x - piece_x)] < y
&& ((c & 0x7f) == ' ')) {
c = (c & (~0x7f)) | '.'
| getcolor(COLOR_BLACK, COLOR_BLACK) | A_BOLD;
}
}
mvaddch(y0+y*2, x0+x*2, c);
addch(c);
mvaddch(y0+y*2+1, x0+x*2, c);
addch(c);
}
}
if (gmsg_inputwin) {
delwin(gmsg_inputwin);
gmsg_inputwin = NULL;
draw_gmsg_input(NULL, -1);
}
if (!field_redraw)
screen_refresh();
}
/*************************************************************************/
/* Display another player's field. */
static void draw_other_field(int player)
{
int x, y, x0, y0;
Field *f;
if (dispmode != MODE_FIELDS)
return;
f = &fields[player-1];
if (player > my_playernum)
player--;
player--;
x0 = other_coord[player][0]+1;
y0 = other_coord[player][1];
for (y = 0; y < 22; y++) {
move(y0+y, x0);
for (x = 0; x < 12; x++) {
addch(tile_chars[(int) (*f)[y][x]]);
}
}
if (gmsg_inputwin) {
delwin(gmsg_inputwin);
gmsg_inputwin = NULL;
draw_gmsg_input(NULL, -1);
}
if (!field_redraw)
screen_refresh();
}
/*************************************************************************/
/* Display the current game status (level, lines, next piece). */
static void draw_status(void)
{
int x, y, i, j;
char buf[32], shape[4][4];
x = wide_screen ? alt_status_coord[0] : status_coord[0];
y = wide_screen ? alt_status_coord[1] : status_coord[1];
sprintf(buf, "%d", lines>99999 ? 99999 : lines);
mvaddstr(y, x+7, buf);
sprintf(buf, "%d", levels[my_playernum]);
mvaddstr(y+1, x+7, buf);
x = wide_screen ? alt_next_coord[0] : next_coord[0];
y = wide_screen ? alt_next_coord[1] : next_coord[1];
if (get_shape(next_piece, 0, shape) == 0) {
for (j = 0; j < 4; j++) {
if (!wide_screen)
move(y+j, x);
for (i = 0; i < 4; i++) {
if (wide_screen) {
move(y+j*2, x+i*2);
addch(tile_chars[(int) shape[j][i]]);
addch(tile_chars[(int) shape[j][i]]);
move(y+j*2+1, x+i*2);
addch(tile_chars[(int) shape[j][i]]);
addch(tile_chars[(int) shape[j][i]]);
} else
addch(tile_chars[(int) shape[j][i]]);
}
}
}
}
/*************************************************************************/
/* Display the special inventory and description of the current special. */
static const char *descs[] = {
" ",
"Add Line ",
"Clear Line ",
"Nuke Field ",
"Clear Random Blocks ",
"Switch Fields ",
"Clear Special Blocks",
"Block Gravity ",
"Blockquake ",
"Block Bomb "
};
static void draw_specials(void)
{
int x, y, i;
if (dispmode != MODE_FIELDS)
return;
x = own_coord[0];
y = own_coord[1]+45;
mvaddstr(y, x, descs[specials[0]+1]);
move(y+1, x+10);
i = 0;
while (i < special_capacity && specials[i] >= 0 && x < attdef_coord[0]-1) {
addch(tile_chars[specials[i]+6]);
i++;
x++;
}
while (x < attdef_coord[0]-1) {
addch(tile_chars[0]);
x++;
}
if (!field_redraw)
screen_refresh();
}
/*************************************************************************/
/* Display an attack/defense message. */
static const char *msgs[][2] = {
{ "cs1", "1 Line Added to All" },
{ "cs2", "2 Lines Added to All" },
{ "cs4", "4 Lines Added to All" },
{ "a", "Add Line" },
{ "c", "Clear Line" },
{ "n", "Nuke Field" },
{ "r", "Clear Random Blocks" },
{ "s", "Switch Fields" },
{ "b", "Clear Special Blocks" },
{ "g", "Block Gravity" },
{ "q", "Blockquake" },
{ "o", "Block Bomb" },
{ NULL }
};
static void draw_attdef(const char *type, int from, int to)
{
int i, width;
char buf[512];
width = other_coord[4][0] - attdef_coord[0] - 1;
for (i = 0; msgs[i][0]; i++) {
if (strcmp(type, msgs[i][0]) == 0)
break;
}
if (!msgs[i][0])
return;
strcpy(buf, msgs[i][1]);
if (to != 0)
sprintf(buf+strlen(buf), " on %s", players[to-1]);
if (from == 0)
sprintf(buf+strlen(buf), " by Server");
else
sprintf(buf+strlen(buf), " by %s", players[from-1]);
draw_text(BUFFER_ATTDEF, buf);
}
/*************************************************************************/
/* Display the in-game text window. */
static void draw_gmsg_input(const char *s, int pos)
{
static int start = 0; /* Start of displayed part of input line */
static const char *last_s;
static int last_pos;
if (s)
last_s = s;
else
s = last_s;
if (pos >= 0)
last_pos = pos;
else
pos = last_pos;
attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
if (!gmsg_inputwin) {
gmsg_inputpos = scrheight/2 - 1;
gmsg_inputheight = 3;
gmsg_inputwin =
subwin(stdscr, gmsg_inputheight, scrwidth, gmsg_inputpos, 0);
werase(gmsg_inputwin);
leaveok(gmsg_inputwin, FALSE);
leaveok(stdscr, FALSE);
mvwaddstr(gmsg_inputwin, 1, 0, "Text>");
}
if (strlen(s) < scrwidth-7) {
start = 0;
mvwaddstr(gmsg_inputwin, 1, 6, s);
wmove(gmsg_inputwin, 1, 6+strlen(s));
move(gmsg_inputpos+1, 6+strlen(s));
wclrtoeol(gmsg_inputwin);
wmove(gmsg_inputwin, 1, 6+pos);
move(gmsg_inputpos+1, 6+pos);
} else {
if (pos < start+8) {
start = pos-8;
if (start < 0)
start = 0;
} else if (pos > start + scrwidth-15) {
start = pos - (scrwidth-15);
if (start > strlen(s) - (scrwidth-7))
start = strlen(s) - (scrwidth-7);
}
mvwaddnstr(gmsg_inputwin, 1, 6, s+start, scrwidth-6);
wmove(gmsg_inputwin, 1, 6 + (pos-start));
move(gmsg_inputpos+1, 6 + (pos-start));
}
screen_refresh();
}
/*************************************************************************/
/* Clear the in-game text window. */
static void clear_gmsg_input(void)
{
if (gmsg_inputwin) {
delwin(gmsg_inputwin);
gmsg_inputwin = NULL;
leaveok(stdscr, TRUE);
touchline(stdscr, gmsg_inputpos, gmsg_inputheight);
setup_fields();
screen_refresh();
}
}
/*************************************************************************/
/*************************** Partyline display ***************************/
/*************************************************************************/
static void setup_partyline(void)
{
close_textwin(&gmsgbuf);
close_textwin(&attdefbuf);
clear();
attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
plinebuf.x = plinebuf.y = 0;
plinebuf.width = scrwidth;
plinebuf.height = scrheight-4;
open_textwin(&plinebuf);
move(scrheight-4, 0);
hline(MY_HLINE, scrwidth);
move(scrheight-3, 0);
addstr("> ");
move(scrheight-2, 0);
hline(MY_HLINE2, scrwidth);
attrset(MY_BOLD);
move(scrheight-1, 0);
addstr("F1=Show Fields F2=Partyline F3=Winlist");
move(scrheight-1, scrwidth-8);
addstr("F10=Quit");
attrset(A_NORMAL);
move(scrheight-3, 2);
leaveok(stdscr, FALSE);
screen_refresh();
}
/*************************************************************************/
static void draw_partyline_input(const char *s, int pos)
{
static int start = 0; /* Start of displayed part of input line */
attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
if (strlen(s) < scrwidth-3) {
start = 0;
mvaddstr(scrheight-3, 2, s);
move(scrheight-3, 2+strlen(s));
clrtoeol();
move(scrheight-3, 2+pos);
} else {
if (pos < start+8) {
start = pos-8;
if (start < 0)
start = 0;
} else if (pos > start + scrwidth-11) {
start = pos - (scrwidth-11);
if (start > strlen(s) - (scrwidth-3))
start = strlen(s) - (scrwidth-3);
}
mvaddnstr(scrheight-3, 2, s+start, scrwidth-2);
move(scrheight-3, 2 + (pos-start));
}
screen_refresh();
}
/*************************************************************************/
/**************************** Winlist display ****************************/
/*************************************************************************/
static void setup_winlist(void)
{
int i, x;
char buf[32];
leaveok(stdscr, TRUE);
close_textwin(&plinebuf);
clear();
attrset(getcolor(COLOR_WHITE,COLOR_BLACK));
for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
x = scrwidth/2 - strlen(winlist[i].name);
if (x < 0)
x = 0;
if (winlist[i].team) {
if (x < 4)
x = 4;
mvaddstr(i*2, x-4, "<T>");
}
mvaddstr(i*2, x, winlist[i].name);
snprintf(buf, sizeof(buf), "%4d", winlist[i].points);
if (winlist[i].games) {
int avg100 = winlist[i].points*100 / winlist[i].games;
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
" %d.%02d",avg100/100, avg100%100);
}
x += strlen(winlist[i].name) + 2;
if (x > scrwidth - strlen(buf))
x = scrwidth - strlen(buf);
mvaddstr(i*2, x, buf);
}
move(scrheight-2, 0);
hline(MY_HLINE2, scrwidth);
attrset(MY_BOLD);
move(scrheight-1, 0);
addstr("F1=Show Fields F2=Partyline F3=Winlist");
move(scrheight-1, scrwidth-8);
addstr("F10=Quit");
attrset(A_NORMAL);
screen_refresh();
}
/*************************************************************************/
/************************** Interface declaration ************************/
/*************************************************************************/
Interface tty_interface = {
wait_for_input,
screen_setup,
screen_refresh,
screen_redraw,
draw_text,
clear_text,
setup_fields,
draw_own_field,
draw_other_field,
draw_status,
draw_specials,
draw_attdef,
draw_gmsg_input,
clear_gmsg_input,
setup_partyline,
draw_partyline_input,
setup_winlist
};
/*************************************************************************/