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