X-Git-Url: http://git.shiar.nl/netris.git/blobdiff_plain/45dc9d995860486f1758dcf79fd2d8cd8dfb210a..0e779d807aa1830dde2f4a75117fd16f5627dc76:/curses.c diff --git a/curses.c b/curses.c index 8d500d9..aab808d 100644 --- a/curses.c +++ b/curses.c @@ -20,31 +20,45 @@ */ #include "netris.h" + #include #include #include #include #include +#include "client.h" +#include "curses.h" +#include "util.h" +#include "board.h" +#include "msg.en.h" + #ifdef NCURSES_VERSION # define HAVE_NCURSES #endif -ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type); - static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event); static EventGenRec keyGen = +// { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key }; { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key }; static int boardYPos[MAX_SCREENS], boardXPos[MAX_SCREENS]; +static int boardSize[MAX_SCREENS]; +//^^^struct static int statusYPos, statusXPos; static int messageYPos, messageXPos, messageHeight, messageWidth; +WINDOW *msgwin; static int haveColor; +int PlayerDisp[MAX_SCREENS]; + +#define MSG_HEIGHT 64 //max history +char *message[MSG_HEIGHT]; +char messages[MSG_HEIGHT][MSG_WIDTH]; static char *term_vi; /* String to make cursor invisible */ static char *term_ve; /* String to make cursor visible */ -ExtFunc void InitScreens(void) +void InitScreens(void) { MySigSet oldMask; @@ -66,10 +80,10 @@ ExtFunc void InitScreens(void) #endif #ifdef HAVE_NCURSES - haveColor = Game.color && has_colors(); + haveColor = Sets.color && has_colors(); if (haveColor) { static struct { - BlockType type; + char type; short color; } myColorTable[] = { { BT_white, COLOR_WHITE }, @@ -103,20 +117,30 @@ ExtFunc void InitScreens(void) // keypad(stdscr, TRUE); //get arrow/functionkeys 'n stuff OutputTermStr(term_vi, 0); AddEventGen(&keyGen); //key handler + signal(SIGWINCH, CatchWinCh); //handle window resize +// ioctl(STDIN_FILENO, KDSKBMODE, K_MEDIUMRAW); standend(); //normal text + + memset(messages, 0, sizeof(messages)); //empty messages + { + int i; + for (i = 0; i", + snprintf(s, sizeof(s), " %s <%s> ", Players[scr].name, Players[scr].host); - else snprintf(s, sizeof(s), "%s", Players[scr].name); - s[strlen(s)] = ' '; - s[sizeof(s) - 7*((Players[scr].flags & SCF_usingRobot) != 0) - - 5*((Players[scr].flags & SCF_fairRobot) != 0)] = 0; + else snprintf(s, sizeof(s), " %s ", Players[scr].name); + s[sizeof(s)] = 0; + if (haveColor && Players[scr].team > 0 && Players[scr].team <= 7) + attrset(A_REVERSE | COLOR_PAIR(Players[scr].team + 1)); mvaddstr(1, boardXPos[scr], s); - - if (Players[scr].flags & SCF_usingRobot) { - addstr((Players[scr].flags & SCF_fairRobot) - ? "(fair robot)" : "(robot)"); - } //add robot indicator + if (haveColor) standend(); } //display playername/host { @@ -252,147 +271,265 @@ ExtFunc void DrawField(int scr) ShowPause(scr); } //DrawField -ExtFunc void InitFields(void) +void InitFields(void) { //calculate positions of all fields int scr, prevscr; int y, x; + int spaceavail; + clear(); + DrawTitle(); getmaxyx(stdscr, y, x); + boardSize[me] = 2; boardXPos[me] = 1; - boardYPos[me] = 22; - statusXPos = 2 * Players[me].boardWidth + 3; - statusYPos = 22; + boardYPos[me] = 21; + PlayerDisp[me] = 1; + statusXPos = boardSize[me] * Players[me].boardWidth + 3; + statusYPos = 21; + ShowScore(me, Players[me].score); + messageXPos = 2; - messageYPos = 25; - messageWidth = x - messageXPos - 2; - if ((messageHeight = y - messageYPos - 1) < 0) messageHeight = 0; - else DrawBox(messageXPos - 2, messageYPos - 1, - messageXPos + messageWidth + 1, messageYPos + messageHeight); + messageYPos = 24; + if ((messageWidth = x - messageXPos - 2) > MSG_WIDTH) messageWidth = MSG_WIDTH; + if ((messageHeight = y - messageYPos - 1) > MSG_HEIGHT) messageHeight = MSG_HEIGHT; + if (messageHeight <= 0) { + messageWidth = 27; + messageHeight = y - 3; + messageXPos = statusXPos + 16; + messageYPos = 2; + } //messagebox doesn't fit below + DrawBox(messageXPos - 2, messageYPos - 1, + messageXPos + messageWidth + 1, messageYPos+messageHeight); + if (msgwin = subwin(stdscr, messageHeight, messageWidth, + messageYPos, messageXPos)) + scrollok(msgwin, 1); //allow scrolling + wmove(msgwin, messageHeight - 2, 0); + for (scr = messageHeight - 2; scr >= 0; scr--) //display message history + DisplayMessage(message[scr]); + + spaceavail = x; + for (scr = 1; scr <= maxPlayer; scr++) + spaceavail -= Players[scr].boardWidth+2; prevscr = me; for (scr = 1; scr < MAX_SCREENS; scr++) if (scr != me) { + boardYPos[scr] = 21; boardXPos[scr] = - boardXPos[prevscr] + 2 * Players[prevscr].boardWidth + 3; - if (prevscr == me) - boardXPos[scr] += 14; //scorebar - boardYPos[scr] = 22; - if (x < boardXPos[scr] + 2 * Players[scr].boardWidth + 1) - Players[scr].spy = 0; //field doesn't fit on screen + boardXPos[prevscr] + 2 + boardSize[prevscr] * Players[prevscr].boardWidth; + if (prevscr == me) { + boardXPos[scr] += 15; //scorebar + if (messageYPos < 24) + boardXPos[scr] += messageWidth + 4; //messagebox + spaceavail -= boardXPos[scr] - 3; + } //stuff before second player + if (spaceavail >= 0) { + boardSize[scr] = 2; + spaceavail -= Players[scr].boardWidth; + } //not enough space, half width + else + boardSize[scr] = 1; + if (x < boardXPos[scr] + 1 + boardSize[scr] * Players[scr].boardWidth) + PlayerDisp[scr] = 0; //field doesn't fit on screen + else + PlayerDisp[scr] = 1; prevscr = scr; } - for (scr = 1; scr <= Game.maxplayers; scr++) + for (scr = 1; scr <= maxPlayer; scr++) DrawField(scr); } //InitFields -ExtFunc void CleanupScreen(int scr) +void CleanupScreen(int scr) { } -ExtFunc void Messagef(char *fmt, ...) +void DisplayMessage(char *p) +{ + char s[MSG_WIDTH]; + char *psearch; + char c; + + memcpy(s, p, sizeof(s)-1); + s[MSG_WIDTH-1] = 0; + p = s; + while (psearch = strchr(p, '\\')) { + *psearch = '\0'; + waddstr(msgwin, p); + c = atoi(++psearch)+1; + if (haveColor) wattrset(msgwin, A_REVERSE | COLOR_PAIR(c)); + p = ++psearch; + } //search for color escapes (\) + waddstr(msgwin, p); + if (haveColor) wstandend(msgwin); + waddch(msgwin, '\n'); +} //DisplayMessage + +void Message(char *fmt, ...) { //print game/bot message - static int line = 0; va_list args; - char s[255]; - char *p, *psearch; + char s[MSG_WIDTH]; + char *p; int i; if (!messageHeight) return; va_start(args, fmt); - move(messageYPos + line, messageXPos); -// vwprintw(stdscr, fmt, args); //doesn't seem to be a vprintw - vsprintf(s, fmt, args); - p = s; - while (psearch = strchr(s, '\\')) { - *psearch = '\0'; - addstr(p); - if (haveColor) - attrset(A_REVERSE | COLOR_PAIR(atoi(psearch + 1) + 1)); - p = psearch + 2; - } //search for color escapes (\) - addstr(p); - if (messageHeight > 1) { - char s[messageWidth + 1]; - line = (line + 1) % messageHeight; - memset(s, ' ', messageWidth); - s[messageWidth] = 0; - mvaddstr(messageYPos + line, messageXPos, s); - } //multiple lines - if (haveColor) standend(); + vsnprintf(s, sizeof(s), fmt, args); va_end(args); + p = message[MSG_HEIGHT - 1]; //save last pointer + for (i = MSG_HEIGHT - 1; i > 0; i--) + message[i] = message[i - 1]; //scroll history + message[0] = p; + strcpy(p, s); + + wmove(msgwin, messageHeight - 1, 0); + DisplayMessage(s); + wclrtoeol(msgwin); + wrefresh(msgwin); } //Message -ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type) -{ - int colorIndex = abs(type); - +void Messagetype(char c, int x, char *s) +{ //show single typed character + if (c == 27) { + mvwaddch(msgwin, messageHeight-1, (x+1) % (messageWidth-1), ' '); + } //escape + else { + if (c == 13 || c==127) //enter/backspace + mvwaddch(msgwin, messageHeight - 1, (x+2) % (messageWidth-1), + x>=messageWidth-3 ? s[x - messageWidth + 3] : ' '); + else //any character + mvwaddch(msgwin, messageHeight - 1, x % (messageWidth-1), c); + mvwaddch(msgwin, messageHeight - 1, (x+1) % (messageWidth-1), '_'); + } //typing mode + wrefresh(msgwin); +} //Messagetype + +void PlotBlock1(int y, int x, unsigned char type) +{ //display block on screen move(y, x); if (type == BT_none) addstr(" "); else if (type == BT_shadow) addstr("::"); else { - if (Game.standout) { #ifdef HAVE_NCURSES - if (haveColor) attrset(COLOR_PAIR(colorIndex)); + if (Sets.standout) { + if (haveColor) attrset(COLOR_PAIR(type & 15)); else attrset(A_REVERSE); -#endif } - addstr(type ? "[]" : "$$"); +#endif + switch (Sets.drawstyle) { + case 2: + switch (type & 192) { + case 64: //right neighbour + addstr("[["); + break; + case 128: //left + addstr("]]"); + break; + default: //both/none + addstr("[]"); + break; + } //horizontal stickiness + break; //ascii horizontally grouped + case 3: + switch (type & 240) { + case 48: + addstr("||"); break; //middle + case 64: case 80: case 96: + addstr("[="); break; //left end + case 112: + addstr("|="); break; + case 128: case 144: case 160: + addstr("=]"); break; //right end + case 176: + addstr("=|"); break; + case 192: case 208: case 224: + addstr("=="); break; + default: + addstr("[]"); break; //top/bottom/mid + } //neighbours + break; //ascii semi-grouped + case 7: + switch (type & 240) { + case 16: addch(ACS_ULCORNER); addch(ACS_URCORNER); break;//top end + case 32: addch(ACS_LLCORNER); addch(ACS_LRCORNER); break;//bottom end + case 48: addch(ACS_VLINE); addch(ACS_VLINE); break; //vertical middle + case 64: addch(' '); addch(ACS_HLINE); break; //left end + case 80: addch(ACS_ULCORNER); addch(ACS_TTEE); break; //top left corner + case 96: addch(ACS_LLCORNER); addch(ACS_BTEE); break; //bottom left corner + case 112: addch(ACS_LTEE); addch(ACS_PLUS); break; //vertical+right + case 128: addch(ACS_HLINE); addch(' '); break; //right end + case 144: addch(ACS_TTEE); addch(ACS_URCORNER); break; //top right corner + case 160: addch(ACS_BTEE); addch(ACS_LRCORNER); break; //bottom right corner + case 176: addch(ACS_PLUS); addch(ACS_RTEE); break; //vertical+left + case 192: addch(ACS_HLINE); addch(ACS_HLINE); break; //horizontal middle + case 208: addch(ACS_TTEE); addch(ACS_TTEE); break; //horizontal+down + case 224: addch(ACS_BTEE); addch(ACS_BTEE); break; //horizontal+up + default: addstr("[]"); break; + } //neighbours + break; //curses grouped + default: + addstr("[]"); + break; //ascii non-grouped + } //draw block #ifdef HAVE_NCURSES - if (Game.standout) standend(); + if (Sets.standout) standend(); #endif } //display one brick } //PlotBlock1 -ExtFunc void PlotBlock(int scr, int y, int x, BlockType type) -{ - if (y >= 0 && y < Players[scr].boardVisible && - x >= 0 && x < Players[scr].boardWidth) - PlotBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type); -} //PlotBlock - -ExtFunc void PlotBlockS1(int scr, int y, int x, BlockType type) -{ //DOESN"T WORK YET... +void PlotBlock1S(int y, int x, unsigned char type) +{ //display block small move(y, x); - if (type == BT_none) addstr(" "); + if (type == BT_none) addch(' '); + else if (type == BT_shadow) addch(':'); else { - addstr(type ? "O" : "$"); - standend(); + if (Sets.standout) { +#ifdef HAVE_NCURSES + if (haveColor) + attrset(COLOR_PAIR(type & 15)); + else attrset(A_REVERSE); +#endif + } + if ((type & 192) == 64) + addch('['); + else if ((type & 192) == 128) + addch(']'); + else + addch('|'); +#ifdef HAVE_NCURSES + if (Sets.standout) standend(); +#endif } //display one brick -} //PlotBlock1 -ExtFunc void PlotBlockS(int scr, int y, int x, BlockType type) +} //PlotBlock1S +void PlotBlock(int scr, int y, int x, unsigned char type) { if (y >= 0 && y < Players[scr].boardVisible && - x >= 0 && x < Players[scr].boardWidth) - PlotBlockS1(scr, boardYPos[scr] - y, boardXPos[scr] + x, type); + x >= 0 && x < Players[scr].boardWidth) { + if (boardSize[scr] > 1) + PlotBlock1(boardYPos[scr] - y, boardXPos[scr] + 2*x, type); + else + PlotBlock1S(boardYPos[scr] - y, boardXPos[scr] + x, type); + } //on screen +} //PlotBlock +void PlotBlockXY(int y, int x, unsigned char type) +{ //Draw block at specified position on screen (not on field) + PlotBlock1(20 - y, 2 * x, type); } //PlotBlock -ExtFunc void PlotUnderline(int scr, int x, int flag) -{ //display piece of bottom fieldgrid - move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x); - if (Game.ascii) - addstr(flag ? "==" : "--"); - else { - addch(flag ? ACS_BTEE : ACS_HLINE); - addch(flag ? ACS_BTEE : ACS_HLINE); - } //ncurses graphics -} //PlotUnderline - -ExtFunc void ShowScore(int scr, struct _Score score) +void ShowScore(int scr, struct _Score score) { //show score stuff float timer; - mvaddstr(13, statusXPos, "next "); + mvaddstr(13, statusXPos, MSG_NEXT " "); mvaddstr(14, statusXPos + 5, " "); ShapeIterate(Players[scr].nextShape, scr, - ShapeToNetNum(Players[scr].nextShape) == 15 ? 6 : 7, - statusXPos / 2 + 4, 1, GlanceFunc, NULL); - mvprintw(3, statusXPos, "level %5d", score.level); - mvprintw(5, statusXPos, "score%8d", score.score); - mvprintw(6, statusXPos, "lines%8d", score.lines); + 8, statusXPos/2 + (Players[scr].nextShape/4 == 5 ? 3 : 4), + GlanceFunc); //draw; stick one more to the left + mvprintw(3, statusXPos, MSG_LEVEL, score.level); + mvprintw(5, statusXPos, MSG_SCORE, score.score); + mvprintw(6, statusXPos, MSG_LINES, score.lines); timer = CurTimeval() / 1e6; if (timer > 4) { - mvprintw(9, statusXPos, "ppm %9.1f", score.drops * 60 / timer); + mvprintw(9, statusXPos, MSG_PPM, score.pieces * 60 / timer); if (score.lines > 0) { - mvprintw(7, statusXPos, - "yield %3d%%", 100 * score.adds / score.lines); - mvprintw(10, statusXPos, "apm %9.1f", score.adds * 60 / timer); + mvprintw(7, statusXPos, MSG_YIELD, 100 * score.adds / score.lines); + mvprintw(10, statusXPos, MSG_APM, score.adds * 60 / timer); } } //display [ap]pm else { @@ -402,15 +539,15 @@ ExtFunc void ShowScore(int scr, struct _Score score) } //too early to display stats, remove old.. } //ShowScore -ExtFunc void FieldMessage(int playa, char *message) +void FieldMessage(int playa, char *message) { //put a message over playa's field - if (!Players[playa].spy) return; + if (!PlayerDisp[playa]) return; if (message) { char s[MAX_BOARD_WIDTH+1]; memset(s, ' ', MAX_BOARD_WIDTH); - memcpy(&s[Players[playa].boardWidth - strlen(message) / 2], + memcpy(&s[(boardSize[playa] * Players[playa].boardWidth / 2) - (strlen(message) / 2)], message, strlen(message)); - s[Players[playa].boardWidth * 2] = 0; + s[boardSize[playa] * Players[playa].boardWidth] = 0; #ifdef HAVE_NCURSES attrset(A_REVERSE); #else @@ -428,30 +565,46 @@ ExtFunc void FieldMessage(int playa, char *message) } //restore field } //FieldMessage -ExtFunc void ShowPause(int playa) +void ShowPause(int playa) { //put paused over player's field - if (Players[playa].alive) + if (Players[playa].alive > 0) if (Players[playa].flags & SCF_paused) - FieldMessage(playa, Game.started > 1 - ? "P A U S E D" : "N O T R E A D Y"); - else FieldMessage(playa, Game.started > 1 ? NULL : "R E A D Y"); - else FieldMessage(playa, playa > maxPlayer - ? "E M P T Y" : "G A M E O V E R"); + if (Game.started > 1) + FieldMessage(playa, boardSize[playa] > 1 ? "P A U S E D" : "PAUSED"); + else + FieldMessage(playa, + boardSize[playa] > 1 ? "N O T R E A D Y" : "NOT READY"); + else + if (Game.started > 1) + FieldMessage(playa, NULL); + else + FieldMessage(playa, boardSize[playa] > 1 ? "R E A D Y" : "READY"); + else if (!Players[playa].alive) + FieldMessage(playa, + boardSize[playa] > 1 ? "G A M E O V E R" : "GAME OVER"); + else + FieldMessage(playa, boardSize[playa] > 1 ? "E M P T Y" : "EMPTY"); } //ShowPause -ExtFunc void ShowTime(void) +void ShowTime(void) { //display timer mvprintw(statusYPos, statusXPos, "timer %7.0f ", CurTimeval() / 1e6); -// move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1); -// refresh(); } //ShowTime -ExtFunc void ScheduleFullRedraw(void) +void ScheduleFullRedraw(void) { touchwin(stdscr); } //ScheduleFullRedraw +void CatchWinCh(int sig) +{ //handle window resize + endwin(); //exit curses + refresh(); //and reinit display (with different sizes) + InitFields(); //manually redraw everything + refresh(); //refresh +} //CatchWinCh + static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event) { //read keypresses if (MyRead(gen->fd, &event->u.key, 1))