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