1039 lines
26 KiB
C
1039 lines
26 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.
|
|
*/
|
|
|
|
static int wait_for_input(int msec)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
int c;
|
|
|
|
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 (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))
|
|
return K_F1;
|
|
else if (c == KEY_F(2))
|
|
return K_F2;
|
|
else if (c == KEY_F(3))
|
|
return K_F3;
|
|
else if (c == KEY_F(4))
|
|
return K_F4;
|
|
else if (c == KEY_F(5))
|
|
return K_F5;
|
|
else if (c == KEY_F(6))
|
|
return K_F6;
|
|
else if (c == KEY_F(7))
|
|
return K_F7;
|
|
else if (c == KEY_F(8))
|
|
return K_F8;
|
|
else if (c == KEY_F(9))
|
|
return K_F9;
|
|
else if (c == KEY_F(10))
|
|
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 linux
|
|
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] = {0,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] = {28,25};
|
|
static const int next_coord[2] = {40,24};
|
|
static const int alt_status_coord[2] = {28,2};
|
|
static const int alt_next_coord[2] = {29,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+FIELD_WIDTH*2+2, buf);
|
|
for (i = 2; i < FIELD_HEIGHT*2 && players[my_playernum-1][i-2]; i++)
|
|
mvaddch(y+i, x+FIELD_WIDTH*2+2, players[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+FIELD_WIDTH+2, buf);
|
|
if (players[j+1]) {
|
|
for (i = 0; i < FIELD_HEIGHT-2 && players[j+1][i]; i++)
|
|
mvaddch(y+i+2, x+FIELD_WIDTH+2, players[j+1][i]);
|
|
}
|
|
draw_other_field(j+2);
|
|
} else {
|
|
sprintf(buf, "%d", j+1);
|
|
mvaddstr(y, x+FIELD_WIDTH+2, buf);
|
|
if (players[j]) {
|
|
for (i = 0; i < FIELD_HEIGHT-2 && players[j][i]; i++)
|
|
mvaddch(y+i+2, x+FIELD_WIDTH+2, players[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];
|
|
|
|
if (dispmode != MODE_FIELDS)
|
|
return;
|
|
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]];
|
|
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+1, x+7, buf);
|
|
sprintf(buf, "%d", levels[my_playernum]);
|
|
mvaddstr(y+2, 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 xwin_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
|
|
};
|
|
|
|
/*************************************************************************/
|