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