1 /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
2 /* NetHack 3.6 cursinit.c */
3 /* Copyright (c) Karl Garrison, 2010. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "curses.h"
7 #include "hack.h"
8 #include "wincurs.h"
9 #include "cursinit.h"
10 /*#include "patchlevel.h"*/
11 
12 #include <ctype.h>
13 
14 /* Initialization and startup functions for curses interface */
15 
16 /* Private declarations */
17 
18 static void set_window_position(int *, int *, int *, int *, int,
19                                 int *, int *, int *, int *, int,
20                                 int, int);
21 
22 /* array to save initial terminal colors for later restoration */
23 
24 typedef struct nhrgb_type {
25     short r;
26     short g;
27     short b;
28 } nhrgb;
29 
30 nhrgb orig_yellow;
31 nhrgb orig_white;
32 nhrgb orig_darkgray;
33 nhrgb orig_hired;
34 nhrgb orig_higreen;
35 nhrgb orig_hiyellow;
36 nhrgb orig_hiblue;
37 nhrgb orig_himagenta;
38 nhrgb orig_hicyan;
39 nhrgb orig_hiwhite;
40 
41 /* Banners used for an optional ASCII splash screen */
42 
43 #define NETHACK_SPLASH_A \
44 " _   _        _    _    _               _    "
45 
46 #define NETHACK_SPLASH_B \
47 "| \\ | |      | |  | |  | |             | |   "
48 
49 #define NETHACK_SPLASH_C \
50 "|  \\| |  ___ | |_ | |__| |  __ _   ___ | | __"
51 
52 #define NETHACK_SPLASH_D \
53 "| . ` | / _ \\| __||  __  | / _` | / __|| |/ /"
54 
55 #define NETHACK_SPLASH_E \
56 "| |\\  ||  __/| |_ | |  | || (_| || (__ |   < "
57 
58 #define NETHACK_SPLASH_F \
59 "|_| \\_| \\___| \\__||_|  |_| \\__,_| \\___||_|\\_\\"
60 
61 
62 /* win* is size and placement of window to change, x/y/w/h is baseline
63    which can decrease depending on alignment of win* in orientation.
64    Negative minh/minw: as much as possible, but at least as much as
65    specified. */
66 static void
set_window_position(int * winx,int * winy,int * winw,int * winh,int orientation,int * x,int * y,int * w,int * h,int border_space,int minh,int minw)67 set_window_position(int *winx, int *winy, int *winw, int *winh,
68                     int orientation,
69                     int *x, int *y, int *w, int *h, int border_space,
70                     int minh, int minw)
71 {
72     *winw = *w;
73     *winh = *h;
74 
75     /* Set window height/width */
76     if (orientation == ALIGN_TOP || orientation == ALIGN_BOTTOM) {
77         if (minh < 0) {
78             *winh = (*h - ROWNO - border_space);
79             if (-minh > *winh)
80                 *winh = -minh;
81         } else
82             *winh = minh;
83         *h -= (*winh + border_space);
84     } else {
85         if (minw < 0) {
86             *winw = (*w - COLNO - border_space);
87             if (-minw > *winw)
88                 *winw = -minw;
89         } else
90             *winw = minw;
91         *w -= (*winw + border_space);
92     }
93 
94     *winx = *w + border_space + *x;
95     *winy = *h + border_space + *y;
96 
97     /* Set window position */
98     if (orientation != ALIGN_RIGHT) {
99         *winx = *x;
100         if (orientation == ALIGN_LEFT)
101             *x += *winw + border_space;
102     }
103     if (orientation != ALIGN_BOTTOM) {
104         *winy = *y;
105         if (orientation == ALIGN_TOP)
106             *y += *winh + border_space;
107     }
108 }
109 
110 /* Create the "main" nonvolatile windows used by nethack */
111 void
curses_create_main_windows()112 curses_create_main_windows()
113 {
114     int min_message_height = 1;
115     int message_orientation = 0;
116     int status_orientation = 0;
117     int border_space = 0;
118     int hspace = term_cols - 80;
119     boolean borders = FALSE;
120 
121     switch (iflags.wc2_windowborders) {
122     case 0:                     /* Off */
123         borders = FALSE;
124         break;
125     case 1:                     /* On */
126         borders = TRUE;
127         break;
128     case 2:                     /* Auto */
129         borders = (term_cols > 81 && term_rows > 25);
130         break;
131     default:
132         borders = FALSE;
133     }
134 
135     if (borders) {
136         border_space = 2;
137         hspace -= border_space;
138     }
139 
140     if ((term_cols - border_space) < COLNO) {
141         min_message_height++;
142     }
143 
144     /* Determine status window orientation */
145     if (!iflags.wc_align_status)
146         iflags.wc_align_status = ALIGN_BOTTOM;
147     if (iflags.wc_align_status == ALIGN_TOP
148         || iflags.wc_align_status == ALIGN_BOTTOM) {
149         status_orientation = iflags.wc_align_status;
150     } else { /* left or right alignment */
151         /*
152          * Max space for player name and title horizontally.
153          * [Width of 26 gives enough room for a 24 character
154          * hitpoint bar (horizontal layout uses 30 for that) and
155          * can accommodate widest field ("Experience  : 30/123456789")
156          * other than title without truncating anything.
157          * Height originally required at least 24 lines, but 21
158          * suffices and 20 can be made to work by suppressing score.]
159          */
160         if (hspace >= 26 && term_rows >= 20) {
161             status_orientation = iflags.wc_align_status;
162             hspace -= (26 + border_space);
163         } else {
164             /* orientation won't match option setting, making 'O' command's
165                list of settings seem inaccurate; but leaving the requested
166                setting in iflags might allow it to take effect if the main
167                window gets resized */
168             status_orientation = ALIGN_BOTTOM;
169         }
170     }
171 
172     /* Determine message window orientation */
173     if (!iflags.wc_align_message)
174         iflags.wc_align_message = ALIGN_TOP;
175     if (iflags.wc_align_message == ALIGN_TOP
176         || iflags.wc_align_message == ALIGN_BOTTOM) {
177         message_orientation = iflags.wc_align_message;
178     } else { /* left or right alignment */
179         if ((hspace - border_space) >= 25) {    /* Arbitrary */
180             message_orientation = iflags.wc_align_message;
181         } else {
182             /* orientation won't match option setting (see above) */
183             message_orientation = ALIGN_TOP;
184         }
185     }
186 
187     /* Figure out window positions and placements. Status and message area
188        can be aligned based on configuration. The priority alignment-wise
189        is: status > msgarea > game.
190        Define everything as taking as much space as possible and shrink/move
191        based on alignment positions. */
192     {
193         int message_x = 0;
194         int message_y = 0;
195         int status_x = 0;
196         int status_y = 0;
197         int inv_x = 0;
198         int inv_y = 0;
199         int map_x = 0;
200         int map_y = 0;
201         int message_height = 0;
202         int message_width = 0;
203         int status_height = 0;
204         int status_width = 0;
205         int inv_height = 0;
206         int inv_width = 0;
207         int map_height = (term_rows - border_space);
208         int map_width = (term_cols - border_space);
209         int statusheight = (iflags.wc2_statuslines < 3) ? 2 : 3;
210         boolean status_vertical = (status_orientation == ALIGN_LEFT
211                                    || status_orientation == ALIGN_RIGHT);
212         boolean msg_vertical = (message_orientation == ALIGN_LEFT
213                                 || message_orientation == ALIGN_RIGHT);
214 
215         /* Vertical windows have priority. Otherwise, priotity is:
216            status > inv > msg */
217         if (status_vertical)
218             set_window_position(&status_x, &status_y,
219                                 &status_width, &status_height,
220                                 status_orientation,
221                                 &map_x, &map_y, &map_width, &map_height,
222                                 border_space, 20, 26);
223 
224         if (iflags.perm_invent) {
225             /* Take up all width unless msgbar is also vertical. */
226             int width = msg_vertical ? 25 : -25;
227 
228             set_window_position(&inv_x, &inv_y, &inv_width, &inv_height,
229                                 ALIGN_RIGHT, &map_x, &map_y,
230                                 &map_width, &map_height,
231                                 border_space, -1, width);
232         }
233 
234         if (msg_vertical)
235             set_window_position(&message_x, &message_y,
236                                 &message_width, &message_height,
237                                 message_orientation,
238                                 &map_x, &map_y, &map_width, &map_height,
239                                 border_space, -1, -25);
240 
241         /* Now draw horizontal windows */
242         if (!status_vertical)
243             set_window_position(&status_x, &status_y,
244                                 &status_width, &status_height,
245                                 status_orientation,
246                                 &map_x, &map_y, &map_width, &map_height,
247                                 border_space, statusheight, 0);
248 
249         if (!msg_vertical)
250             set_window_position(&message_x, &message_y,
251                                 &message_width, &message_height,
252                                 message_orientation,
253                                 &map_x, &map_y, &map_width, &map_height,
254                                 border_space, -1, -25);
255 
256         if (map_width > COLNO)
257             map_width = COLNO;
258         if (map_height > ROWNO)
259             map_height = ROWNO;
260 
261         if (curses_get_nhwin(STATUS_WIN)) {
262             curses_del_nhwin(STATUS_WIN);
263             /* 'count window' overlays last line of mesg win;
264                asking it to display a Null string removes it */
265             curses_count_window((char *) 0);
266             curses_del_nhwin(MESSAGE_WIN);
267             curses_del_nhwin(MAP_WIN);
268             curses_del_nhwin(INV_WIN);
269             clear();
270         }
271 
272         curses_add_nhwin(STATUS_WIN, status_height, status_width, status_y,
273                          status_x, status_orientation, borders);
274 
275         curses_add_nhwin(MESSAGE_WIN, message_height, message_width, message_y,
276                          message_x, message_orientation, borders);
277 
278         if (iflags.perm_invent)
279             curses_add_nhwin(INV_WIN, inv_height, inv_width, inv_y, inv_x,
280                              ALIGN_RIGHT, borders);
281 
282         curses_add_nhwin(MAP_WIN, map_height, map_width,
283                          map_y, map_x, 0, borders);
284 
285         refresh();
286 
287         curses_refresh_nethack_windows();
288     /*
289         if (iflags.window_inited) {
290             curses_update_stats();
291             if (iflags.perm_invent)
292                 curses_update_inventory();
293         } else {
294             iflags.window_inited = TRUE;
295         }
296     */
297     }
298 }
299 
300 /* Initialize curses colors to colors used by NetHack */
301 void
curses_init_nhcolors()302 curses_init_nhcolors()
303 {
304 #ifdef TEXTCOLOR
305     if (has_colors()) {
306         use_default_colors();
307         init_pair(1, COLOR_BLACK, -1);
308         init_pair(2, COLOR_RED, -1);
309         init_pair(3, COLOR_GREEN, -1);
310         init_pair(4, COLOR_YELLOW, -1);
311         init_pair(5, COLOR_BLUE, -1);
312         init_pair(6, COLOR_MAGENTA, -1);
313         init_pair(7, COLOR_CYAN, -1);
314         init_pair(8, -1, -1);
315 
316         {
317             int i;
318             boolean hicolor = FALSE;
319 
320             static const int clr_remap[16] = {
321                 COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
322                 COLOR_BLUE,
323                 COLOR_MAGENTA, COLOR_CYAN, -1, COLOR_WHITE,
324                 COLOR_RED + 8, COLOR_GREEN + 8, COLOR_YELLOW + 8,
325                 COLOR_BLUE + 8,
326                 COLOR_MAGENTA + 8, COLOR_CYAN + 8, COLOR_WHITE + 8
327             };
328 
329             for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
330                 init_pair(17 + (i * 2) + 0, clr_remap[i], COLOR_RED);
331                 init_pair(17 + (i * 2) + 1, clr_remap[i], COLOR_BLUE);
332             }
333 
334             if (COLORS >= 16)
335                 hicolor = TRUE;
336 
337             /* Work around the crazy definitions above for more background
338                colors... */
339             for (i = 0; i < (COLORS >= 16 ? 16 : 8); i++) {
340                 init_pair((hicolor ? 49 : 9) + i, clr_remap[i], COLOR_GREEN);
341                 init_pair((hicolor ? 65 : 33) + i, clr_remap[i], COLOR_YELLOW);
342                 init_pair((hicolor ? 81 : 41) + i, clr_remap[i], COLOR_MAGENTA);
343                 init_pair((hicolor ? 97 : 49) + i, clr_remap[i], COLOR_CYAN);
344                 init_pair((hicolor ? 113 : 57) + i, clr_remap[i], COLOR_WHITE);
345             }
346         }
347 
348 
349         if (COLORS >= 16) {
350             init_pair(9, COLOR_WHITE, -1);
351             init_pair(10, COLOR_RED + 8, -1);
352             init_pair(11, COLOR_GREEN + 8, -1);
353             init_pair(12, COLOR_YELLOW + 8, -1);
354             init_pair(13, COLOR_BLUE + 8, -1);
355             init_pair(14, COLOR_MAGENTA + 8, -1);
356             init_pair(15, COLOR_CYAN + 8, -1);
357             init_pair(16, COLOR_WHITE + 8, -1);
358         }
359 
360         if (can_change_color()) {
361             /* Preserve initial terminal colors */
362             color_content(COLOR_YELLOW, &orig_yellow.r, &orig_yellow.g,
363                           &orig_yellow.b);
364             color_content(COLOR_WHITE, &orig_white.r, &orig_white.g,
365                           &orig_white.b);
366 
367             /* Set colors to appear as NetHack expects */
368             init_color(COLOR_YELLOW, 500, 300, 0);
369             init_color(COLOR_WHITE, 600, 600, 600);
370             if (COLORS >= 16) {
371                 /* Preserve initial terminal colors */
372                 color_content(COLOR_RED + 8, &orig_hired.r,
373                               &orig_hired.g, &orig_hired.b);
374                 color_content(COLOR_GREEN + 8, &orig_higreen.r,
375                               &orig_higreen.g, &orig_higreen.b);
376                 color_content(COLOR_YELLOW + 8, &orig_hiyellow.r,
377                               &orig_hiyellow.g, &orig_hiyellow.b);
378                 color_content(COLOR_BLUE + 8, &orig_hiblue.r,
379                               &orig_hiblue.g, &orig_hiblue.b);
380                 color_content(COLOR_MAGENTA + 8, &orig_himagenta.r,
381                               &orig_himagenta.g, &orig_himagenta.b);
382                 color_content(COLOR_CYAN + 8, &orig_hicyan.r,
383                               &orig_hicyan.g, &orig_hicyan.b);
384                 color_content(COLOR_WHITE + 8, &orig_hiwhite.r,
385                               &orig_hiwhite.g, &orig_hiwhite.b);
386 
387                 /* Set colors to appear as NetHack expects */
388                 init_color(COLOR_RED + 8, 1000, 500, 0);
389                 init_color(COLOR_GREEN + 8, 0, 1000, 0);
390                 init_color(COLOR_YELLOW + 8, 1000, 1000, 0);
391                 init_color(COLOR_BLUE + 8, 0, 0, 1000);
392                 init_color(COLOR_MAGENTA + 8, 1000, 0, 1000);
393                 init_color(COLOR_CYAN + 8, 0, 1000, 1000);
394                 init_color(COLOR_WHITE + 8, 1000, 1000, 1000);
395 # ifdef USE_DARKGRAY
396                 if (COLORS > 16) {
397                     color_content(CURSES_DARK_GRAY, &orig_darkgray.r,
398                                   &orig_darkgray.g, &orig_darkgray.b);
399                     init_color(CURSES_DARK_GRAY, 300, 300, 300);
400                     /* just override black colorpair entry here */
401                     init_pair(1, CURSES_DARK_GRAY, -1);
402                 }
403 # endif
404             } else {
405                 /* Set flag to use bold for bright colors */
406             }
407         }
408     }
409 #endif
410 }
411 
412 /* Allow player to pick character's role, race, gender, and alignment.
413    Borrowed from the Gnome window port. */
414 void
curses_choose_character()415 curses_choose_character()
416 {
417     int n, i, sel, count_off, pick4u;
418     int count = 0;
419     int cur_character = 0;
420     const char **choices;
421     int *pickmap;
422     char *prompt;
423     char pbuf[QBUFSZ];
424     char choice[QBUFSZ];
425     char tmpchoice[QBUFSZ];
426 
427     prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
428                                       flags.initrace, flags.initgend,
429                                       flags.initalign);
430 
431     /* This part is irritating: we have to strip the choices off of
432        the string and put them in a separate string in order to use
433        curses_character_input_dialog for this prompt. */
434     while (cur_character != '[') {
435         cur_character = prompt[count];
436         count++;
437     }
438 
439     count_off = count;
440 
441     while (cur_character != ']') {
442         tmpchoice[count - count_off] = prompt[count];
443         count++;
444         cur_character = prompt[count];
445     }
446 
447     tmpchoice[count - count_off] = '\0';
448     lcase(tmpchoice);
449 
450     while (!isspace(prompt[count_off])) {
451         count_off--;
452     }
453 
454     prompt[count_off] = '\0';
455     sprintf(choice, "%s%c", tmpchoice, '\033');
456     if (strchr(tmpchoice, 't')) {       /* Tutorial mode */
457         mvaddstr(0, 1, "New? Press t to enter a tutorial.");
458     }
459 
460     /* Add capital letters as choices that aren't displayed */
461     for (count = 0; tmpchoice[count]; count++) {
462         tmpchoice[count] = toupper(tmpchoice[count]);
463     }
464 
465     strcat(choice, tmpchoice);
466 
467     /* prevent an unnecessary prompt */
468     rigid_role_checks();
469 
470     if (!flags.randomall &&
471         (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
472          flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
473         pick4u = tolower(curses_character_input_dialog(prompt, choice, 'y'));
474     } else {
475         pick4u = 'y';
476     }
477 
478     if (pick4u == 'q') {        /* Quit or cancelled */
479         clearlocks();
480         curses_bail(0);
481     }
482 
483     if (pick4u == 'y') {
484         flags.randomall = TRUE;
485     }
486 
487     clear();
488     refresh();
489 
490     if (!flags.randomall && flags.initrole < 0) {
491         /* select a role */
492         for (n = 0; roles[n].name.m; n++)
493             continue;
494         choices = (const char **) alloc(sizeof (char *) * (n + 1));
495         pickmap = (int *) alloc(sizeof (int) * (n + 1));
496         for (;;) {
497             for (n = 0, i = 0; roles[i].name.m; i++) {
498                 if (ok_role(i, flags.initrace,
499                             flags.initgend, flags.initalign)) {
500                     if (flags.initgend >= 0 && flags.female && roles[i].name.f)
501                         choices[n] = roles[i].name.f;
502                     else
503                         choices[n] = roles[i].name.m;
504                     pickmap[n++] = i;
505                 }
506             }
507             if (n > 0)
508                 break;
509             else if (flags.initalign >= 0)
510                 flags.initalign = -1;   /* reset */
511             else if (flags.initgend >= 0)
512                 flags.initgend = -1;
513             else if (flags.initrace >= 0)
514                 flags.initrace = -1;
515             else
516                 panic("no available ROLE+race+gender+alignment combinations");
517         }
518         choices[n] = (const char *) 0;
519         if (n > 1)
520             sel = curses_character_dialog(choices,
521                                         "Choose one of the following roles:");
522         else
523             sel = 0;
524         if (sel >= 0)
525             sel = pickmap[sel];
526         else if (sel == ROLE_NONE) {    /* Quit */
527             clearlocks();
528             curses_bail(0);
529         }
530         free((genericptr_t) choices);
531         free((genericptr_t) pickmap);
532     } else if (flags.initrole < 0)
533         sel = ROLE_RANDOM;
534     else
535         sel = flags.initrole;
536 
537     if (sel == ROLE_RANDOM) {   /* Random role */
538         sel = pick_role(flags.initrace, flags.initgend,
539                         flags.initalign, PICK_RANDOM);
540         if (sel < 0)
541             sel = randrole(FALSE);
542     }
543 
544     flags.initrole = sel;
545 
546     /* Select a race, if necessary */
547     /* force compatibility with role, try for compatibility with
548      * pre-selected gender/alignment */
549     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
550         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
551             flags.initrace = pick_race(flags.initrole, flags.initgend,
552                                        flags.initalign, PICK_RANDOM);
553             if (flags.initrace < 0)
554                 flags.initrace = randrace(flags.initrole);
555         } else {
556             /* Count the number of valid races */
557             n = 0;              /* number valid */
558             for (i = 0; races[i].noun; i++) {
559                 if (ok_race(flags.initrole, i,
560                             flags.initgend, flags.initalign))
561                     n++;
562             }
563             if (n == 0) {
564                 for (i = 0; races[i].noun; i++) {
565                     if (validrace(flags.initrole, i))
566                         n++;
567                 }
568             }
569 
570             choices = (const char **) alloc(sizeof (char *) * (n + 1));
571             pickmap = (int *) alloc(sizeof (int) * (n + 1));
572             for (n = 0, i = 0; races[i].noun; i++) {
573                 if (ok_race(flags.initrole, i,
574                             flags.initgend, flags.initalign)) {
575                     choices[n] = races[i].noun;
576                     pickmap[n++] = i;
577                 }
578             }
579             choices[n] = (const char *) 0;
580             /* Permit the user to pick, if there is more than one */
581             if (n > 1)
582                 sel = curses_character_dialog(choices,
583                                         "Choose one of the following races:");
584             else
585                 sel = 0;
586             if (sel >= 0)
587                 sel = pickmap[sel];
588             else if (sel == ROLE_NONE) {        /* Quit */
589                 clearlocks();
590                 curses_bail(0);
591             }
592             flags.initrace = sel;
593             free((genericptr_t) choices);
594             free((genericptr_t) pickmap);
595         }
596         if (flags.initrace == ROLE_RANDOM) {    /* Random role */
597             sel = pick_race(flags.initrole, flags.initgend,
598                             flags.initalign, PICK_RANDOM);
599             if (sel < 0)
600                 sel = randrace(flags.initrole);
601             flags.initrace = sel;
602         }
603     }
604 
605     /* Select a gender, if necessary */
606     /* force compatibility with role/race, try for compatibility with
607      * pre-selected alignment */
608     if (flags.initgend < 0 ||
609         !validgend(flags.initrole, flags.initrace, flags.initgend)) {
610         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
611             flags.initgend = pick_gend(flags.initrole, flags.initrace,
612                                        flags.initalign, PICK_RANDOM);
613             if (flags.initgend < 0)
614                 flags.initgend = randgend(flags.initrole, flags.initrace);
615         } else {
616             /* Count the number of valid genders */
617             n = 0;              /* number valid */
618             for (i = 0; i < ROLE_GENDERS; i++) {
619                 if (ok_gend(flags.initrole, flags.initrace,
620                             i, flags.initalign))
621                     n++;
622             }
623             if (n == 0) {
624                 for (i = 0; i < ROLE_GENDERS; i++) {
625                     if (validgend(flags.initrole, flags.initrace, i))
626                         n++;
627                 }
628             }
629 
630             choices = (const char **) alloc(sizeof (char *) * (n + 1));
631             pickmap = (int *) alloc(sizeof (int) * (n + 1));
632             for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
633                 if (ok_gend(flags.initrole, flags.initrace,
634                             i, flags.initalign)) {
635                     choices[n] = genders[i].adj;
636                     pickmap[n++] = i;
637                 }
638             }
639             choices[n] = (const char *) 0;
640             /* Permit the user to pick, if there is more than one */
641             if (n > 1)
642                 sel = curses_character_dialog(choices,
643                                       "Choose one of the following genders:");
644             else
645                 sel = 0;
646             if (sel >= 0)
647                 sel = pickmap[sel];
648             else if (sel == ROLE_NONE) {        /* Quit */
649                 clearlocks();
650                 curses_bail(0);
651             }
652             flags.initgend = sel;
653             free((genericptr_t) choices);
654             free((genericptr_t) pickmap);
655         }
656         if (flags.initgend == ROLE_RANDOM) {    /* Random gender */
657             sel = pick_gend(flags.initrole, flags.initrace,
658                             flags.initalign, PICK_RANDOM);
659             if (sel < 0)
660                 sel = randgend(flags.initrole, flags.initrace);
661             flags.initgend = sel;
662         }
663     }
664 
665     /* Select an alignment, if necessary */
666     /* force compatibility with role/race/gender */
667     if (flags.initalign < 0 ||
668         !validalign(flags.initrole, flags.initrace, flags.initalign)) {
669         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
670             flags.initalign = pick_align(flags.initrole, flags.initrace,
671                                          flags.initgend, PICK_RANDOM);
672             if (flags.initalign < 0)
673                 flags.initalign = randalign(flags.initrole, flags.initrace);
674         } else {
675             /* Count the number of valid alignments */
676             n = 0;              /* number valid */
677             for (i = 0; i < ROLE_ALIGNS; i++) {
678                 if (ok_align(flags.initrole, flags.initrace,
679                              flags.initgend, i))
680                     n++;
681             }
682             if (n == 0) {
683                 for (i = 0; i < ROLE_ALIGNS; i++)
684                     if (validalign(flags.initrole, flags.initrace, i))
685                         n++;
686             }
687 
688             choices = (const char **) alloc(sizeof (char *) * (n + 1));
689             pickmap = (int *) alloc(sizeof (int) * (n + 1));
690             for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
691                 if (ok_align(flags.initrole, flags.initrace,
692                              flags.initgend, i)) {
693                     choices[n] = aligns[i].adj;
694                     pickmap[n++] = i;
695                 }
696             }
697             choices[n] = (const char *) 0;
698             /* Permit the user to pick, if there is more than one */
699             if (n > 1)
700                 sel = curses_character_dialog(choices,
701                                    "Choose one of the following alignments:");
702             else
703                 sel = 0;
704             if (sel >= 0)
705                 sel = pickmap[sel];
706             else if (sel == ROLE_NONE) {        /* Quit */
707                 clearlocks();
708                 curses_bail(0);
709             }
710             flags.initalign = sel;
711             free((genericptr_t) choices);
712             free((genericptr_t) pickmap);
713         }
714         if (flags.initalign == ROLE_RANDOM) {
715             sel = pick_align(flags.initrole, flags.initrace,
716                              flags.initgend, PICK_RANDOM);
717             if (sel < 0)
718                 sel = randalign(flags.initrole, flags.initrace);
719             flags.initalign = sel;
720         }
721     }
722 }
723 
724 /* Prompt user for character race, role, alignment, or gender */
725 int
curses_character_dialog(const char ** choices,const char * prompt)726 curses_character_dialog(const char **choices, const char *prompt)
727 {
728     int count, count2, ret, curletter;
729     char used_letters[52];
730     anything identifier;
731     menu_item *selected = NULL;
732     winid wid = curses_get_wid(NHW_MENU);
733 
734     identifier.a_void = 0;
735     curses_start_menu(wid);
736 
737     for (count = 0; choices[count]; count++) {
738         curletter = tolower(choices[count][0]);
739         for (count2 = 0; count2 < count; count2++) {
740             if (curletter == used_letters[count2]) {
741                 curletter = toupper(curletter);
742             }
743         }
744 
745         identifier.a_int = (count + 1); /* Must be non-zero */
746         curses_add_menu(wid, NO_GLYPH, &identifier, curletter, 0,
747                         A_NORMAL, choices[count], FALSE);
748         used_letters[count] = curletter;
749     }
750 
751     /* Random Selection */
752     identifier.a_int = ROLE_RANDOM;
753     curses_add_menu(wid, NO_GLYPH, &identifier, '*', 0, A_NORMAL, "Random",
754                     FALSE);
755 
756     /* Quit prompt */
757     identifier.a_int = ROLE_NONE;
758     curses_add_menu(wid, NO_GLYPH, &identifier, 'q', 0, A_NORMAL, "Quit",
759                     FALSE);
760     curses_end_menu(wid, prompt);
761     ret = curses_select_menu(wid, PICK_ONE, &selected);
762     if (ret == 1) {
763         ret = (selected->item.a_int);
764     } else {                    /* Cancelled selection */
765 
766         ret = ROLE_NONE;
767     }
768 
769     if (ret > 0) {
770         ret--;
771     }
772 
773     free((genericptr_t) selected);
774     return ret;
775 }
776 
777 /* Initialize and display options appropriately */
778 void
curses_init_options()779 curses_init_options()
780 {
781     /* change these from DISP_IN_GAME to SET_IN_GAME */
782     set_wc_option_mod_status(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS, SET_IN_GAME);
783 
784     /* Remove a few options that are irrelevant to this windowport */
785     set_option_mod_status("eight_bit_tty", SET_IN_FILE);
786 
787     /* If we don't have a symset defined, load the curses symset by default */
788     if (!symset[PRIMARY].explicitly)
789         load_symset("curses", PRIMARY);
790     if (!symset[ROGUESET].explicitly)
791         load_symset("default", ROGUESET);
792 
793 #ifdef PDCURSES
794     /* PDCurses for SDL, win32 and OS/2 has the ability to set the
795        terminal size programatically.  If the user does not specify a
796        size in the config file, we will set it to a nice big 32x110 to
797        take advantage of some of the nice features of this windowport. */
798     if (iflags.wc2_term_cols == 0)
799         iflags.wc2_term_cols = 110;
800     if (iflags.wc2_term_rows == 0)
801         iflags.wc2_term_rows = 32;
802 
803     resize_term(iflags.wc2_term_rows, iflags.wc2_term_cols);
804     getmaxyx(base_term, term_rows, term_cols);
805 
806     /* This is needed for an odd bug with PDCurses-SDL */
807 /* How to deal with this?
808     switch_graphics(ASCII_GRAPHICS);
809     if (iflags.IBMgraphics) {
810         switch_graphics(IBM_GRAPHICS);
811     } else if (iflags.cursesgraphics) {
812         switch_graphics(CURS_GRAPHICS);
813     } else {
814         switch_graphics(ASCII_GRAPHICS);
815     }
816 */
817 #endif /* PDCURSES */
818 
819     /* FIXME: this overrides explicit OPTIONS=!use_inverse */
820     iflags.wc_inverse = TRUE; /* aka iflags.use_inverse; default is False */
821 
822     /* fix up pet highlighting */
823     if (iflags.wc2_petattr == -1) /* shouldn't happen */
824         iflags.wc2_petattr = A_NORMAL;
825     if (iflags.wc2_petattr != A_NORMAL) {
826         /* Pet attribute specified, so hilite_pet should be true */
827         iflags.hilite_pet = TRUE;
828     } else if (iflags.hilite_pet) {
829         /* pet highlighting specified, so don't leave petattr at A_NORMAL */
830         iflags.wc2_petattr = A_REVERSE;
831     }
832 
833     /* curses doesn't support 's' (single message at a time; successive
834        ^P's go back to earlier messages) and 'c' (combination; single
835        on first and second of consecutive ^P's, full on third) */
836     if (iflags.prevmsg_window != 'f')
837         iflags.prevmsg_window = 'r';
838 
839 #ifdef NCURSES_MOUSE_VERSION
840     if (iflags.wc_mouse_support) {
841         curses_mouse_support(iflags.wc_mouse_support);
842     }
843 #else
844     iflags.wc_mouse_support = 0;
845 #endif
846 }
847 
848 /* Display an ASCII splash screen if the splash_screen option is set */
849 void
curses_display_splash_window()850 curses_display_splash_window()
851 {
852      int i, x_start, y_start;
853 
854     curses_get_window_xy(MAP_WIN, &x_start, &y_start);
855 
856     if ((term_cols < 70) || (term_rows < 20)) {
857         iflags.wc_splash_screen = FALSE;        /* No room for s.s. */
858     }
859 
860     if (iflags.wc_splash_screen) {
861          if (iflags.wc2_guicolor)
862               curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, ON);
863         mvaddstr(y_start, x_start, NETHACK_SPLASH_A);
864         mvaddstr(y_start + 1, x_start, NETHACK_SPLASH_B);
865         mvaddstr(y_start + 2, x_start, NETHACK_SPLASH_C);
866         mvaddstr(y_start + 3, x_start, NETHACK_SPLASH_D);
867         mvaddstr(y_start + 4, x_start, NETHACK_SPLASH_E);
868         mvaddstr(y_start + 5, x_start, NETHACK_SPLASH_F);
869         y_start += 7;
870     }
871     if (iflags.wc2_guicolor)
872          curses_toggle_color_attr(stdscr, CLR_WHITE, A_NORMAL, OFF);
873 
874     for (i = 1; i <= 4; ++i) {
875          mvaddstr(y_start, x_start, copyright_banner_line(i));
876          y_start++;
877     }
878 
879     refresh();
880 }
881 
882 /* Resore colors and cursor state before exiting */
883 void
curses_cleanup()884 curses_cleanup()
885 {
886 #ifdef TEXTCOLOR
887     if (has_colors() && can_change_color()) {
888         init_color(COLOR_YELLOW, orig_yellow.r, orig_yellow.g, orig_yellow.b);
889         init_color(COLOR_WHITE, orig_white.r, orig_white.g, orig_white.b);
890 
891         if (COLORS >= 16) {
892             init_color(COLOR_RED + 8, orig_hired.r, orig_hired.g, orig_hired.b);
893             init_color(COLOR_GREEN + 8, orig_higreen.r, orig_higreen.g,
894                        orig_higreen.b);
895             init_color(COLOR_YELLOW + 8, orig_hiyellow.r,
896                        orig_hiyellow.g, orig_hiyellow.b);
897             init_color(COLOR_BLUE + 8, orig_hiblue.r, orig_hiblue.g,
898                        orig_hiblue.b);
899             init_color(COLOR_MAGENTA + 8, orig_himagenta.r,
900                        orig_himagenta.g, orig_himagenta.b);
901             init_color(COLOR_CYAN + 8, orig_hicyan.r, orig_hicyan.g,
902                        orig_hicyan.b);
903             init_color(COLOR_WHITE + 8, orig_hiwhite.r, orig_hiwhite.g,
904                        orig_hiwhite.b);
905 # ifdef USE_DARKGRAY
906             if (COLORS > 16) {
907                 init_color(CURSES_DARK_GRAY, orig_darkgray.r,
908                            orig_darkgray.g, orig_darkgray.b);
909             }
910 # endif
911         }
912     }
913 #endif
914 }
915