Initial checkin. Not there yet.
[xor] / ui-curses.c
CommitLineData
fc27aa70 1/* -*-c-*-
2 *
3 * $Id: ui-curses.c,v 1.1 2003/12/12 10:55:30 mdw Exp $
4 *
5 * Curses user interface
6 *
7 * (c) 2003 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of XOR.
13 *
14 * XOR is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * XOR is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with XOR; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: ui-curses.c,v $
32 * Revision 1.1 2003/12/12 10:55:30 mdw
33 * Initial checkin. Not there yet.
34 *
35 */
36
37/*----- Header files ------------------------------------------------------*/
38
39#include "xor.h"
40
41#if defined(HAVE_NCURSES_H)
42# include <ncurses.h>
43#elif defined(HAVE_NCURSES_NCURSES_H)
44# include <ncurses/ncurses.h>
45#else
46# include <curses.h>
47#endif
48
49#ifdef __CYGWIN__
50# include <windows.h>
51# undef usleep
52# define usleep(n) Sleep(n / 1000);
53#endif
54
55/*----- Data structures ---------------------------------------------------*/
56
57struct ui_state {
58 int vx, vy;
59 level *l;
60};
61
62/*---- Display setup ------------------------------------------------------*/
63
64#define VIEWX 5
65#define VIEWY 3
66#define VIEWWD 8
67#define VIEWHT 8
68#define CELLHT 2
69#define CELLWD 3
70
71#define STATUSX 40
72#define STATUSY 10
73#define STATUSWD 36
74#define STATUSHT 2
75
76#define MESSAGEY 16
77
78static chtype *dispmap[CHAR_MAX], mdispmap[CHAR_MAX];
79
80static struct { int c; chtype m; chtype d[CELLHT * CELLWD]; } disptab[] = {
81 { C_EMPTY, ' ', { ' ', ' ', ' ', ' ', ' ', ' ' } },
82 { C_PLAYER, '$', { '|', '~', '|', '\\', '_', '/' } },
83 { C_SPARE, '%', { '|', 'X', '|', '\\', '_', '/' } },
84 { C_EXIT, '=', { '|', '=', '|', '|', '_', '|' } },
85 { C_WALL, '#', { '#', '#', '#', '#', '#', '#' } },
86 { C_MASK, '+', { 'o', ' ', 'o', ' ', '>', ' ' } },
87 { C_NWMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
88 { C_NEMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
89 { C_SWMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
90 { C_SEMAP, 'M', { '|', '~', '|', '|', '_', '|' } },
91 { C_DOT, ':', { ':', ':', ':', ':', ':', ':' } },
92 { C_WAVE, '~', { '~', '~', '~', '~', '~', '~' } },
93 { C_CHICKEN, '<', { '<', 'o', ')', ' ', '(', ')' } },
94 { C_FISH, 'V', { ' ', 'V', ' ', ' ', 'V', ' ' } },
95 { C_HBOMB, 'U', { '_', 'T', '_', '|', '_', '|' } },
96 { C_VBOMB, 'C', { ' ', ',', '*', '(', ')', ' ' } },
97 { C_DOLLY, '@', { '\\', '@', '/', '/', '=', '\\' } },
98 { C_SWITCH, '~', { 'o', ' ', 'o', ' ', '*', ' ' } },
99 { C_BMUS, 'O', { '/', '~', '\\', '\\', '_', '/' } },
100 { 0, 0, { 0 } }
101};
102
103/*----- Main code ---------------------------------------------------------*/
104
105static void rect(int x, int y, int w, int h)
106{
107 int i;
108
109 mvaddch(y - 1, x - 1, ACS_ULCORNER);
110 for (i = 0; i < w; i++) addch(ACS_HLINE);
111 addch(ACS_URCORNER);
112 for (i = 0; i < h; i++) {
113 mvaddch(y + i, x - 1, ACS_VLINE);
114 mvaddch(y + i, x + w, ACS_VLINE);
115 }
116 mvaddch(y + h, x - 1, ACS_LLCORNER);
117 for (i = 0; i < w; i++) addch(ACS_HLINE);
118 addch(ACS_LRCORNER);
119}
120
121static void hbar(int x, int y, int w)
122{
123 int i;
124
125 move(y, x - 1);
126 addch(ACS_LTEE);
127 for (i = 0; i < w; i++) addch(ACS_HLINE);
128 addch(ACS_RTEE);
129}
130
131static void vbar(int x, int y, int h)
132{
133 int i;
134
135 mvaddch(y - 1, x, ACS_TTEE);
136 for (i = 0; i < h; i++) mvaddch(y + i, x, ACS_VLINE);
137 mvaddch(y + h, x, ACS_BTEE);
138}
139
140static void draw(void)
141{
142 erase();
143 rect(VIEWX, VIEWY, VIEWWD * CELLWD, VIEWHT * CELLHT);
144 rect(STATUSX, STATUSY - 2, STATUSWD, STATUSHT + 2);
145 hbar(STATUSX, STATUSY - 1, STATUSWD);
146 vbar(STATUSX + STATUSWD - 5, STATUSY, STATUSHT);
147}
148
149void ui_init(void)
150{
151 int i;
152
153 /* --- Kick curses --- */
154
155 initscr();
156 if (LINES < 25 || COLS < 80) {
157 endwin();
158 fprintf(stderr, "terminal too small\n");
159 exit(1);
160 }
161 cbreak();
162 noecho();
163 nonl();
164 intrflush(stdscr, FALSE);
165 keypad(stdscr, TRUE);
166 draw();
167
168 /* --- Initialize the display map --- */
169
170 for (i = 0; disptab[i].c; i++) {
171 dispmap[disptab[i].c] = disptab[i].d;
172 mdispmap[disptab[i].c] = disptab[i].m;
173 }
174 for (i = 0; i < CELLWD * CELLHT; i++)
175 dispmap[C_WALL][i] = ' ' | A_REVERSE;
176 mdispmap[C_WALL] = ' ' | A_REVERSE;
177}
178
179static void redraw(ui_state *u)
180{
181 const level *l = u->l;
182 int x, y, i, j, c;
183
184 for (y = 0; y < VIEWHT; y++) {
185 for (j = 0; j < CELLHT; j++) {
186 move(VIEWY + y * CELLHT + j, VIEWX);
187 for (x = 0; x < VIEWWD; x++) {
188 c = CELL(l, u->vx + x, u->vy + y) & CF_CELLMASK;
189 if ((l->f & LF_DARK) && (cellmap[c]->f & CF_HIDE)) c = C_EMPTY;
190 for (i = 0; i < CELLWD; i++) {
191 addch(dispmap[c][i + j * CELLWD]);
192 }
193 }
194 }
195 }
196
197 mvaddstr(STATUSY - 2, STATUSX + (STATUSWD - strlen(u->l->name))/2,
198 u->l->name);
199
200 mvprintw(STATUSY, STATUSX, "Masks: %2d/%2d", l->m, l->mtot);
201 mvprintw(STATUSY + 1, STATUSX, "Moves: %4d/%4d", l->v, l->vtot);
202 move(STATUSY, STATUSX + STATUSWD - 4);
203 if (l->f & LF_NWMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
204 else { addch(' '); addch(' '); }
205 if (l->f & LF_NEMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
206 else { addch(' '); addch(' '); }
207 move(STATUSY + 1, STATUSX + STATUSWD - 4);
208 if (l->f & LF_SWMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
209 else { addch(' '); addch(' '); }
210 if (l->f & LF_SEMAP) { addch(' ' | A_REVERSE); addch(' ' | A_REVERSE); }
211 else { addch(' '); addch(' '); }
212}
213
214void ui_update(ui_state *u)
215{
216 redraw(u);
217 refresh();
218}
219
220void ui_explode(struct ui_state *u, const point *pt, int n)
221{
222 int i, j, x, y;
223 static const chtype frame[][CELLWD * CELLHT] = {
224 { '\\', '|', '/', '/', '|', '\\' },
225 { '*', '*', '*', '*', '*', '*' },
226 { ':', ':', ':', ':', ':', ':' },
227 { '.', '.', '.', ':', ':', ':' },
228 { ' ', ' ', ' ', ':', ':', ':' },
229 { ' ', ' ', ' ', '.', '.', '.' }
230 };
231
232 redraw(u);
233 refresh();
234 for (i = 0; i < sizeof(frame)/sizeof(frame[0]); i++) {
235 for (j = 0; j < n; j++) {
236 if (pt[j].x < u->vx || pt[j].x >= u->vx + VIEWWD ||
237 pt[j].y < u->vy || pt[j].y >= u->vy + VIEWHT)
238 continue;
239 for (y = 0; y < CELLHT; y++) {
240 move(VIEWY + (pt[j].y - u->vy) * CELLHT + y,
241 VIEWX + (pt[j].x - u->vx) * CELLWD);
242 for (x = 0; x < CELLWD; x++)
243 addch(frame[i][x + CELLWD * y]);
244 }
245 }
246 refresh();
247 usleep(10000);
248 }
249}
250
251void ui_track(ui_state *u, int x, int y)
252{
253 if (u->vx == -1) {
254 u->vx = x - VIEWWD/2;
255 u->vy = y - VIEWHT/2;
256 if (u->vx < 0) u->vx = 0;
257 else if (u->vx > u->l->w - VIEWWD) u->vx = u->l->w - VIEWWD;
258 if (u->vy < 0) u->vy = 0;
259 else if (u->vy > u->l->h - VIEWHT) u->vy = u->l->h - VIEWHT;
260 } else {
261 if (u->vx + 1 > x) u->vx = x - 1;
262 else if (u->vx + VIEWWD - 2 < x) u->vx = x - VIEWWD + 2;
263 if (u->vy + 1 > y) u->vy = y - 1;
264 else if (u->vy + VIEWHT - 2 < y) u->vy = y - VIEWHT + 2;
265 }
266}
267
268ui_state *ui_start(level *l, int x, int y)
269{
270 ui_state *u = CREATE(ui_state);
271
272 u->l = l;
273 u->vx = u->vy = -1;
274 ui_track(u, x, y);
275 return (u);
276}
277
278void ui_switch(ui_state *u)
279{
280 redraw(u);
281}
282
283void ui_frame(struct ui_state *u)
284{
285 redraw(u);
286 refresh();
287 usleep(20000);
288}
289
290void ui_message(struct ui_state *u, const char *p)
291{
292 size_t n = strlen(p);
293 rect((COLS - n)/2, MESSAGEY, n, 1);
294 mvaddstr(MESSAGEY, (COLS - n)/2, p);
295 refresh();
296 usleep(500000);
297 draw();
298 redraw(u);
299}
300
301static void showmap(ui_state *u)
302{
303 const level *l = u->l;
304 int wd, ht, x, y;
305 int vs = 0, hs = 0, maxv, maxh;
306 int i, j;
307 int bit;
308 int c;
309
310 erase();
311 wd = l->w; if (wd + 6 > COLS) wd = COLS - 6;
312 ht = l->h; if (ht + 6 > LINES) ht = LINES - 6;
313 x = (COLS - wd)/2; y = (LINES - ht)/2;
314 rect(x, y, wd, ht);
315 maxh = l->w - wd; maxv = l->h - ht;
316
317 for (;;) {
318 for (j = 0; j < ht; j++) {
319 move(y + j, x);
320 for (i = 0; i < wd; i++) {
321 bit = LF_NWMAP;
322 if (hs + i >= l->w/2) bit <<= 1;
323 if (vs + j >= l->h/2) bit <<= 2;
324 if (!(l->f & bit))
325 c = C_EMPTY;
326 else {
327 c = CELL(l, hs + i, vs + j) & CF_CELLMASK;
328 if (!(cellmap[c]->f & CF_MAP)) c = C_EMPTY;
329 }
330 addch(mdispmap[c]);
331 }
332 }
333 switch (getch()) {
334 case 'q': case 'Q': case 'm': case 'M': case 0x1b: goto done;
335 case KEY_UP: case 'k': case 'K': vs--; break;
336 case KEY_DOWN: case 'j': case 'J': vs++; break;
337 case KEY_LEFT: case 'h': case 'H': hs--; break;
338 case KEY_RIGHT: case 'l': case 'L': hs++; break;
339 }
340 if (hs < 0) hs = 0; else if (hs > maxh) hs = maxh;
341 if (vs < 0) vs = 0; else if (vs > maxv) vs = maxv;
342 }
343done:
344 draw();
345 redraw(u);
346 return;
347}
348
349void ui_go(struct game_state *g, ui_state *u)
350{
351 refresh();
352 switch (getch()) {
353 case KEY_UP: case 'k': case 'K': game_move(g, 0, -1); break;
354 case KEY_DOWN: case 'j': case 'J': game_move(g, 0, +1); break;
355 case KEY_LEFT: case 'h': case 'H': game_move(g, -1, 0); break;
356 case KEY_RIGHT: case 'l': case 'L': game_move(g, +1, 0); break;
357 case 'u': case 'U': undo(g); break;
358 case 'r': case 'R': redo(g); break;
359 case ' ': case '\r': game_switch(g); break;
360 case 'm': case 'M': showmap(u); break;
361 case 0x1b: case 'q': case 'Q': game_quit(g); break;
362 }
363}
364
365void ui_destroy(ui_state *u)
366{
367 DESTROY(u);
368}
369
370void ui_exit(void)
371{
372 endwin();
373}
374
375/*----- That's all, folks -------------------------------------------------*/