+/* -*-c-*-
+ *
+ * $Id: ui-curses.c,v 1.1 2003/12/12 10:55:30 mdw Exp $
+ *
+ * Curses user interface
+ *
+ * (c) 2003 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of XOR.
+ *
+ * XOR is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * XOR is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XOR; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: ui-curses.c,v $
+ * Revision 1.1 2003/12/12 10:55:30 mdw
+ * Initial checkin. Not there yet.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "xor.h"
+
+#if defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#else
+# include <curses.h>
+#endif
+
+#ifdef __CYGWIN__
+# include <windows.h>
+# undef usleep
+# define usleep(n) Sleep(n / 1000);
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+struct ui_state {
+ int vx, vy;
+ level *l;
+};
+
+/*---- Display setup ------------------------------------------------------*/
+
+#define VIEWX 5
+#define VIEWY 3
+#define VIEWWD 8
+#define VIEWHT 8
+#define CELLHT 2
+#define CELLWD 3
+
+#define STATUSX 40
+#define STATUSY 10
+#define STATUSWD 36
+#define STATUSHT 2
+
+#define MESSAGEY 16
+
+static chtype *dispmap[CHAR_MAX], mdispmap[CHAR_MAX];
+
+static struct { int c; chtype m; chtype d[CELLHT * CELLWD]; } disptab[] = {
+ { C_EMPTY, ' ', { ' ', ' ', ' ', ' ', ' ', ' ' } },
+ { C_PLAYER, '$', { '|', '~', '|', '\\', '_', '/' } },
+ { C_SPARE, '%', { '|', 'X', '|', '\\', '_', '/' } },
+ { C_EXIT, '=', { '|', '=', '|', '|', '_', '|' } },
+ { C_WALL, '#', { '#', '#', '#', '#', '#', '#' } },
+ { C_MASK, '+', { 'o', ' ', 'o', ' ', '>', ' ' } },
+ { C_NWMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
+ { C_NEMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
+ { C_SWMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
+ { C_SEMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
+ { C_DOT, ':', { ':', ':', ':', ':', ':', ':' } },
+ { C_WAVE, '~', { '~', '~', '~', '~', '~', '~' } },
+ { C_CHICKEN, '<', { '<', 'o', ')', ' ', '(', ')' } },
+ { C_FISH, 'V', { ' ', 'V', ' ', ' ', 'V', ' ' } },
+ { C_HBOMB, 'U', { '_', 'T', '_', '|', '_', '|' } },
+ { C_VBOMB, 'C', { ' ', ',', '*', '(', ')', ' ' } },
+ { C_DOLLY, '@', { '\\', '@', '/', '/', '=', '\\' } },
+ { C_SWITCH, '~', { 'o', ' ', 'o', ' ', '*', ' ' } },
+ { C_BMUS, 'O', { '/', '~', '\\', '\\', '_', '/' } },
+ { 0, 0, { 0 } }
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+static void rect(int x, int y, int w, int h)
+{
+ int i;
+
+ mvaddch(y - 1, x - 1, ACS_ULCORNER);
+ for (i = 0; i < w; i++) addch(ACS_HLINE);
+ addch(ACS_URCORNER);
+ for (i = 0; i < h; i++) {
+ mvaddch(y + i, x - 1, ACS_VLINE);
+ mvaddch(y + i, x + w, ACS_VLINE);
+ }
+ mvaddch(y + h, x - 1, ACS_LLCORNER);
+ for (i = 0; i < w; i++) addch(ACS_HLINE);
+ addch(ACS_LRCORNER);
+}
+
+static void hbar(int x, int y, int w)
+{
+ int i;
+
+ move(y, x - 1);
+ addch(ACS_LTEE);
+ for (i = 0; i < w; i++) addch(ACS_HLINE);
+ addch(ACS_RTEE);
+}
+
+static void vbar(int x, int y, int h)
+{
+ int i;
+
+ mvaddch(y - 1, x, ACS_TTEE);
+ for (i = 0; i < h; i++) mvaddch(y + i, x, ACS_VLINE);
+ mvaddch(y + h, x, ACS_BTEE);
+}
+
+static void draw(void)
+{
+ erase();
+ rect(VIEWX, VIEWY, VIEWWD * CELLWD, VIEWHT * CELLHT);
+ rect(STATUSX, STATUSY - 2, STATUSWD, STATUSHT + 2);
+ hbar(STATUSX, STATUSY - 1, STATUSWD);
+ vbar(STATUSX + STATUSWD - 5, STATUSY, STATUSHT);
+}
+
+void ui_init(void)
+{
+ int i;
+
+ /* --- Kick curses --- */
+
+ initscr();
+ if (LINES < 25 || COLS < 80) {
+ endwin();
+ fprintf(stderr, "terminal too small\n");
+ exit(1);
+ }
+ cbreak();
+ noecho();
+ nonl();
+ intrflush(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ draw();
+
+ /* --- Initialize the display map --- */
+
+ for (i = 0; disptab[i].c; i++) {
+ dispmap[disptab[i].c] = disptab[i].d;
+ mdispmap[disptab[i].c] = disptab[i].m;
+ }
+ for (i = 0; i < CELLWD * CELLHT; i++)
+ dispmap[C_WALL][i] = ' ' | A_REVERSE;
+ mdispmap[C_WALL] = ' ' | A_REVERSE;
+}
+
+static void redraw(ui_state *u)
+{
+ const level *l = u->l;
+ int x, y, i, j, c;
+
+ for (y = 0; y < VIEWHT; y++) {
+ for (j = 0; j < CELLHT; j++) {
+ move(VIEWY + y * CELLHT + j, VIEWX);
+ for (x = 0; x < VIEWWD; x++) {
+ c = CELL(l, u->vx + x, u->vy + y) & CF_CELLMASK;
+ if ((l->f & LF_DARK) && (cellmap[c]->f & CF_HIDE)) c = C_EMPTY;
+ for (i = 0; i < CELLWD; i++) {
+ addch(dispmap[c][i + j * CELLWD]);
+ }
+ }
+ }
+ }
+
+ mvaddstr(STATUSY - 2, STATUSX + (STATUSWD - strlen(u->l->name))/2,
+ u->l->name);
+
+ mvprintw(STATUSY, STATUSX, "Masks: %2d/%2d", l->m, l->mtot);
+ mvprintw(STATUSY + 1, STATUSX, "Moves: %4d/%4d", l->v, l->vtot);
+ move(STATUSY, STATUSX + STATUSWD - 4);
+ if (l->f & LF_NWMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
+ else { addch(' '); addch(' '); }
+ if (l->f & LF_NEMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
+ else { addch(' '); addch(' '); }
+ move(STATUSY + 1, STATUSX + STATUSWD - 4);
+ if (l->f & LF_SWMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
+ else { addch(' '); addch(' '); }
+ if (l->f & LF_SEMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
+ else { addch(' '); addch(' '); }
+}
+
+void ui_update(ui_state *u)
+{
+ redraw(u);
+ refresh();
+}
+
+void ui_explode(struct ui_state *u, const point *pt, int n)
+{
+ int i, j, x, y;
+ static const chtype frame[][CELLWD * CELLHT] = {
+ { '\\', '|', '/', '/', '|', '\\' },
+ { '*', '*', '*', '*', '*', '*' },
+ { ':', ':', ':', ':', ':', ':' },
+ { '.', '.', '.', ':', ':', ':' },
+ { ' ', ' ', ' ', ':', ':', ':' },
+ { ' ', ' ', ' ', '.', '.', '.' }
+ };
+
+ redraw(u);
+ refresh();
+ for (i = 0; i < sizeof(frame)/sizeof(frame[0]); i++) {
+ for (j = 0; j < n; j++) {
+ if (pt[j].x < u->vx || pt[j].x >= u->vx + VIEWWD ||
+ pt[j].y < u->vy || pt[j].y >= u->vy + VIEWHT)
+ continue;
+ for (y = 0; y < CELLHT; y++) {
+ move(VIEWY + (pt[j].y - u->vy) * CELLHT + y,
+ VIEWX + (pt[j].x - u->vx) * CELLWD);
+ for (x = 0; x < CELLWD; x++)
+ addch(frame[i][x + CELLWD * y]);
+ }
+ }
+ refresh();
+ usleep(10000);
+ }
+}
+
+void ui_track(ui_state *u, int x, int y)
+{
+ if (u->vx == -1) {
+ u->vx = x - VIEWWD/2;
+ u->vy = y - VIEWHT/2;
+ if (u->vx < 0) u->vx = 0;
+ else if (u->vx > u->l->w - VIEWWD) u->vx = u->l->w - VIEWWD;
+ if (u->vy < 0) u->vy = 0;
+ else if (u->vy > u->l->h - VIEWHT) u->vy = u->l->h - VIEWHT;
+ } else {
+ if (u->vx + 1 > x) u->vx = x - 1;
+ else if (u->vx + VIEWWD - 2 < x) u->vx = x - VIEWWD + 2;
+ if (u->vy + 1 > y) u->vy = y - 1;
+ else if (u->vy + VIEWHT - 2 < y) u->vy = y - VIEWHT + 2;
+ }
+}
+
+ui_state *ui_start(level *l, int x, int y)
+{
+ ui_state *u = CREATE(ui_state);
+
+ u->l = l;
+ u->vx = u->vy = -1;
+ ui_track(u, x, y);
+ return (u);
+}
+
+void ui_switch(ui_state *u)
+{
+ redraw(u);
+}
+
+void ui_frame(struct ui_state *u)
+{
+ redraw(u);
+ refresh();
+ usleep(20000);
+}
+
+void ui_message(struct ui_state *u, const char *p)
+{
+ size_t n = strlen(p);
+ rect((COLS - n)/2, MESSAGEY, n, 1);
+ mvaddstr(MESSAGEY, (COLS - n)/2, p);
+ refresh();
+ usleep(500000);
+ draw();
+ redraw(u);
+}
+
+static void showmap(ui_state *u)
+{
+ const level *l = u->l;
+ int wd, ht, x, y;
+ int vs = 0, hs = 0, maxv, maxh;
+ int i, j;
+ int bit;
+ int c;
+
+ erase();
+ wd = l->w; if (wd + 6 > COLS) wd = COLS - 6;
+ ht = l->h; if (ht + 6 > LINES) ht = LINES - 6;
+ x = (COLS - wd)/2; y = (LINES - ht)/2;
+ rect(x, y, wd, ht);
+ maxh = l->w - wd; maxv = l->h - ht;
+
+ for (;;) {
+ for (j = 0; j < ht; j++) {
+ move(y + j, x);
+ for (i = 0; i < wd; i++) {
+ bit = LF_NWMAP;
+ if (hs + i >= l->w/2) bit <<= 1;
+ if (vs + j >= l->h/2) bit <<= 2;
+ if (!(l->f & bit))
+ c = C_EMPTY;
+ else {
+ c = CELL(l, hs + i, vs + j) & CF_CELLMASK;
+ if (!(cellmap[c]->f & CF_MAP)) c = C_EMPTY;
+ }
+ addch(mdispmap[c]);
+ }
+ }
+ switch (getch()) {
+ case 'q': case 'Q': case 'm': case 'M': case 0x1b: goto done;
+ case KEY_UP: case 'k': case 'K': vs--; break;
+ case KEY_DOWN: case 'j': case 'J': vs++; break;
+ case KEY_LEFT: case 'h': case 'H': hs--; break;
+ case KEY_RIGHT: case 'l': case 'L': hs++; break;
+ }
+ if (hs < 0) hs = 0; else if (hs > maxh) hs = maxh;
+ if (vs < 0) vs = 0; else if (vs > maxv) vs = maxv;
+ }
+done:
+ draw();
+ redraw(u);
+ return;
+}
+
+void ui_go(struct game_state *g, ui_state *u)
+{
+ refresh();
+ switch (getch()) {
+ case KEY_UP: case 'k': case 'K': game_move(g, 0, -1); break;
+ case KEY_DOWN: case 'j': case 'J': game_move(g, 0, +1); break;
+ case KEY_LEFT: case 'h': case 'H': game_move(g, -1, 0); break;
+ case KEY_RIGHT: case 'l': case 'L': game_move(g, +1, 0); break;
+ case 'u': case 'U': undo(g); break;
+ case 'r': case 'R': redo(g); break;
+ case ' ': case '\r': game_switch(g); break;
+ case 'm': case 'M': showmap(u); break;
+ case 0x1b: case 'q': case 'Q': game_quit(g); break;
+ }
+}
+
+void ui_destroy(ui_state *u)
+{
+ DESTROY(u);
+}
+
+void ui_exit(void)
+{
+ endwin();
+}
+
+/*----- That's all, folks -------------------------------------------------*/