1 /*
2  * enigma/screen.c - perform screen/keyboard handling. All
3  * interaction with Curses is contained in this module, so an X
4  * front end (for example) could be implemented just by replacing
5  * this one file.
6  *
7  * Copyright 2000 Simon Tatham. All rights reserved.
8  *
9  * Enigma is licensed under the MIT licence. See the file LICENCE for
10  * details.
11  *
12  * - we are all amf -
13  */
14 
15 #include <stdio.h>
16 #include <string.h>
17 #ifdef CURSES_HDR
18 #  include CURSES_HDR
19 #else
20 #  include "curses.h"
21 #endif
22 #include "enigma.h"
23 
24 #ifndef HAS_ATTR_T
25 typedef int attr_t;
26 #endif
27 
28 /*
29  * Attributes.
30  */
31 static struct {
32     int fg, bg, attrs;
33 } attrs[] = {
34     /*
35      * Attributes to use when displaying a level in play.
36      */
37 #define T_WALL_STRAIGHT 0	       /* straight walls: - and | */
38     { COLOR_CYAN, COLOR_BLUE, 0 },
39 #define T_WALL_CORNER 1		       /* corner walls: + */
40     { COLOR_WHITE, COLOR_BLUE, 0 },
41 #define T_WALL_AMORPH 2		       /* amorphous walls: % */
42     { COLOR_CYAN, COLOR_BLUE, 0 },
43 #define T_WALL_KILLER 3		       /* killer walls: & */
44     { COLOR_GREEN, COLOR_BLACK, 0 },
45 #define T_BOMB 4		       /* bombs: W X Y Z */
46     { COLOR_RED, COLOR_BLACK, A_BOLD },
47 #define T_ARROW 5		       /* arrows: > v < ^ */
48     { COLOR_RED, COLOR_BLACK, 0 },
49 #define T_GOLD 6		       /* gold: $ */
50     { COLOR_YELLOW, COLOR_BLACK, A_BOLD },
51 #define T_EARTH 7		       /* earth: . : ! = */
52     { COLOR_YELLOW, COLOR_BLACK, 0 },
53 #define T_SACK 8		       /* sacks: o 8 */
54     { COLOR_CYAN, COLOR_BLACK, 0 },
55 #define T_PLAYER 9		       /* the player: @ */
56     { COLOR_WHITE, COLOR_BLACK, A_BOLD },
57 #define T_TITLE 10		       /* title at top of screen */
58     { COLOR_CYAN, COLOR_BLUE, A_BOLD },
59 #define T_SPACE 11		       /* empty space: ' ' */
60     { COLOR_WHITE, COLOR_BLACK, 0 },
61 #define T_STATUS_1 12		       /* level number and slash at bottom */
62     { COLOR_CYAN, COLOR_BLACK, 0 },
63 #define T_STATUS_2 13		       /* level title and gold counters */
64     { COLOR_YELLOW, COLOR_BLACK, 0 },
65 #define T_TELEPORTER 14		       /* teleporters: # */
66     { COLOR_GREEN, COLOR_BLACK, A_BOLD },
67 
68     /*
69      * Attributes to use when displaying the main game menu.
70      */
71 #define T_ASCII_ART 15    	       /* the Enigma banner */
72     { COLOR_CYAN, COLOR_BLUE, A_BOLD },
73 #define T_LIST_ELEMENT 16	       /* a playable level or saved game */
74     { COLOR_WHITE, COLOR_BLACK, 0 },
75 #define T_LIST_SELECTED 17	       /* a selected level or saved game */
76     { COLOR_WHITE, COLOR_BLUE, A_BOLD },
77 #define T_LIST_ADMIN 18		       /* "...more..." or "...remain unseen" */
78     { COLOR_RED, COLOR_BLACK, 0 },
79 #define T_LIST_BOX 19		       /* outline of list box */
80     { COLOR_CYAN, COLOR_BLUE, 0 },
81 #define T_INSTRUCTIONS 20	       /* instructions on what keys to press */
82     { COLOR_YELLOW, COLOR_BLACK, 0 },
83 
84 #define T_INPUT 21		       /* accepting text input from user */
85     { COLOR_WHITE, COLOR_BLACK, 0 },
86 };
87 
screen_init(void)88 void screen_init(void) {
89     int i;
90 
91     initscr();
92     noecho();
93     keypad(stdscr, 1);
94     move(0,0);
95     refresh();
96     if (has_colors()) {
97 	start_color();
98 	for (i = 0; i < (int)lenof(attrs); i++) {
99 	    init_pair(i+1, attrs[i].fg, attrs[i].bg);
100 	    attrs[i].attrs |= COLOR_PAIR(i+1);
101 	}
102     }
103 }
104 
screen_finish(void)105 void screen_finish(void) {
106     endwin();
107 }
108 
screen_prints(int x,int y,int attr,char * string)109 void screen_prints(int x, int y, int attr, char *string) {
110     wattrset(stdscr, attrs[attr].attrs);
111     wmove(stdscr, y, x);
112     waddstr(stdscr, string);
113 }
114 
screen_printc(int x,int y,int attr,int c)115 void screen_printc(int x, int y, int attr, int c) {
116     wattrset(stdscr, attrs[attr].attrs);
117     wmove(stdscr, y, x);
118     waddch(stdscr, c);
119 }
120 
screen_level_init(void)121 void screen_level_init(void) {
122     /*
123      * Hook to make a windowing port of Enigma easier. This is
124      * called when the player begins to play a level, and
125      * screen_level_finish() is called when the level is finished.
126      * The idea is that these routines can be reimplemented to open
127      * and close a window of some sort.
128      */
129 
130     /* In this implementation, we do nothing. */
131 }
132 
screen_level_finish(void)133 void screen_level_finish(void) {
134     /*
135      * This is called after the last game position (GAME OVER,
136      * COMPLETED, ABANDONED) has been displayed. Its task is to
137      * close the window.
138      */
139 
140     /* In this implementation, we do nothing. */
141 }
142 
screen_level_display(gamestate * s,char * message)143 void screen_level_display(gamestate *s, char *message) {
144     int sw, sh;
145     int i, j;
146     int dx, dy;
147     char buf[20];
148 
149     getmaxyx(stdscr, sh, sw);
150 
151     werase(stdscr);
152 
153     if (sw < s->width || sh < s->height+2) {
154 	char buf[3][40];
155 	int i;
156 	sprintf(buf[0], "Need terminal at least");
157 	sprintf(buf[1], "%dx%d characters to", s->width, s->height+2);
158 	sprintf(buf[2], "to display this level!");
159 
160 	for (i = 0; i < 3; i++)
161 	    screen_prints((sw - strlen(buf[i]))/2, sh/2 + i - 1, T_BOMB,
162 			   buf[i]);
163 
164 	move(0,0);
165 	refresh();
166 	return;
167     }
168 
169     dx = (sw - s->width) / 2;
170     dy = (sh - s->height) / 2;
171 
172     for (j = 0; j < s->height; j++)
173 	for (i = 0; i < s->width; i++) {
174 	    char c = s->leveldata[j*s->width+i];
175 	    attr_t attr = 0;
176 	    switch (c) {
177 	      case '-': case '|':      attr = T_WALL_STRAIGHT; break;
178 	      case '+':                attr = T_WALL_CORNER; break;
179 	      case '%':                attr = T_WALL_AMORPH; break;
180 	      case '&':                attr = T_WALL_KILLER; break;
181 	      case 'W': case 'X':
182 	      case 'Y': case 'Z':
183 	      case 'w': case 'x':
184 	      case 'y': case 'z':      attr = T_BOMB; break;
185 	      case '>': case 'v':
186 	      case '<': case '^':      attr = T_ARROW; break;
187 	      case '$':                attr = T_GOLD; break;
188 	      case '.': case ':':
189 	      case '!': case '=':      attr = T_EARTH; break;
190 	      case 'o': case '8':      attr = T_SACK; break;
191 	      case '@': case 'O':      attr = T_PLAYER; break;
192 	      case '#':                attr = T_TELEPORTER; break;
193 	      case '~':                c = ' '; attr = T_SPACE; break;
194 	      default:                 attr = T_SPACE; break;
195 	    }
196 	    screen_printc(i+dx, j+dy, attr, c);
197 	}
198 
199     /*
200      * Display title.
201      */
202     i = 8;
203     if (message) i += 3 + strlen(message);
204     screen_prints((sw-i)/2, dy-1, T_TITLE, " Enigma ");
205     if (message) {
206 	screen_prints((sw-i)/2+8, dy-1, T_TITLE, "- ");
207 	screen_prints((sw-i)/2+10, dy-1, T_TITLE, message);
208 	screen_prints((sw-i)/2+i-1, dy-1, T_TITLE, " ");
209     }
210 
211     /*
212      * Display status line.
213      */
214     sprintf(buf, "%d) ", s->levnum);
215     screen_prints(dx, dy+s->height, T_STATUS_1, buf);
216     screen_prints(dx+strlen(buf), dy+s->height, T_STATUS_2, s->title);
217     sprintf(buf, "%2d", s->gold_got);
218     screen_prints(dx+s->width-5, dy+s->height, T_STATUS_2, buf);
219     screen_printc(dx+s->width-3, dy+s->height, T_STATUS_1, '/');
220     sprintf(buf, "%2d", s->gold_total);
221     screen_prints(dx+s->width-2, dy+s->height, T_STATUS_2, buf);
222 
223     move(0,0);
224     refresh();
225 }
226 
227 /*
228  * Get a move. Can return 'h','j','k','l','x', or 'q', or '0'-'9'
229  * for saves, or 's', 'm' (enter movie mode) and 'w' (save
230  * sequence).
231  */
screen_level_getmove(int playing)232 int screen_level_getmove(int playing) {
233     int i;
234     do {
235 	i = getch();
236 	if (playing) {
237 	    if (i == KEY_UP) i = 'k';
238 	    if (i == KEY_DOWN) i = 'j';
239 	    if (i == KEY_LEFT) i = 'h';
240 	    if (i == KEY_RIGHT) i = 'l';
241 	    if (i >= 'A' && i <= 'Z')
242 		i += 'a' - 'A';
243 	} else if (i != 'w' && i != 'r') {
244 	    /* When the playing is over, most keys just quit. */
245 	    i = 'q';
246 	}
247     } while (i != 'h' && i != 'j' && i != 'k' && i != 'l' && i != 'x' &&
248 	     i != 'q' && i != 's' && i != 'r' && (i < '0' || i > '9') &&
249 	     i != 'm' && i != 'w');
250     return i;
251 }
252 
253 /*
254  * Get a keypress in movie mode. Can return 'f' or 'b', '+' or '-',
255  * '>' or '<', or 'q'.
256  */
screen_movie_getmove(void)257 int screen_movie_getmove(void) {
258     int i;
259     do {
260 	i = getch();
261 	if (i >= 'A' && i <= 'Z')
262 	    i += 'a' - 'A';
263 	if (i == ' ') i = 'f';
264 	if (i == KEY_LEFT) i = '-';
265 	if (i == KEY_RIGHT) i = '+';
266 	if (i == '\033') i = 'q';
267     } while (i != 'f' && i != 'b' && i != '+' && i != '-' &&
268 	     i != '>' && i != '<' && i != 'q');
269     return i;
270 }
271 
272 /*
273  * Format a save slot into a string.
274  */
saveslot_fmt(char * buf,int slotnum,gamestate * gs)275 void saveslot_fmt(char *buf, int slotnum, gamestate *gs) {
276     if (gs) {
277 	sprintf(buf, "%d) Level:%3d Moves:%4d   ", (slotnum+1)%10,
278 		gs->levnum, gs->movenum);
279     } else {
280 	sprintf(buf, "%d) [empty save slot]      ", (slotnum+1)%10);
281     }
282 }
283 
284 /*
285  * Display the levels-or-saves main menu. Returns a level number (1
286  * or greater), a save number (0 to -9), a save number to delete
287  * (-10 to -19), or a `quit' signal (-100).
288  */
screen_main_menu(levelset * set,gamestate ** saves,int maxlev,int startlev)289 int screen_main_menu(levelset *set, gamestate **saves,
290 		     int maxlev, int startlev) {
291     const int colwidth = 26, colgap = 8;
292     const int height = 21, llines = height-6;
293     int sx, sy, dx, dy, dx2;
294     int save = 0;
295     int level;
296     int levtop = 0;
297     int i, k;
298     int unseen;
299 
300     getmaxyx(stdscr, sy, sx);
301 
302     werase(stdscr);
303 
304     if (maxlev > set->nlevels) {
305 	/*
306 	 * User has completed game. Different defaults.
307 	 */
308 	maxlev = set->nlevels;
309 	startlev = 0;
310     }
311     level = startlev;
312     if (level == set->nlevels-1)
313 	levtop = level - (llines-1);
314     else
315 	levtop = level - (llines-2);
316     if (levtop < 0)
317 	levtop = 0;
318 
319     dx = (sx - 2*colwidth - colgap) / 2;
320     dy = (sy - height) / 2;
321     dx2 = dx + colwidth + colgap;
322 
323     while (1) {
324 	/*
325 	 * Display the ASCII art banner in the top right.
326 	 */
327 	screen_prints(dx2, dy+0, T_ASCII_ART, "                          ");
328 	screen_prints(dx2, dy+1, T_ASCII_ART, "    ._                    ");
329 	screen_prints(dx2, dy+2, T_ASCII_ART, "    |_ ._ o _ ._ _  _.    ");
330 	screen_prints(dx2, dy+3, T_ASCII_ART, "    |_ | ||(_|| | |(_|    ");
331 	screen_prints(dx2, dy+4, T_ASCII_ART, "            _|            ");
332 	screen_prints(dx2, dy+5, T_ASCII_ART, "                          ");
333 
334 	/*
335 	 * Display the saved position list.
336 	 */
337 	for (i = 0; i < 10; i++) {
338 	    char buf[40];
339 	    saveslot_fmt(buf, i, saves[i]);
340 	    screen_prints(dx2, dy+7+i,
341 			  i == save ? T_LIST_SELECTED : T_LIST_ELEMENT, buf);
342 	}
343 
344 	/*
345 	 * Display the level title list box.
346 	 */
347 	wattrset(stdscr, attrs[T_LIST_BOX].attrs);
348 	for (i = 1; i < colwidth+1; i++) {
349 	    screen_printc(dx+i, dy, T_LIST_BOX, '-');
350 	    screen_printc(dx+i, dy+height-5, T_LIST_BOX, '-');
351 	}
352 	screen_printc(dx, dy, T_LIST_BOX, '+');
353 	screen_printc(dx, dy+height-5, T_LIST_BOX, '+');
354 	screen_printc(dx+colwidth+1, dy, T_LIST_BOX, '+');
355 	screen_printc(dx+colwidth+1, dy+height-5, T_LIST_BOX, '+');
356 	for (i = 1; i < height-5; i++) {
357 	    char buf[50];
358 	    int attr = T_LIST_ELEMENT;
359 	    unseen = FALSE;
360 	    if (i == 1 && levtop > 0) {
361 		sprintf(buf, "(more)");
362 		attr = T_LIST_ADMIN;
363 	    } else if (i+levtop-1 == maxlev && maxlev < set->nlevels) {
364 		sprintf(buf, "(%d remain%s unseen)",
365 			set->nlevels - maxlev,
366 			set->nlevels - maxlev == 1 ? "s" : "");
367 		unseen = TRUE;
368 		attr = T_LIST_ADMIN;
369 	    } else if (i == llines && i+levtop-1 < maxlev-1 && !unseen) {
370 		sprintf(buf, "(more)");
371 		attr = T_LIST_ADMIN;
372 	    } else {
373 		if (i+levtop-1 < maxlev)
374 		    sprintf(buf, "%2d) %.40s", i+levtop,
375 			    set->levels[i+levtop-1]->title);
376 		else
377 		    *buf = '\0';
378 		if (i+levtop-1 == level)
379 		    attr = T_LIST_SELECTED;
380 	    }
381 	    if ((int)strlen(buf) < colwidth)
382 		sprintf(buf+strlen(buf), "%*s", colwidth-strlen(buf), "");
383 	    buf[colwidth] = '\0';
384 	    screen_prints(dx+1, dy+i, attr, buf);
385 	    screen_printc(dx, dy+i, T_LIST_BOX, '|');
386 	    screen_printc(dx+colwidth+1, dy+i, T_LIST_BOX, '|');
387 	}
388 	screen_prints(dx, dy+height-3, T_INSTRUCTIONS, "Press Up/Down to choose a");
389 	screen_prints(dx, dy+height-2, T_INSTRUCTIONS, "level, and RET to play from");
390 	screen_prints(dx, dy+height-1, T_INSTRUCTIONS, "the start of that level.");
391 	screen_prints(dx, dy+height, T_INSTRUCTIONS, "Press Q to exit Enigma.");
392 	screen_prints(dx2+colwidth-26, dy+height-3, T_INSTRUCTIONS, "Press 1-9 or 0 to choose a");
393 	screen_prints(dx2+colwidth-24, dy+height-2, T_INSTRUCTIONS, "saved position, and R to");
394 	screen_prints(dx2+colwidth-26, dy+height-1, T_INSTRUCTIONS, "resume playing from there.");
395 	screen_prints(dx2+colwidth-27, dy+height, T_INSTRUCTIONS, "Press Del to delete a save.");
396 
397 	move(0,0);
398 	refresh();
399 	k = getch();
400 	if (k >= 'A' && k <= 'Z') k += 'a'-'A';
401 	if (k >= '0' && k <= '9')
402 	    save = (k - '1' + 10) % 10;
403 	if ((k == 'k' || k == KEY_UP) && level > 0) {
404 	    level--;
405 	    if (!(level > levtop || (level == 0 && levtop == 0)))
406 		levtop = (level == 0 ? level : level-1);
407 	}
408 	if ((k == 'j' || k == KEY_DOWN) && level < maxlev-1) {
409 	    level++;
410 	    if (!(level < levtop+llines-1 || (level == levtop+llines-1 &&
411 					      level == set->nlevels-1))) {
412 		if (level == set->nlevels-1)
413 		    levtop = level - (llines-1);
414 		else
415 		    levtop = level - (llines-2);
416 	    }
417 	}
418 	if (k == 'q')
419 	    return -100;
420 	if (k == '\r' || k == '\n')
421 	    return level+1;
422 	if (k == 'r')
423 	    return -save;
424 	if (k == '\010' || k == '\177' || k == KEY_BACKSPACE)
425 	    return -save-10;	       /* delete a save */
426     }
427 }
428 
screen_saveslot_ask(char action,gamestate ** saves,int defslot)429 int screen_saveslot_ask(char action, gamestate **saves, int defslot) {
430     const int width = 28;
431     const int height = 14;
432     int sx, sy, dx, dy;
433     int i, k;
434     char buf[50];
435 
436     getmaxyx(stdscr, sy, sx);
437     dx = (sx - width) / 2;
438     dy = (sy - height) / 2;
439 
440     while (1) {
441 	for (i = 1; i < width-1; i++) {
442 	    screen_printc(dx+i, dy, T_LIST_BOX, '-');
443 	    screen_printc(dx+i, dy+height-1, T_LIST_BOX, '-');
444 	}
445 	for (i = 1; i < height-1; i++) {
446 	    screen_printc(dx, dy+i, T_LIST_BOX, '|');
447 	    screen_printc(dx+width-1, dy+i, T_LIST_BOX, '|');
448 	}
449 	screen_printc(dx, dy, T_LIST_BOX, '+');
450 	screen_printc(dx, dy+height-1, T_LIST_BOX, '+');
451 	screen_printc(dx+width-1, dy, T_LIST_BOX, '+');
452 	screen_printc(dx+width-1, dy+height-1, T_LIST_BOX, '+');
453 	screen_prints(dx+1, dy+1, T_INSTRUCTIONS,
454 		      "Press 0-9 to pick a slot  ");
455 	screen_prints(dx+1, dy+height-2, T_INSTRUCTIONS,
456 		      (action == 's' ?
457 		       "   Y to save over slot X  " :
458 		       "Y to restore from slot X  "));
459 	screen_printc(dx+24, dy+height-2, T_INSTRUCTIONS, '0'+(defslot+1)%10);
460 	for (i = 0; i < 10; i++) {
461 	    saveslot_fmt(buf, i, saves[i]);
462 	    screen_prints(dx+1, dy+i+2,
463 			  i == defslot ? T_LIST_SELECTED : T_LIST_ELEMENT,
464 			  buf);
465 	}
466 	move(0,0);
467 	refresh();
468 	k = getch();
469 	if (k >= 'A' && k <= 'Q') {
470 	    k += 'a'-'A';
471 	}
472 	if (k >= '0' && k <= '9') {
473 	    defslot = (k+9-'0') % 10;
474 	}
475 	if (k == 'y' && (action == 's' || saves[defslot]))
476 	    return defslot;
477 	if (k == 'n' || k == 'q')
478 	    return -1;
479     }
480 }
481 
screen_ask_movefile(int saving)482 char *screen_ask_movefile(int saving) {
483     const int width = 40;
484     const int height = 4;
485     int sx, sy, dx, dy;
486     int i, k;
487     char buf[50];
488     int len;
489     char *p;
490 
491     getmaxyx(stdscr, sy, sx);
492     dx = (sx - width) / 2;
493     dy = (sy - height) / 2;
494 
495     len = 0;
496 
497     while (1) {
498 	for (i = 1; i < width-1; i++) {
499 	    screen_printc(dx+i, dy, T_LIST_BOX, '-');
500 	    screen_printc(dx+i, dy+height-1, T_LIST_BOX, '-');
501 	    screen_printc(dx+i, dy+1, T_INSTRUCTIONS, ' ');
502 	    screen_printc(dx+i, dy+2, T_INPUT, ' ');
503 	}
504 	for (i = 1; i < height-1; i++) {
505 	    screen_printc(dx, dy+i, T_LIST_BOX, '|');
506 	    screen_printc(dx+width-1, dy+i, T_LIST_BOX, '|');
507 	}
508 	screen_printc(dx, dy, T_LIST_BOX, '+');
509 	screen_printc(dx, dy+height-1, T_LIST_BOX, '+');
510 	screen_printc(dx+width-1, dy, T_LIST_BOX, '+');
511 	screen_printc(dx+width-1, dy+height-1, T_LIST_BOX, '+');
512 	if (saving) {
513 	    screen_prints(dx+1, dy+1, T_INSTRUCTIONS,
514 			  "Enter a move sequence file to save:");
515 	} else {
516 	    screen_prints(dx+1, dy+1, T_INSTRUCTIONS,
517 			  "Enter a move sequence file to load:");
518 	}
519 	buf[len] = '\0';
520 	screen_prints(dx+1, dy+2, T_INPUT, buf);
521 	move(dy+2, dx+1+len);
522 	refresh();
523 	k = getch();
524 	if (k == '\r' || k == '\n')
525 	    break;
526 	else if (k == '\e')
527 	    return NULL;	       /* input abandoned */
528 	else if (k == '\010' || k == '\177' || k == KEY_BACKSPACE)
529 	    len = (len>0 ? len-1 : 0);
530 	else if (k == '\025')
531 	    len = 0;
532 	else if (k >= '\040' && k <= '\176' && len < width-2)
533 	    buf[len++] = (char)k;
534     }
535     if (!len)
536 	return NULL;
537     buf[len] = '\0';
538     p = smalloc(len+1);
539     strcpy(p, buf);
540     return p;
541 }
542 
screen_error_box(char * msg)543 void screen_error_box(char *msg) {
544     int width = 2 + strlen(msg);
545     const int height = 3;
546     int sx, sy, dx, dy;
547     int i;
548     int len;
549 
550     getmaxyx(stdscr, sy, sx);
551     dx = (sx - width) / 2;
552     dy = (sy - height) / 2;
553 
554     len = 0;
555 
556     for (i = 1; i < width-1; i++) {
557 	screen_printc(dx+i, dy, T_LIST_BOX, '-');
558 	screen_printc(dx+i, dy+height-1, T_LIST_BOX, '-');
559     }
560     for (i = 1; i < height-1; i++) {
561 	screen_printc(dx, dy+i, T_LIST_BOX, '|');
562 	screen_printc(dx+width-1, dy+i, T_LIST_BOX, '|');
563     }
564     screen_printc(dx, dy, T_LIST_BOX, '+');
565     screen_printc(dx, dy+height-1, T_LIST_BOX, '+');
566     screen_printc(dx+width-1, dy, T_LIST_BOX, '+');
567     screen_printc(dx+width-1, dy+height-1, T_LIST_BOX, '+');
568     screen_prints(dx+1, dy+1, T_INSTRUCTIONS, msg);
569     move(0,0);
570     refresh();
571     getch();
572 }
573 
screen_completed_game(void)574 void screen_completed_game(void) {
575     int sx, sy;
576 
577     getmaxyx(stdscr, sy, sx);
578 
579     werase(stdscr);
580     screen_prints((sx-51)/2, sy/2-2, T_INSTRUCTIONS,
581 		  "Congratulations! You have completed this level set.");
582     screen_prints((sx-49)/2, sy/2, T_INSTRUCTIONS,
583 		  "Now why not become an Enigma developer, and write");
584     screen_prints((sx-48)/2, sy/2+1, T_INSTRUCTIONS,
585 		  "some levels of your own, or perhaps even help me");
586     screen_prints((sx-32)/2, sy/2+2, T_INSTRUCTIONS,
587 		  "write a better finishing screen?");
588     screen_prints((sx-15)/2, sy-1, T_INSTRUCTIONS,
589 		  "(Press any key)");
590     move(0,0);
591     refresh();
592     getch();
593 }
594