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