1 #include "curses.h"
2 #include "hack.h"
3 #include "wincurs.h"
4 #include "cursinit.h"
5 #include "patchlevel.h"
6 
7 #include <ctype.h>
8 
9 /* Initialization and startup functions for curses interface */
10 
11 /* Private declarations */
12 
13 #define NETHACK_CURSES      1
14 #define SLASHEM_CURSES      2
15 #define UNNETHACK_CURSES    3
16 #define SPORKHACK_CURSES    4
17 
18 /* array to save initial terminal colors for later restoration */
19 
20 typedef struct nhrgb_type
21 {
22     short r;
23     short g;
24     short b;
25 } nhrgb;
26 
27 nhrgb orig_yellow;
28 nhrgb orig_white;
29 nhrgb orig_darkgray;
30 nhrgb orig_hired;
31 nhrgb orig_higreen;
32 nhrgb orig_hiyellow;
33 nhrgb orig_hiblue;
34 nhrgb orig_himagenta;
35 nhrgb orig_hicyan;
36 nhrgb orig_hiwhite;
37 
38 /* Banners used for an optional ASCII splash screen */
39 
40 #define NETHACK_SPLASH_A \
41 " _   _        _    _    _               _    "
42 
43 #define NETHACK_SPLASH_B \
44 "| \\ | |      | |  | |  | |             | |   "
45 
46 #define NETHACK_SPLASH_C \
47 "|  \\| |  ___ | |_ | |__| |  __ _   ___ | | __"
48 
49 #define NETHACK_SPLASH_D \
50 "| . ` | / _ \\| __||  __  | / _` | / __|| |/ /"
51 
52 #define NETHACK_SPLASH_E \
53 "| |\\  ||  __/| |_ | |  | || (_| || (__ |   < "
54 
55 #define NETHACK_SPLASH_F \
56 "|_| \\_| \\___| \\__||_|  |_| \\__,_| \\___||_|\\_\\"
57 
58 #define SLASHEM_SPLASH_A \
59 " _____  _              _     _  ______  __  __ "
60 
61 #define SLASHEM_SPLASH_B \
62 " / ____|| |            | |   ( )|  ____||  \\/  |"
63 
64 #define SLASHEM_SPLASH_C \
65 "| (___  | |  __ _  ___ | |__  \\|| |__   | \\  / |"
66 
67 #define SLASHEM_SPLASH_D \
68 " \\___ \\ | | / _` |/ __|| '_ \\   |  __|  | |\\/| |"
69 
70 #define SLASHEM_SPLASH_E \
71 " ____) || || (_| |\\__ \\| | | |  | |____ | |  | |"
72 
73 #define SLASHEM_SPLASH_F \
74 "|_____/ |_| \\__,_||___/|_| |_|  |______||_|  |_|"
75 
76 #define UNNETHACK_SPLASH_A \
77 " _    _         _   _        _    _    _               _"
78 
79 #define UNNETHACK_SPLASH_B \
80 "| |  | |       | \\ | |      | |  | |  | |             | |"
81 
82 #define UNNETHACK_SPLASH_C \
83 "| |  | | _ __  |  \\| |  ___ | |_ | |__| |  __ _   ___ | | __"
84 
85 #define UNNETHACK_SPLASH_D \
86 "| |  | || '_ \\ | . ` | / _ \\| __||  __  | / _` | / __|| |/ /"
87 
88 #define UNNETHACK_SPLASH_E \
89 "| |__| || | | || |\\  ||  __/| |_ | |  | || (_| || (__ |   <"
90 
91 #define UNNETHACK_SPLASH_F \
92 " \\____/ |_| |_||_| \\_| \\___| \\__||_|  |_| \\__,_| \\___||_|\\_\\"
93 
94 #define SPORKHACK_SPLASH_A \
95 "  _____                      _     _    _               _    "
96 #define SPORKHACK_SPLASH_B \
97 " / ____|                    | |   | |  | |             | |   "
98 #define SPORKHACK_SPLASH_C \
99 "| (___   _ __    ___   _ __ | | __| |__| |  __ _   ___ | | __"
100 #define SPORKHACK_SPLASH_D \
101 " \\___ \\ | '_ \\  / _ \\ | '__|| |/ /|  __  | / _` | / __|| |/ /"
102 #define SPORKHACK_SPLASH_E \
103 " ____) || |_) || (_) || |   |   < | |  | || (_| || (__ |   < "
104 #define SPORKHACK_SPLASH_F \
105 "|_____/ | .__/  \\___/ |_|   |_|\\_\\|_|  |_| \\__,_| \\___||_|\\_\\"
106 #define SPORKHACK_SPLASH_G \
107 "        | |                                                  "
108 #define SPORKHACK_SPLASH_H \
109 "        |_|                                                  "
110 
111 
112 /* Create the "main" nonvolitile windows used by nethack */
113 
curses_create_main_windows()114 void curses_create_main_windows()
115 {
116     int message_x = 0;
117     int message_y = 0;
118     int status_x = 0;
119     int status_y = 0;
120     int map_x = 0;
121     int map_y = 0;
122     int message_height = 0;
123     int message_width = 0;
124     int status_height = 0;
125     int status_width = 0;
126     int map_height = 0;
127     int map_width = 0;
128     int min_message_height = 1;
129     int message_orientation = 0;
130     int status_orientation = 0;
131     int border_space = 0;
132     int hspace = term_cols - 80;
133     boolean borders = FALSE;
134 
135     switch (iflags.wc2_windowborders)
136     {
137         case 1: /* On */
138         {
139             borders = TRUE;
140             break;
141         }
142         case 2: /* Off */
143         {
144             borders = FALSE;
145             break;
146         }
147         case 3: /* Auto */
148         {
149             if ((term_cols > 81) && (term_rows > 25))
150             {
151                 borders = TRUE;
152             }
153             break;
154         }
155         default:
156         {
157             borders = FALSE;
158         }
159     }
160 
161 
162     if (borders)
163     {
164         border_space = 2;
165         hspace -= border_space;
166     }
167 
168     if ((term_cols - border_space) < COLNO)
169     {
170         min_message_height++;
171     }
172 
173     /* Determine status window orientation */
174     if (!iflags.wc_align_status || (iflags.wc_align_status == ALIGN_TOP)
175      || (iflags.wc_align_status == ALIGN_BOTTOM))
176     {
177         if (!iflags.wc_align_status)
178         {
179             iflags.wc_align_status = ALIGN_BOTTOM;
180         }
181         status_orientation = iflags.wc_align_status;
182     }
183     else    /* left or right alignment */
184     {
185         /* Max space for player name and title horizontally */
186         if ((hspace >= 26) && (term_rows >= 24))
187         {
188             status_orientation = iflags.wc_align_status;
189             hspace -= (26 + border_space);
190         }
191         else
192         {
193             status_orientation = ALIGN_BOTTOM;
194         }
195     }
196 
197     /* Determine message window orientation */
198     if (!iflags.wc_align_message || (iflags.wc_align_message == ALIGN_TOP)
199      || (iflags.wc_align_message == ALIGN_BOTTOM))
200     {
201         if (!iflags.wc_align_message)
202         {
203             iflags.wc_align_message = ALIGN_TOP;
204         }
205         message_orientation = iflags.wc_align_message;
206     }
207     else    /* left or right alignment */
208     {
209         if ((hspace - border_space) >= 25)   /* Arbitrary */
210         {
211             message_orientation = iflags.wc_align_message;
212         }
213         else
214         {
215             message_orientation = ALIGN_TOP;
216         }
217     }
218 
219     /* Determine window placement and size - 16 possible combos
220        If anyone wants to try to generalize this, be my guest! */
221     if ((status_orientation == ALIGN_TOP) &&
222      (message_orientation == ALIGN_TOP))
223     {
224         status_x = 0;
225         status_y = 0;
226         status_width = (term_cols - border_space);
227         status_height = 2;
228         message_x = 0;
229         message_y = status_y + (status_height + border_space);
230         message_width = (term_cols - border_space);
231         message_height = term_rows - (status_height + ROWNO + (border_space * 3));
232         if (message_height < min_message_height)
233         {
234             message_height = min_message_height;
235         }
236         map_x = 0;
237         map_y = message_y + (message_height + border_space);
238         map_width = (term_cols - border_space);
239         map_height = term_rows - (status_height + message_height + (border_space * 3));
240     }
241     else if ((status_orientation == ALIGN_TOP) &&
242      (message_orientation == ALIGN_RIGHT))
243     {
244         status_x = 0;
245         status_y = 0;
246         status_height = 2;
247         message_height = (term_rows - border_space);
248         message_width = term_cols - (COLNO + (border_space * 2));
249         status_width = term_cols - (message_width + (border_space * 2));
250         message_x = status_x + (status_width + border_space);
251         message_y = 0;
252         map_x = 0;
253         map_y = status_y + (status_height + border_space);
254         map_width = status_width;
255         map_height = term_rows - (status_height + (border_space * 2));
256     }
257     else if ((status_orientation == ALIGN_TOP) &&
258      (message_orientation == ALIGN_BOTTOM))
259     {
260         status_x = 0;
261         status_y = 0;
262         status_width = (term_cols - border_space);
263         status_height = 2;
264         map_x = 0;
265         map_y = status_y + (status_height + border_space);
266         map_width = (term_cols - border_space);
267         message_height = term_rows - (status_height + ROWNO + (border_space * 3));
268         if (message_height < min_message_height)
269         {
270             message_height = min_message_height;
271         }
272         map_height = term_rows - (status_height + message_height + (border_space * 3));
273         message_x = 0;
274         message_y = map_y + (map_height + border_space);
275         message_width = (term_cols - border_space);
276     }
277     else if ((status_orientation == ALIGN_TOP) &&
278      (message_orientation == ALIGN_LEFT))
279     {
280         message_x = 0;
281         message_y = 0;
282         message_height = (term_rows - border_space);
283         message_width = term_cols - (COLNO + (border_space * 2));
284         status_x = message_x + (message_width + border_space);
285         status_y = 0;
286         status_height = 2;
287         status_width = term_cols - (message_width + (border_space * 2));
288         map_x = status_x;
289         map_y = status_y + (status_height + border_space);
290         map_height = term_rows - (status_height + (border_space * 2));
291         map_width = status_width;
292     }
293     if ((status_orientation == ALIGN_RIGHT) &&
294      (message_orientation == ALIGN_TOP))
295     {
296         status_width = 26;
297         status_height = (term_rows - border_space);
298         status_x = term_cols - (status_width + border_space);
299         status_y = 0;
300         message_x = 0;
301         message_y = 0;
302         message_width = term_cols - (status_width + (border_space * 2));
303         message_height = term_rows - (ROWNO + (border_space * 2));
304         if (message_height < min_message_height)
305         {
306             message_height = min_message_height;
307         }
308         map_x = 0;
309         map_y = message_y + (message_height + border_space);
310         map_width = term_cols - (status_width + (border_space * 2));
311         map_height = term_rows - (message_height + (border_space * 2));
312     }
313     else if ((status_orientation == ALIGN_RIGHT) &&
314      (message_orientation == ALIGN_RIGHT))
315     {
316         map_x = 0;
317         map_y = 0;
318         map_height = (term_rows - border_space);
319         status_width = 26;
320         message_width = term_cols - (COLNO + status_width + (border_space * 3));
321         map_width = term_cols - (status_width + message_width + (border_space * 3));
322         message_x = map_x + (map_width + border_space);
323         message_y = 0;
324         message_height = (term_rows - border_space);
325         status_x = message_x + (message_width + border_space);
326         status_y = 0;
327         status_height = (term_rows - border_space);
328     }
329     else if ((status_orientation == ALIGN_RIGHT) &&
330      (message_orientation == ALIGN_BOTTOM))
331     {
332         map_x = 0;
333         map_y = 0;
334         status_width = 26;
335         map_width = term_cols - (status_width + (border_space * 2));
336         message_height = term_rows - (ROWNO + (border_space * 2));
337         if (message_height < min_message_height)
338         {
339             message_height = min_message_height;
340         }
341         map_height = term_rows - (message_height + (border_space * 2));
342         message_x = 0;
343         message_y = map_y + (map_height + border_space);
344         message_width = map_width;
345         status_x = map_x + (map_width + border_space);
346         status_y = 0;
347         status_height = (term_rows - border_space);
348     }
349     else if ((status_orientation == ALIGN_RIGHT) &&
350      (message_orientation == ALIGN_LEFT))
351     {
352         status_x = 0;
353         status_y = 0;
354         status_height = (term_rows - border_space);
355         status_width = 26;
356         message_width = term_cols - (status_width + COLNO + (border_space * 3));
357         map_x = status_x + (status_width + border_space);
358         map_y = 0;
359         map_height = (term_rows - border_space);
360         map_width = term_cols - (status_width + message_width + (border_space * 3));
361         message_x = map_x + (map_width + border_space);
362         message_y = 0;
363         message_height = (term_rows - border_space);
364     }
365     if ((status_orientation == ALIGN_BOTTOM) &&
366      (message_orientation == ALIGN_TOP))
367     {
368         message_x = 0;
369         message_y = 0;
370         message_width = (term_cols - border_space);
371         status_height = 2;
372         message_height = term_rows - (status_height + ROWNO + (border_space * 3));
373         if (message_height < min_message_height)
374         {
375             message_height = min_message_height;
376         }
377         map_x = 0;
378         map_y = message_y + (message_height + border_space);
379         map_width = (term_cols - border_space);
380         map_height = term_rows - (status_height + message_height + (border_space * 3));
381         status_x = 0;
382         status_y = map_y + (map_height + border_space);
383         status_width = (term_cols - border_space);
384     }
385     else if ((status_orientation == ALIGN_BOTTOM) &&
386      (message_orientation == ALIGN_RIGHT))
387     {
388         map_x = 0;
389         map_y = 0;
390         status_height = 2;
391         map_height = term_rows - (status_height + (border_space * 2));
392         message_width = term_cols - (COLNO + (border_space * 2));
393         map_width = term_cols - (message_width + (border_space * 2));
394         status_x = 0;
395         status_y = map_y + (map_height + border_space);
396         status_width = map_width;
397         message_x = map_x + (map_width + border_space);
398         message_y = 0;
399         message_height = (term_rows - border_space);
400     }
401     else if ((status_orientation == ALIGN_BOTTOM) &&
402      (message_orientation == ALIGN_BOTTOM))
403     {
404         map_x = 0;
405         map_y = 0;
406         message_x = 0;
407         status_x = 0;
408         message_width = (term_cols - border_space);
409         status_height = 2;
410         message_height = term_rows - (status_height + ROWNO + (border_space * 3));
411         if (message_height < min_message_height)
412         {
413             message_height = min_message_height;
414         }
415         map_width = (term_cols - border_space);
416         map_height = term_rows - (status_height + message_height + (border_space * 3));
417         message_y = map_y + (map_height + border_space);
418         status_y = message_y + (message_height + border_space);
419         status_width = (term_cols - border_space);
420     }
421     else if ((status_orientation == ALIGN_BOTTOM) &&
422      (message_orientation == ALIGN_LEFT))
423     {
424         message_x = 0;
425         message_y = 0;
426         message_height = (term_rows - border_space);
427         message_width = term_cols - (COLNO + (border_space * 2));
428         status_height = 2;
429         map_x = message_x + (message_width + border_space);
430         map_y = 0;
431         map_height = term_rows - (status_height + (border_space * 2));
432         map_width = term_cols - (message_width + (border_space * 2));
433         status_x = map_x;
434         status_y = map_y + (map_height + border_space);
435         status_width = term_cols - (message_width + (border_space * 2));
436     }
437     if ((status_orientation == ALIGN_LEFT) &&
438      (message_orientation == ALIGN_TOP))
439     {
440         status_x = 0;
441         status_y = 0;
442         status_height = (term_rows - border_space);
443         status_width = 26;
444         message_x = status_x + (status_width + border_space);
445         message_y = 0;
446         message_height = term_rows - (ROWNO + (border_space * 2));
447         if (message_height < min_message_height)
448         {
449             message_height = min_message_height;
450         }
451         message_width = term_cols - (status_width + (border_space * 2));
452         map_x = message_x;
453         map_y = message_y + (message_height + border_space);
454         map_height = term_rows - (message_height + (border_space * 2));
455         map_width = term_cols - (status_width + (border_space * 2));
456     }
457     else if ((status_orientation == ALIGN_LEFT) &&
458      (message_orientation == ALIGN_RIGHT))
459     {
460         message_x = 0;
461         message_y = 0;
462         message_height = (term_rows - border_space);
463         status_width = 26;
464         message_width = term_cols - (status_width + COLNO + (border_space * 3));
465         map_x = message_x + (message_width + border_space);
466         map_y = 0;
467         map_height = (term_rows - border_space);
468         map_width = term_cols - (status_width + message_width + (border_space * 3));
469         status_x = map_x + (map_width + border_space);
470         status_y = 0;
471         status_height = (term_rows - border_space);
472     }
473     else if ((status_orientation == ALIGN_LEFT) &&
474      (message_orientation == ALIGN_BOTTOM))
475     {
476         status_x = 0;
477         status_y = 0;
478         status_height = (term_rows - border_space);
479         status_width = 26;
480         map_x = status_x + (status_width + border_space);
481         map_y = 0;
482         message_height = term_rows - (ROWNO + (border_space * 2));
483         if (message_height < min_message_height)
484         {
485             message_height = min_message_height;
486         }
487         map_height = term_rows - (message_height + (border_space * 2));
488         map_width = term_cols - (status_width + (border_space * 2));
489         message_x = status_x + (status_width + border_space);
490         message_y = map_y + (map_height + border_space);
491         message_width = map_width;
492     }
493     else if ((status_orientation == ALIGN_LEFT) &&
494      (message_orientation == ALIGN_LEFT))
495     {
496         status_x = 0;
497         status_y = 0;
498         status_height = (term_rows - border_space);
499         status_width = 26;
500         message_x = status_x + (status_width + border_space);
501         message_y = 0;
502         message_height = status_height;
503         message_width = term_cols - (COLNO + status_width + (border_space * 3));
504         map_x = message_x + (message_width + border_space);
505         map_y = 0;
506         map_height = message_height;
507         map_width = term_cols - (status_width + message_width + (border_space * 3));
508     }
509 
510     if (map_width > COLNO)
511     {
512         map_width = COLNO;
513     }
514 
515     if (map_height > ROWNO)
516     {
517         map_height = ROWNO;
518     }
519 
520     if (curses_window_exists(STATUS_WIN))
521     {
522         curses_del_nhwin(STATUS_WIN);
523         curses_del_nhwin(MESSAGE_WIN);
524         curses_del_nhwin(MAP_WIN);
525         clear();
526     }
527 
528     curses_add_nhwin(STATUS_WIN, status_height, status_width, status_y,
529      status_x, status_orientation, borders);
530 
531     curses_add_nhwin(MESSAGE_WIN, message_height, message_width, message_y,
532      message_x, message_orientation, borders);
533 
534     curses_add_nhwin(MAP_WIN, map_height, map_width, map_y, map_x, 0,
535      borders);
536 
537     refresh();
538 
539     curses_refresh_nethack_windows();
540 
541     if (iflags.window_inited)
542     {
543         curses_update_stats(TRUE);
544     }
545     else
546     {
547         iflags.window_inited = TRUE;
548     }
549 }
550 
551 
552 /* Initialize curses colors to colors used by NetHack */
553 
curses_init_nhcolors()554 void curses_init_nhcolors()
555 {
556 #ifdef TEXTCOLOR
557     if (has_colors())
558     {
559         use_default_colors();
560         init_pair(1, COLOR_BLACK, -1);
561         init_pair(2, COLOR_RED, -1);
562         init_pair(3, COLOR_GREEN, -1);
563         init_pair(4, COLOR_YELLOW, -1);
564         init_pair(5, COLOR_BLUE, -1);
565         init_pair(6, COLOR_MAGENTA, -1);
566         init_pair(7, COLOR_CYAN, -1);
567         init_pair(8, -1, -1);
568 
569         if (COLORS >= 16)
570         {
571             init_pair(9, COLOR_WHITE, -1);
572             init_pair(10, COLOR_RED + 8, -1);
573             init_pair(11, COLOR_GREEN + 8, -1);
574             init_pair(12, COLOR_YELLOW + 8, -1);
575             init_pair(13, COLOR_BLUE + 8, -1);
576             init_pair(14, COLOR_MAGENTA + 8, -1);
577             init_pair(15, COLOR_CYAN + 8, -1);
578             init_pair(16, COLOR_WHITE + 8, -1);
579         }
580 
581         if (can_change_color())
582         {
583             /* Preserve initial terminal colors */
584             color_content(COLOR_YELLOW, &orig_yellow.r, &orig_yellow.g,
585              &orig_yellow.b);
586             color_content(COLOR_WHITE, &orig_white.r, &orig_white.g,
587              &orig_white.b);
588 
589             /* Set colors to appear as NetHack expects */
590             init_color(COLOR_YELLOW, 500, 300, 0);
591             init_color(COLOR_WHITE, 600, 600, 600);
592             if (COLORS >= 16)
593             {
594                 /* Preserve initial terminal colors */
595                 color_content(COLOR_RED + 8, &orig_hired.r,
596                  &orig_hired.g, &orig_hired.b);
597                 color_content(COLOR_GREEN + 8, &orig_higreen.r,
598                  &orig_higreen.g, &orig_higreen.b);
599                 color_content(COLOR_YELLOW + 8, &orig_hiyellow.r,
600                  &orig_hiyellow.g, &orig_hiyellow.b);
601                 color_content(COLOR_BLUE + 8, &orig_hiblue.r,
602                  &orig_hiblue.g, &orig_hiblue.b);
603                 color_content(COLOR_MAGENTA + 8, &orig_himagenta.r,
604                  &orig_himagenta.g, &orig_himagenta.b);
605                 color_content(COLOR_CYAN + 8, &orig_hicyan.r,
606                  &orig_hicyan.g, &orig_hicyan.b);
607                 color_content(COLOR_WHITE + 8, &orig_hiwhite.r,
608                  &orig_hiwhite.g, &orig_hiwhite.b);
609 
610                 /* Set colors to appear as NetHack expects */
611                 init_color(COLOR_RED + 8, 1000, 500, 0);
612                 init_color(COLOR_GREEN + 8, 0, 1000, 0);
613                 init_color(COLOR_YELLOW + 8, 1000, 1000, 0);
614                 init_color(COLOR_BLUE + 8, 0, 0, 1000);
615                 init_color(COLOR_MAGENTA + 8, 1000, 0, 1000);
616                 init_color(COLOR_CYAN + 8, 0, 1000, 1000);
617                 init_color(COLOR_WHITE + 8, 1000, 1000, 1000);
618 #ifdef USE_DARKGRAY
619                 if (COLORS > 16)
620                 {
621                     init_color(CURSES_DARK_GRAY, 300, 300, 300);
622                 }
623 #endif
624             }
625             else
626             {
627                 /* Set flag to use bold for bright colors */
628             }
629         }
630     }
631 #endif
632 }
633 
634 
635 /* Allow player to pick character's role, race, gender, and alignment.
636 Borrowed from the Gnome window port. */
637 
curses_choose_character()638 void curses_choose_character()
639 {
640     int n, i, sel, count_off, pick4u;
641     int count = 0;
642     int cur_character = 0;
643     const char** choices;
644     int* pickmap;
645     char *prompt;
646     char pbuf[QBUFSZ];
647     char choice[QBUFSZ];
648     char tmpchoice[QBUFSZ];
649 #ifdef TUTORIAL_MODE
650 	winid win;
651 	anything any;
652 	menu_item *selected = 0;
653 #endif
654 
655 	prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
656 	 flags.initrace, flags.initgend, flags.initalign);
657 
658     /* This part is irritating: we have to strip the choices off of
659     the string and put them in a separate string in order to use
660     curses_character_input_dialog for this prompt. */
661 
662     while (cur_character != '[')
663     {
664         cur_character = prompt[count];
665         count++;
666     }
667 
668     count_off = count;
669 
670     while (cur_character != ']')
671     {
672         tmpchoice[count - count_off] = prompt[count];
673         count++;
674         cur_character = prompt[count];
675     }
676 
677     tmpchoice[count - count_off] = '\0';
678     lcase(tmpchoice);
679 
680     while (!isspace(prompt[count_off]))
681     {
682         count_off--;
683     }
684 
685     prompt[count_off] = '\0';
686     sprintf(choice, "%s%c", tmpchoice, '\033');
687     if(strchr(tmpchoice, 't'))  /* Tutorial mode */
688     {
689         mvaddstr(0, 1, "New? Press t to enter a tutorial.");
690     }
691 
692     /* Add capital letters as choices that aren't displayed */
693 
694     for (count = 0; tmpchoice[count]; count++)
695     {
696         tmpchoice[count] = toupper(tmpchoice[count]);
697     }
698 
699     sprintf(choice, "%s%s", choice, tmpchoice);
700 
701     /* prevent an unnecessary prompt */
702     rigid_role_checks();
703 
704 	if (!flags.randomall &&
705 	    (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
706 	     flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE))
707 	{
708         pick4u = tolower(curses_character_input_dialog(prompt, choice,
709          'y'));
710     }
711     else
712     {
713         pick4u = 'y';
714     }
715 
716     if (pick4u == 'q')  /* Quit or cancelled */
717     {
718 	    clearlocks();
719 	    curses_bail(0);
720     }
721 
722     if (pick4u == 'y')
723     {
724         flags.randomall = TRUE;
725     }
726 #ifdef TUTORIAL_MODE
727     else if (pick4u == 't') /* Tutorial mode in UnNetHack */
728     {
729 	    clear();
730         mvaddstr(0, 1, "Choose a character");
731         refresh();
732 	    win = curses_get_wid(NHW_MENU);
733         curses_create_nhmenu(win);
734 	    any.a_int = 1;
735 	    curses_add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, 'v', 0, ATR_NONE,
736 		     "lawful female dwarf Valkyrie (uses melee and thrown weapons)",
737 		     MENU_UNSELECTED);
738 	    any.a_int = 2;
739 	    curses_add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, 'w', 0, ATR_NONE,
740 		     "chaotic male elf Wizard (relies mostly on spells)",
741 		     MENU_UNSELECTED);
742 	    any.a_int = 3;
743 	    curses_add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, 'R', 0, ATR_NONE,
744 		     "neutral female human Ranger (good with ranged combat)",
745 		     MENU_UNSELECTED);
746 	    any.a_int = 4;
747 	    curses_add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, 'q', 0, ATR_NONE,
748 		     "quit", MENU_UNSELECTED);
749 	    curses_end_menu(win, "What character do you want to try?");
750 	    n = curses_select_menu(win, PICK_ONE, &selected);
751 	    destroy_nhwindow(win);
752 	    if (n != 1 || selected[0].item.a_int == 4)
753 	    {
754     	    clearlocks();
755     	    curses_bail(0);
756 	    }
757 	    switch (selected[0].item.a_int) {
758 	    case 1:
759 		flags.initrole = str2role("Valkyrie");
760 		flags.initrace = str2race("dwarf");
761 		flags.initgend = str2gend("female");
762 		flags.initalign = str2align("lawful");
763 		break;
764 	    case 2:
765 		flags.initrole = str2role("Wizard");
766 		flags.initrace = str2race("elf");
767 		flags.initgend = str2gend("male");
768 		flags.initalign = str2align("chaotic");
769 		break;
770 	    case 3:
771 		flags.initrole = str2role("Ranger");
772 		flags.initrace = str2race("human");
773 		flags.initgend = str2gend("female");
774 		flags.initalign = str2align("neutral");
775 		break;
776 	    default: panic("Impossible menu selection"); break;
777 	    }
778 	    free((genericptr_t) selected);
779 	    selected = 0;
780 	    flags.tutorial = 1;
781 	}
782 #endif
783 
784     clear();
785     refresh();
786 
787     if (!flags.randomall && flags.initrole < 0) {
788 	/* select a role */
789 	for (n = 0; roles[n].name.m; n++) continue;
790 	choices = (const char **)alloc(sizeof(char *) * (n+1));
791 	pickmap = (int*)alloc(sizeof(int) * (n+1));
792 	for (;;) {
793 	    for (n = 0, i = 0; roles[i].name.m; i++) {
794 		if (ok_role(i, flags.initrace,
795 			    flags.initgend, flags.initalign)) {
796 		    if (flags.initgend >= 0 && flags.female && roles[i].name.f)
797 			choices[n] = roles[i].name.f;
798 		    else
799 			choices[n] = roles[i].name.m;
800 		    pickmap[n++] = i;
801 		}
802 	    }
803 	    if (n > 0) break;
804 	    else if (flags.initalign >= 0) flags.initalign = -1;    /* reset */
805 	    else if (flags.initgend >= 0) flags.initgend = -1;
806 	    else if (flags.initrace >= 0) flags.initrace = -1;
807 	    else panic("no available ROLE+race+gender+alignment combinations");
808 	}
809 	choices[n] = (const char *) 0;
810 	if (n > 1)
811         sel = curses_character_dialog(choices, "Choose one of the following roles:");
812 	else sel = 0;
813 	if (sel >= 0) sel = pickmap[sel];
814 	else if (sel == ROLE_NONE) {		/* Quit */
815 	    clearlocks();
816 	    curses_bail(0);
817 	}
818 	free(choices);
819 	free(pickmap);
820     } else if (flags.initrole < 0) sel = ROLE_RANDOM;
821     else sel = flags.initrole;
822 
823     if (sel == ROLE_RANDOM) {	/* Random role */
824 	sel = pick_role(flags.initrace, flags.initgend,
825 			  flags.initalign, PICK_RANDOM);
826 	if (sel < 0) sel = randrole();
827     }
828 
829     flags.initrole = sel;
830 
831     /* Select a race, if necessary */
832     /* force compatibility with role, try for compatibility with
833      * pre-selected gender/alignment */
834     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
835 	if (flags.initrace == ROLE_RANDOM || flags.randomall) {
836 	    flags.initrace = pick_race(flags.initrole, flags.initgend,
837 				       flags.initalign, PICK_RANDOM);
838 	    if (flags.initrace < 0) flags.initrace = randrace(flags.initrole);
839 	} else {
840 	    /* Count the number of valid races */
841 	    n = 0;	/* number valid */
842 	    for (i = 0; races[i].noun; i++) {
843 		if (ok_race(flags.initrole, i, flags.initgend, flags.initalign))
844 		    n++;
845 	    }
846 	    if (n == 0) {
847 		for (i = 0; races[i].noun; i++) {
848 		    if (validrace(flags.initrole, i)) n++;
849 		}
850 	    }
851 
852 	    choices = (const char **)alloc(sizeof(char *) * (n+1));
853 	    pickmap = (int*)alloc(sizeof(int) * (n + 1));
854 	    for (n = 0, i = 0; races[i].noun; i++) {
855 		if (ok_race(flags.initrole, i, flags.initgend,
856 			    flags.initalign)) {
857 		    choices[n] = races[i].noun;
858 		    pickmap[n++] = i;
859 		}
860 	    }
861 	    choices[n] = (const char *) 0;
862 	    /* Permit the user to pick, if there is more than one */
863 	    if (n > 1)
864 		sel = curses_character_dialog(choices, "Choose one of the following races:");
865 	    else sel = 0;
866 	    if (sel >= 0) sel = pickmap[sel];
867 	    else if (sel == ROLE_NONE) { /* Quit */
868 		clearlocks();
869 		curses_bail(0);
870 	    }
871 	    flags.initrace = sel;
872 	    free(choices);
873 	    free(pickmap);
874 	}
875 	if (flags.initrace == ROLE_RANDOM) {	/* Random role */
876 	    sel = pick_race(flags.initrole, flags.initgend,
877 			    flags.initalign, PICK_RANDOM);
878 	    if (sel < 0) sel = randrace(flags.initrole);
879 	    flags.initrace = sel;
880 	}
881     }
882 
883     /* Select a gender, if necessary */
884     /* force compatibility with role/race, try for compatibility with
885      * pre-selected alignment */
886     if (flags.initgend < 0 ||
887 	!validgend(flags.initrole, flags.initrace, flags.initgend)) {
888 	if (flags.initgend == ROLE_RANDOM || flags.randomall) {
889 	    flags.initgend = pick_gend(flags.initrole, flags.initrace,
890 				       flags.initalign, PICK_RANDOM);
891 	    if (flags.initgend < 0)
892 		flags.initgend = randgend(flags.initrole, flags.initrace);
893 	} else {
894 	    /* Count the number of valid genders */
895 	    n = 0;	/* number valid */
896 	    for (i = 0; i < ROLE_GENDERS; i++) {
897 		if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign))
898 		    n++;
899 	    }
900 	    if (n == 0) {
901 		for (i = 0; i < ROLE_GENDERS; i++) {
902 		    if (validgend(flags.initrole, flags.initrace, i)) n++;
903 		}
904 	    }
905 
906 	    choices = (const char **)alloc(sizeof(char *) * (n+1));
907 	    pickmap = (int*)alloc(sizeof(int) * (n + 1));
908 	    for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
909 		if (ok_gend(flags.initrole, flags.initrace, i,
910 				flags.initalign)) {
911 		    choices[n] = genders[i].adj;
912 		    pickmap[n++] = i;
913 		}
914 	    }
915 	    choices[n] = (const char *) 0;
916 	    /* Permit the user to pick, if there is more than one */
917 	    if (n > 1)
918 		sel = curses_character_dialog(choices, "Choose one of the following genders:");
919 	    else sel = 0;
920 	    if (sel >= 0) sel = pickmap[sel];
921 	    else if (sel == ROLE_NONE) { /* Quit */
922 		clearlocks();
923 		curses_bail(0);
924 	    }
925 	    flags.initgend = sel;
926 	    free(choices);
927 	    free(pickmap);
928 	}
929 	if (flags.initgend == ROLE_RANDOM) {	/* Random gender */
930 	    sel = pick_gend(flags.initrole, flags.initrace,
931 			    flags.initalign, PICK_RANDOM);
932 	    if (sel < 0) sel = randgend(flags.initrole, flags.initrace);
933 	    flags.initgend = sel;
934 	}
935     }
936 
937     /* Select an alignment, if necessary */
938     /* force compatibility with role/race/gender */
939     if (flags.initalign < 0 ||
940 	!validalign(flags.initrole, flags.initrace, flags.initalign)) {
941 	if (flags.initalign == ROLE_RANDOM || flags.randomall) {
942 	    flags.initalign = pick_align(flags.initrole, flags.initrace,
943 					 flags.initgend, PICK_RANDOM);
944 	    if (flags.initalign < 0)
945 		flags.initalign = randalign(flags.initrole, flags.initrace);
946 	} else {
947 	    /* Count the number of valid alignments */
948 	    n = 0;	/* number valid */
949 	    for (i = 0; i < ROLE_ALIGNS; i++) {
950 		if (ok_align(flags.initrole, flags.initrace, flags.initgend, i))
951 		    n++;
952 	    }
953 	    if (n == 0) {
954 		for (i = 0; i < ROLE_ALIGNS; i++)
955 		    if (validalign(flags.initrole, flags.initrace, i)) n++;
956 	    }
957 
958 	    choices = (const char **)alloc(sizeof(char *) * (n+1));
959 	    pickmap = (int*)alloc(sizeof(int) * (n + 1));
960 	    for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
961 		if (ok_align(flags.initrole,
962 			     flags.initrace, flags.initgend, i)) {
963 		    choices[n] = aligns[i].adj;
964 		    pickmap[n++] = i;
965 		}
966 	    }
967 	    choices[n] = (const char *) 0;
968 	    /* Permit the user to pick, if there is more than one */
969 	    if (n > 1)
970 		sel = curses_character_dialog(choices, "Choose one of the following alignments:");
971 	    else sel = 0;
972 	    if (sel >= 0) sel = pickmap[sel];
973 	    else if (sel == ROLE_NONE) { /* Quit */
974 		clearlocks();
975 		curses_bail(0);
976 	    }
977 	    flags.initalign = sel;
978 	    free(choices);
979 	    free(pickmap);
980 	}
981 	if (flags.initalign == ROLE_RANDOM) {
982 	    sel = pick_align(flags.initrole, flags.initrace,
983 			     flags.initgend, PICK_RANDOM);
984 	    if (sel < 0) sel = randalign(flags.initrole, flags.initrace);
985 	    flags.initalign = sel;
986 	}
987     }
988 }
989 
990 
991 /* Prompt user for character race, role, alignment, or gender */
992 
curses_character_dialog(const char ** choices,const char * prompt)993 int curses_character_dialog(const char** choices, const char *prompt)
994 {
995     int count, count2, ret, curletter;
996     char used_letters[52];
997     anything identifier;
998     menu_item *selected = NULL;
999     winid wid = curses_get_wid(NHW_MENU);
1000 
1001     identifier.a_void = 0;
1002     curses_start_menu(wid);
1003 
1004     for (count=0; choices[count]; count++)
1005     {
1006         curletter=tolower(choices[count][0]);
1007         for (count2=0; count2<count; count2++)
1008         {
1009             if (curletter==used_letters[count2])
1010             {
1011                 curletter=toupper(curletter);
1012             }
1013         }
1014 
1015         identifier.a_int = (count + 1); /* Must be non-zero */
1016         curses_add_menu(wid, NO_GLYPH, MENU_DEFCNT, &identifier, curletter, 0,
1017          A_NORMAL, choices[count], FALSE);
1018         used_letters[count] = curletter;
1019     }
1020 
1021     /* Random Selection */
1022     identifier.a_int = ROLE_RANDOM;
1023     curses_add_menu(wid, NO_GLYPH, MENU_DEFCNT, &identifier, '*', 0, A_NORMAL, "Random",
1024      FALSE);
1025 
1026     /* Quit prompt */
1027     identifier.a_int = ROLE_NONE;
1028     curses_add_menu(wid, NO_GLYPH, MENU_DEFCNT, &identifier, 'q', 0, A_NORMAL, "Quit",
1029      FALSE);
1030     curses_end_menu(wid, prompt);
1031     ret = curses_select_menu(wid, PICK_ONE, &selected);
1032     if (ret == 1)
1033     {
1034         ret = (selected->item.a_int);
1035     }
1036     else    /* Cancelled selection */
1037     {
1038         ret = ROLE_NONE;
1039     }
1040 
1041     if (ret > 0)
1042     {
1043         ret--;
1044     }
1045 
1046     free(selected);
1047     return ret;
1048 }
1049 
1050 
1051 /* Initialize and display options appropriately */
1052 
curses_init_options()1053 void curses_init_options()
1054 {
1055     set_wc_option_mod_status(WC_ALIGN_MESSAGE|WC_ALIGN_STATUS|WC_COLOR|
1056      WC_HILITE_PET|WC_POPUP_DIALOG, SET_IN_GAME);
1057 
1058     set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_GAME);
1059 
1060     /* Remove a few options that are irrelevant to this windowport */
1061     set_option_mod_status("DECgraphics", SET_IN_FILE);
1062     set_option_mod_status("perm_invent", SET_IN_FILE);
1063     set_option_mod_status("eight_bit_tty", SET_IN_FILE);
1064 
1065     /* Make sure that DECgraphics is not set to true via the config
1066     file, as this will cause display issues.  We can't disable it in
1067     options.c in case the game is compiled with both tty and curses.*/
1068     if (iflags.DECgraphics)
1069     {
1070         switch_graphics(CURS_GRAPHICS);
1071     }
1072 
1073 #ifdef PDCURSES
1074     /* PDCurses for SDL, win32 and OS/2 has the ability to set the
1075      terminal size programatically.  If the user does not specify a
1076      size in the config file, we will set it to a nice big 110x32 to
1077      take advantage of some of the nice features of this windowport. */
1078     if (iflags.wc2_term_cols == 0)
1079     {
1080         iflags.wc2_term_cols = 110;
1081     }
1082 
1083     if (iflags.wc2_term_rows == 0)
1084     {
1085         iflags.wc2_term_rows = 32;
1086     }
1087 
1088     resize_term(iflags.wc2_term_rows, iflags.wc2_term_cols);
1089     getmaxyx(base_term, term_rows, term_cols);
1090 
1091     /* This is needed for an odd bug with PDCurses-SDL */
1092     switch_graphics(ASCII_GRAPHICS);
1093     if (iflags.IBMgraphics)
1094     {
1095         switch_graphics(IBM_GRAPHICS);
1096     }
1097     else if (iflags.cursesgraphics)
1098     {
1099         switch_graphics(CURS_GRAPHICS);
1100     }
1101     else
1102     {
1103         switch_graphics(ASCII_GRAPHICS);
1104     }
1105 #endif  /* PDCURSES */
1106     if (!iflags.wc2_windowborders)
1107     {
1108         iflags.wc2_windowborders = 3; /* Set to auto if not specified */
1109     }
1110 
1111     if (!iflags.wc2_petattr)
1112     {
1113         iflags.wc2_petattr = A_REVERSE;
1114     }
1115     else    /* Pet attribute specified, so hilite_pet should be true */
1116     {
1117         iflags.hilite_pet = TRUE;
1118     }
1119 
1120 #ifdef NCURSES_MOUSE_VERSION
1121     if (iflags.wc_mouse_support)
1122     {
1123     	mousemask(BUTTON1_CLICKED, NULL);
1124     }
1125 #endif
1126 }
1127 
1128 
1129 /* Display an ASCII splash screen if the splash_screen option is set */
1130 
curses_display_splash_window()1131 void curses_display_splash_window()
1132 {
1133     int x_start = 1;
1134     int y_start = 6;
1135     int which_variant = NETHACK_CURSES;  /* Default to NetHack */
1136 
1137     if ((term_cols < 70) || (term_rows < 20))
1138     {
1139         iflags.wc_splash_screen = FALSE;    /* No room for s.s. */
1140     }
1141 
1142 #ifdef DEF_GAME_NAME
1143     if (strcmp(DEF_GAME_NAME, "SlashEM") == 0)
1144     {
1145         which_variant = SLASHEM_CURSES;
1146     }
1147 #endif
1148 
1149 #ifdef GAME_SHORT_NAME
1150     if (strcmp(GAME_SHORT_NAME, "UNH") == 0)
1151     {
1152         which_variant = UNNETHACK_CURSES;
1153     }
1154 #endif
1155 
1156     if (strncmp("SporkHack", COPYRIGHT_BANNER_A, 9) == 0)
1157     {
1158         which_variant = SPORKHACK_CURSES;
1159     }
1160 
1161     curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, ON);
1162 
1163     if (iflags.wc_splash_screen)
1164     {
1165         switch (which_variant)
1166         {
1167             case NETHACK_CURSES:
1168             {
1169                 mvaddstr(y_start, x_start, NETHACK_SPLASH_A);
1170                 mvaddstr(y_start + 1, x_start, NETHACK_SPLASH_B);
1171                 mvaddstr(y_start + 2, x_start, NETHACK_SPLASH_C);
1172                 mvaddstr(y_start + 3, x_start, NETHACK_SPLASH_D);
1173                 mvaddstr(y_start + 4, x_start, NETHACK_SPLASH_E);
1174                 mvaddstr(y_start + 5, x_start, NETHACK_SPLASH_F);
1175                 y_start += 7;
1176                 break;
1177             }
1178             case SLASHEM_CURSES:
1179             {
1180                 mvaddstr(y_start, x_start, SLASHEM_SPLASH_A);
1181                 mvaddstr(y_start + 1, x_start, SLASHEM_SPLASH_B);
1182                 mvaddstr(y_start + 2, x_start, SLASHEM_SPLASH_C);
1183                 mvaddstr(y_start + 3, x_start, SLASHEM_SPLASH_D);
1184                 mvaddstr(y_start + 4, x_start, SLASHEM_SPLASH_E);
1185                 mvaddstr(y_start + 5, x_start, SLASHEM_SPLASH_F);
1186                 y_start += 7;
1187                 break;
1188             }
1189             case UNNETHACK_CURSES:
1190             {
1191                 mvaddstr(y_start, x_start, UNNETHACK_SPLASH_A);
1192                 mvaddstr(y_start + 1, x_start, UNNETHACK_SPLASH_B);
1193                 mvaddstr(y_start + 2, x_start, UNNETHACK_SPLASH_C);
1194                 mvaddstr(y_start + 3, x_start, UNNETHACK_SPLASH_D);
1195                 mvaddstr(y_start + 4, x_start, UNNETHACK_SPLASH_E);
1196                 mvaddstr(y_start + 5, x_start, UNNETHACK_SPLASH_F);
1197                 y_start += 7;
1198                 break;
1199             }
1200             case SPORKHACK_CURSES:
1201                 mvaddstr(y_start, x_start, SPORKHACK_SPLASH_A);
1202                 mvaddstr(y_start + 1, x_start, SPORKHACK_SPLASH_B);
1203                 mvaddstr(y_start + 2, x_start, SPORKHACK_SPLASH_C);
1204                 mvaddstr(y_start + 3, x_start, SPORKHACK_SPLASH_D);
1205                 mvaddstr(y_start + 4, x_start, SPORKHACK_SPLASH_E);
1206                 mvaddstr(y_start + 5, x_start, SPORKHACK_SPLASH_F);
1207                 mvaddstr(y_start + 6, x_start, SPORKHACK_SPLASH_G);
1208                 mvaddstr(y_start + 7, x_start, SPORKHACK_SPLASH_H);
1209                 y_start += 9;
1210                 break;
1211             default:
1212             {
1213                 impossible("which_variant number %d out of range",
1214                  which_variant);
1215             }
1216         }
1217     }
1218 
1219     curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, OFF);
1220 
1221 #ifdef COPYRIGHT_BANNER_A
1222     mvaddstr(y_start, x_start, COPYRIGHT_BANNER_A);
1223     y_start++;
1224 #endif
1225 
1226 #ifdef COPYRIGHT_BANNER_B
1227     mvaddstr(y_start, x_start, COPYRIGHT_BANNER_B);
1228     y_start++;
1229 #endif
1230 
1231 #ifdef COPYRIGHT_BANNER_C
1232     mvaddstr(y_start, x_start, COPYRIGHT_BANNER_C);
1233     y_start++;
1234 #endif
1235 
1236 #ifdef COPYRIGHT_BANNER_D   /* Just in case */
1237     mvaddstr(y_start, x_start, COPYRIGHT_BANNER_D);
1238     y_start++;
1239 #endif
1240     refresh();
1241 }
1242 
1243 
1244 /* Resore colors and cursor state before exiting */
1245 
curses_cleanup()1246 void curses_cleanup()
1247 {
1248 #ifdef TEXTCOLOR
1249     if (has_colors() && can_change_color())
1250     {
1251         init_color(COLOR_YELLOW, orig_yellow.r, orig_yellow.g,
1252          orig_yellow.b);
1253         init_color(COLOR_WHITE, orig_white.r, orig_white.g,
1254          orig_white.b);
1255 
1256         if (COLORS >= 16)
1257         {
1258             init_color(COLOR_RED + 8, orig_hired.r, orig_hired.g,
1259              orig_hired.b);
1260             init_color(COLOR_GREEN + 8, orig_higreen.r, orig_higreen.g,
1261              orig_higreen.b);
1262             init_color(COLOR_YELLOW + 8, orig_hiyellow.r,
1263              orig_hiyellow.g, orig_hiyellow.b);
1264             init_color(COLOR_BLUE + 8, orig_hiblue.r, orig_hiblue.g,
1265              orig_hiblue.b);
1266             init_color(COLOR_MAGENTA + 8, orig_himagenta.r,
1267              orig_himagenta.g, orig_himagenta.b);
1268             init_color(COLOR_CYAN + 8, orig_hicyan.r, orig_hicyan.g,
1269              orig_hicyan.b);
1270             init_color(COLOR_WHITE + 8, orig_hiwhite.r, orig_hiwhite.g,
1271              orig_hiwhite.b);
1272 # ifdef USE_DARKGRAY
1273             if (COLORS > 16)
1274             {
1275                 init_color(CURSES_DARK_GRAY, orig_darkgray.r,
1276              orig_darkgray.g, orig_darkgray.b);
1277             }
1278 # endif
1279         }
1280     }
1281 #endif
1282 }
1283 
1284