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