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