+ int rows, cols;
+ char *s;
+
+#ifdef HAVE_NCURSES
+ attrset(A_REVERSE);
+#else
+ standout();
+#endif
+ getmaxyx(stdscr, rows, cols);
+ s = malloc(cols + 1);
+ sprintf(s, " " MSG_TITLE " %s", version_string);
+ const int titlelen = strlen(s);
+ memset(&s[titlelen], ' ', cols - titlelen); // pad
+ if (cols > titlelen + 1 + strlen(MSG_TITLESUB))
+ memcpy(&s[cols - 1 - strlen(MSG_TITLESUB)], MSG_TITLESUB, sizeof(MSG_TITLESUB) - 1);
+ memcpy(&s[cols], "\0", 1);
+ mvaddstr(0, 0, s);
+ free(s);
+ standend(); //normal text
+}
+
+static void window_border(int x1, int y1, int x2, int y2)
+{ //draw grid
+ int y, x;
+
+ for (y = y1 + 1; y < y2; y++) {
+ mvaddch(y, x1, Sets.ascii ? '|' : ACS_VLINE); //left
+ mvaddch(y, x2, Sets.ascii ? '|' : ACS_VLINE); //right
+ } //draw vertical lines
+ move(y1, x1); //top
+ addch(Sets.ascii ? '+' : ACS_ULCORNER);
+ for (x = x1 + 1; x < x2; x++)
+ addch(Sets.ascii ? '-' : ACS_HLINE);
+ addch(Sets.ascii ? '+' : ACS_URCORNER);
+ move(y2, x1); //bottom
+ addch(Sets.ascii ? '+' : ACS_LLCORNER);
+ for (x = x1 + 1; x < x2; x++)
+ addch(Sets.ascii ? '-' : ACS_HLINE);
+ addch(Sets.ascii ? '+' : ACS_LRCORNER);
+}
+
+void window_draw(int player)
+{ //draw field for player
+ if (!window[player].shown) return;
+ window_border(window[player].posx - 1, window[player].posy - Players[player].boardVisible,
+ window[player].posx + window[player].size * Players[player].boardWidth, window[player].posy + 1);
+ {
+ char s[window[player].size * Players[player].boardWidth + 1];
+
+ if (Players[player].host && Players[player].host[0])
+ snprintf(s, sizeof(s), " %s <%s> ",
+ Players[player].name, Players[player].host);
+ else snprintf(s, sizeof(s), " %s ", Players[player].name);
+ s[sizeof(s)] = 0;
+ if (haveColor && Players[player].team > 0 && Players[player].team <= 7)
+ attrset(A_REVERSE | COLOR_PAIR(Players[player].team + 1));
+ mvaddstr(1, window[player].posx, s);
+ if (haveColor) standend();
+ } //display playername/host
+
+ {
+ int x, y;
+ for (y = 0; y <= Players[player].boardVisible; y++)
+ for (x = 0; x <= Players[player].boardWidth; x++)
+ block_draw_window(player, y, x, block_get(player, y, x));
+ } //draw field
+
+ window_msg_status(player);
+}
+
+void screen_setup(void)
+{ //calculate positions of all fields
+ int i, prev;
+ int y, x;
+ int spaceavail;
+
+ clear();
+ DrawTitle();
+ getmaxyx(stdscr, y, x);
+ window[me].size = 2;
+ window[me].posx = 1;
+ window[me].posy = 21;
+ window[me].shown = 1;
+ statusXPos = window[me].size * Players[me].boardWidth + 3;
+ statusYPos = 21;
+ status_draw(me, Players[me].score);
+
+ messageXPos = 2;
+ messageYPos = 24;
+ messageWidth = MIN(x - messageXPos - 2, MSG_WIDTH);
+ messageHeight = MIN(y - messageYPos - 1, MSG_HEIGHT);
+ if (messageHeight < 3) {
+ messageWidth = MIN(x - statusXPos - 18, 27);
+ messageHeight = y - 3;
+ messageXPos = statusXPos + 16;
+ messageYPos = 2;
+ } //messagebox doesn't fit below
+ window_border(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 (i = messageHeight - 2; i >= 0; i--) //display message history
+ msg_draw(message[i]);
+
+ spaceavail = x;
+ for (i = 1; i <= maxPlayer; i++)
+ spaceavail -= Players[i].boardWidth+2;
+ prev = me;
+ for (i = 1; i < MAX_SCREENS; i++) if (i != me) {
+ window[i].posy = 21;
+ window[i].posx =
+ window[prev].posx + 2 + window[prev].size * Players[prev].boardWidth;
+ if (prev == me) {
+ window[i].posx += 15; //scorebar
+ if (messageYPos < 24)
+ window[i].posx += messageWidth + 4; //messagebox
+ spaceavail -= window[i].posx - 3;
+ } //stuff before second player
+ if (spaceavail >= 0) {
+ window[i].size = 2;
+ spaceavail -= Players[i].boardWidth;
+ } //not enough space, half width
+ else
+ window[i].size = 1;
+ if (x < window[i].posx + 1 + window[i].size * Players[i].boardWidth)
+ window[i].shown = 0; //field doesn't fit on screen
+ else
+ window[i].shown = 1;
+ prev = i;
+ }
+ for (i = 1; i <= maxPlayer; i++)
+ window_draw(i);
+}
+
+static void msg_draw(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');
+}
+
+void msg_add(char *fmt, ...)
+{ //print game/bot message
+ va_list args;
+ char s[MSG_WIDTH];
+ char *p;
+ int i;
+
+ if (!messageHeight) return;
+ va_start(args, fmt);
+ 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);
+ msg_draw(s);
+ wclrtoeol(msgwin);
+ wrefresh(msgwin);
+}
+
+void msg_add_char(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);
+}
+
+static void block_draw_2(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 {
+#ifdef HAVE_NCURSES
+ if (Sets.standout) {
+ attrset(haveColor ? COLOR_PAIR(type & 15) : A_REVERSE);
+ }
+#endif
+ switch (Sets.drawstyle) {
+ case 2: // ascii horizontally grouped
+ switch (type & 0xC0) {
+ case 0x40: // right neighbour
+ addstr("[["); break;
+ case 0x80: // left
+ addstr("]]"); break;
+ default: // both/none
+ addstr("[]"); break;
+ } // horizontal stickiness