3 * $Id: ui-curses.c,v 1.1 2003/12/12 10:55:30 mdw Exp $
5 * Curses user interface
7 * (c) 2003 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of XOR.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
31 * $Log: ui-curses.c,v $
32 * Revision 1.1 2003/12/12 10:55:30 mdw
33 * Initial checkin. Not there yet.
37 /*----- Header files ------------------------------------------------------*/
41 #if defined(HAVE_NCURSES_H)
43 #elif defined(HAVE_NCURSES_NCURSES_H)
44 # include <ncurses/ncurses.h>
52 # define usleep(n) Sleep(n / 1000);
55 /*----- Data structures ---------------------------------------------------*/
62 /*---- Display setup ------------------------------------------------------*/
78 static chtype
*dispmap
[CHAR_MAX
], mdispmap
[CHAR_MAX
];
80 static 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', { '/', '~', '\\', '\\', '_', '/' } },
103 /*----- Main code ---------------------------------------------------------*/
105 static void rect(int x
, int y
, int w
, int h
)
109 mvaddch(y
- 1, x
- 1, ACS_ULCORNER
);
110 for (i
= 0; i
< w
; i
++) addch(ACS_HLINE
);
112 for (i
= 0; i
< h
; i
++) {
113 mvaddch(y
+ i
, x
- 1, ACS_VLINE
);
114 mvaddch(y
+ i
, x
+ w
, ACS_VLINE
);
116 mvaddch(y
+ h
, x
- 1, ACS_LLCORNER
);
117 for (i
= 0; i
< w
; i
++) addch(ACS_HLINE
);
121 static void hbar(int x
, int y
, int w
)
127 for (i
= 0; i
< w
; i
++) addch(ACS_HLINE
);
131 static void vbar(int x
, int y
, int h
)
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
);
140 static void draw(void)
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
);
153 /* --- Kick curses --- */
156 if (LINES
< 25 || COLS
< 80) {
158 fprintf(stderr
, "terminal too small\n");
164 intrflush(stdscr
, FALSE
);
165 keypad(stdscr
, TRUE
);
168 /* --- Initialize the display map --- */
170 for (i
= 0; disptab
[i
].c
; i
++) {
171 dispmap
[disptab
[i
].c
] = disptab
[i
].d
;
172 mdispmap
[disptab
[i
].c
] = disptab
[i
].m
;
174 for (i
= 0; i
< CELLWD
* CELLHT
; i
++)
175 dispmap
[C_WALL
][i
] = ' ' | A_REVERSE
;
176 mdispmap
[C_WALL
] = ' ' | A_REVERSE
;
179 static void redraw(ui_state
*u
)
181 const level
*l
= u
->l
;
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
]);
197 mvaddstr(STATUSY
- 2, STATUSX
+ (STATUSWD
- strlen(u
->l
->name
))/2,
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(' '); }
214 void ui_update(ui_state
*u
)
220 void ui_explode(struct ui_state
*u
, const point
*pt
, int n
)
223 static const chtype frame
[][CELLWD
* CELLHT
] = {
224 { '\\', '|', '/', '/', '|', '\\' },
225 { '*', '*', '*', '*', '*', '*' },
226 { ':', ':', ':', ':', ':', ':' },
227 { '.', '.', '.', ':', ':', ':' },
228 { ' ', ' ', ' ', ':', ':', ':' },
229 { ' ', ' ', ' ', '.', '.', '.' }
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
)
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
]);
251 void ui_track(ui_state
*u
, int x
, int y
)
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
;
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;
268 ui_state
*ui_start(level
*l
, int x
, int y
)
270 ui_state
*u
= CREATE(ui_state
);
278 void ui_switch(ui_state
*u
)
283 void ui_frame(struct ui_state
*u
)
290 void ui_message(struct ui_state
*u
, const char *p
)
292 size_t n
= strlen(p
);
293 rect((COLS
- n
)/2, MESSAGEY
, n
, 1);
294 mvaddstr(MESSAGEY
, (COLS
- n
)/2, p
);
301 static void showmap(ui_state
*u
)
303 const level
*l
= u
->l
;
305 int vs
= 0, hs
= 0, maxv
, maxh
;
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;
315 maxh
= l
->w
- wd
; maxv
= l
->h
- ht
;
318 for (j
= 0; j
< ht
; j
++) {
320 for (i
= 0; i
< wd
; i
++) {
322 if (hs
+ i
>= l
->w
/2) bit
<<= 1;
323 if (vs
+ j
>= l
->h
/2) bit
<<= 2;
327 c
= CELL(l
, hs
+ i
, vs
+ j
) & CF_CELLMASK
;
328 if (!(cellmap
[c
]->f
& CF_MAP
)) c
= C_EMPTY
;
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;
340 if (hs
< 0) hs
= 0; else if (hs
> maxh
) hs
= maxh
;
341 if (vs
< 0) vs
= 0; else if (vs
> maxv
) vs
= maxv
;
349 void ui_go(struct game_state
*g
, ui_state
*u
)
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;
365 void ui_destroy(ui_state
*u
)
375 /*----- That's all, folks -------------------------------------------------*/