Accessibility: Pick travel/cursor targets from a menu
[aNetHack.git] / src / cmd.c
blob3735b95d04266bdd4fa4e6d589fcc614e07bc60a
1 /* NetHack 3.6 cmd.c $NHDT-Date: 1457207033 2016/03/05 19:43:53 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.220 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "func_tab.h"
8 #ifdef ALTMETA
9 STATIC_VAR boolean alt_esc = FALSE;
10 #endif
12 struct cmd Cmd = { 0 }; /* flag.h */
14 extern const char *hu_stat[]; /* hunger status from eat.c */
15 extern const char *enc_stat[]; /* encumbrance status from botl.c */
17 #ifdef UNIX
19 * Some systems may have getchar() return EOF for various reasons, and
20 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
22 #if defined(SYSV) || defined(DGUX) || defined(HPUX)
23 #define NR_OF_EOFS 20
24 #endif
25 #endif
27 #define CMD_TRAVEL (char) 0x90
28 #define CMD_CLICKLOOK (char) 0x8F
30 #ifdef DEBUG
31 extern int NDECL(wiz_debug_cmd_bury);
32 extern int NDECL(wiz_debug_cmd_traveldisplay);
33 #endif
35 #ifdef DUMB /* stuff commented out in extern.h, but needed here */
36 extern int NDECL(doapply); /**/
37 extern int NDECL(dorub); /**/
38 extern int NDECL(dojump); /**/
39 extern int NDECL(doextlist); /**/
40 extern int NDECL(enter_explore_mode); /**/
41 extern int NDECL(dodrop); /**/
42 extern int NDECL(doddrop); /**/
43 extern int NDECL(dodown); /**/
44 extern int NDECL(doup); /**/
45 extern int NDECL(donull); /**/
46 extern int NDECL(dowipe); /**/
47 extern int NDECL(docallcnd); /**/
48 extern int NDECL(dotakeoff); /**/
49 extern int NDECL(doremring); /**/
50 extern int NDECL(dowear); /**/
51 extern int NDECL(doputon); /**/
52 extern int NDECL(doddoremarm); /**/
53 extern int NDECL(dokick); /**/
54 extern int NDECL(dofire); /**/
55 extern int NDECL(dothrow); /**/
56 extern int NDECL(doeat); /**/
57 extern int NDECL(done2); /**/
58 extern int NDECL(vanquished); /**/
59 extern int NDECL(doengrave); /**/
60 extern int NDECL(dopickup); /**/
61 extern int NDECL(ddoinv); /**/
62 extern int NDECL(dotypeinv); /**/
63 extern int NDECL(dolook); /**/
64 extern int NDECL(doprgold); /**/
65 extern int NDECL(doprwep); /**/
66 extern int NDECL(doprarm); /**/
67 extern int NDECL(doprring); /**/
68 extern int NDECL(dopramulet); /**/
69 extern int NDECL(doprtool); /**/
70 extern int NDECL(dosuspend); /**/
71 extern int NDECL(doforce); /**/
72 extern int NDECL(doopen); /**/
73 extern int NDECL(doclose); /**/
74 extern int NDECL(dosh); /**/
75 extern int NDECL(dodiscovered); /**/
76 extern int NDECL(doclassdisco); /**/
77 extern int NDECL(doset); /**/
78 extern int NDECL(dotogglepickup); /**/
79 extern int NDECL(dowhatis); /**/
80 extern int NDECL(doquickwhatis); /**/
81 extern int NDECL(dowhatdoes); /**/
82 extern int NDECL(dohelp); /**/
83 extern int NDECL(dohistory); /**/
84 extern int NDECL(doloot); /**/
85 extern int NDECL(dodrink); /**/
86 extern int NDECL(dodip); /**/
87 extern int NDECL(dosacrifice); /**/
88 extern int NDECL(dopray); /**/
89 extern int NDECL(dotip); /**/
90 extern int NDECL(doturn); /**/
91 extern int NDECL(doredraw); /**/
92 extern int NDECL(doread); /**/
93 extern int NDECL(dosave); /**/
94 extern int NDECL(dosearch); /**/
95 extern int NDECL(doidtrap); /**/
96 extern int NDECL(dopay); /**/
97 extern int NDECL(dosit); /**/
98 extern int NDECL(dotalk); /**/
99 extern int NDECL(docast); /**/
100 extern int NDECL(dovspell); /**/
101 extern int NDECL(dotele); /**/
102 extern int NDECL(dountrap); /**/
103 extern int NDECL(doversion); /**/
104 extern int NDECL(doextversion); /**/
105 extern int NDECL(doswapweapon); /**/
106 extern int NDECL(dowield); /**/
107 extern int NDECL(dowieldquiver); /**/
108 extern int NDECL(dozap); /**/
109 extern int NDECL(doorganize); /**/
110 #endif /* DUMB */
112 static int NDECL(dosuspend_core); /**/
114 static int NDECL((*timed_occ_fn));
116 STATIC_PTR int NDECL(doprev_message);
117 STATIC_PTR int NDECL(timed_occupation);
118 STATIC_PTR int NDECL(doextcmd);
119 STATIC_PTR int NDECL(domonability);
120 STATIC_PTR int NDECL(dotravel);
121 STATIC_PTR int NDECL(doterrain);
122 STATIC_PTR int NDECL(wiz_wish);
123 STATIC_PTR int NDECL(wiz_identify);
124 STATIC_PTR int NDECL(wiz_intrinsic);
125 STATIC_PTR int NDECL(wiz_map);
126 STATIC_PTR int NDECL(wiz_genesis);
127 STATIC_PTR int NDECL(wiz_where);
128 STATIC_PTR int NDECL(wiz_detect);
129 STATIC_PTR int NDECL(wiz_panic);
130 STATIC_PTR int NDECL(wiz_polyself);
131 STATIC_PTR int NDECL(wiz_level_tele);
132 STATIC_PTR int NDECL(wiz_level_change);
133 STATIC_PTR int NDECL(wiz_show_seenv);
134 STATIC_PTR int NDECL(wiz_show_vision);
135 STATIC_PTR int NDECL(wiz_smell);
136 STATIC_PTR int NDECL(wiz_mon_polycontrol);
137 STATIC_PTR int NDECL(wiz_show_wmodes);
138 STATIC_DCL void NDECL(wiz_map_levltyp);
139 STATIC_DCL void NDECL(wiz_levltyp_legend);
140 #if defined(__BORLANDC__) && !defined(_WIN32)
141 extern void FDECL(show_borlandc_stats, (winid));
142 #endif
143 #ifdef DEBUG_MIGRATING_MONS
144 STATIC_PTR int NDECL(wiz_migrate_mons);
145 #endif
146 STATIC_DCL int FDECL(size_monst, (struct monst *, BOOLEAN_P));
147 STATIC_DCL int FDECL(size_obj, (struct obj *));
148 STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *,
149 BOOLEAN_P, BOOLEAN_P));
150 STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *,
151 BOOLEAN_P, long *, long *));
152 STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *,
153 long *, long *));
154 STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *,
155 BOOLEAN_P, long *, long *));
156 STATIC_DCL void FDECL(contained_stats, (winid, const char *, long *, long *));
157 STATIC_DCL void FDECL(misc_stats, (winid, long *, long *));
158 STATIC_PTR int NDECL(wiz_show_stats);
159 STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*))));
160 #ifdef PORT_DEBUG
161 STATIC_DCL int NDECL(wiz_port_debug);
162 #endif
163 STATIC_PTR int NDECL(wiz_rumor_check);
164 STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*))));
165 STATIC_PTR int NDECL(doattributes);
166 STATIC_PTR int NDECL(doconduct); /**/
168 STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *,
169 const char *));
170 STATIC_DCL char *FDECL(enlght_combatinc, (const char *, int, int, char *));
171 STATIC_DCL void FDECL(enlght_halfdmg, (int, int));
172 STATIC_DCL boolean NDECL(walking_on_water);
173 STATIC_DCL boolean FDECL(cause_known, (int));
174 STATIC_DCL char *FDECL(attrval, (int, int, char *));
175 STATIC_DCL void FDECL(background_enlightenment, (int, int));
176 STATIC_DCL void FDECL(characteristics_enlightenment, (int, int));
177 STATIC_DCL void FDECL(one_characteristic, (int, int, int));
178 STATIC_DCL void FDECL(status_enlightenment, (int, int));
179 STATIC_DCL void FDECL(attributes_enlightenment, (int, int));
181 static const char *readchar_queue = "";
182 static coord clicklook_cc;
184 STATIC_DCL char *NDECL(parse);
185 STATIC_DCL void FDECL(show_direction_keys, (winid, BOOLEAN_P));
186 STATIC_DCL boolean FDECL(help_dir, (CHAR_P, const char *));
188 STATIC_PTR int
189 doprev_message(VOID_ARGS)
191 return nh_doprev_message();
194 /* Count down by decrementing multi */
195 STATIC_PTR int
196 timed_occupation(VOID_ARGS)
198 (*timed_occ_fn)();
199 if (multi > 0)
200 multi--;
201 return multi > 0;
204 /* If you have moved since initially setting some occupations, they
205 * now shouldn't be able to restart.
207 * The basic rule is that if you are carrying it, you can continue
208 * since it is with you. If you are acting on something at a distance,
209 * your orientation to it must have changed when you moved.
211 * The exception to this is taking off items, since they can be taken
212 * off in a number of ways in the intervening time, screwing up ordering.
214 * Currently: Take off all armor.
215 * Picking Locks / Forcing Chests.
216 * Setting traps.
218 void
219 reset_occupations()
221 reset_remarm();
222 reset_pick();
223 reset_trapset();
226 /* If a time is given, use it to timeout this function, otherwise the
227 * function times out by its own means.
229 void
230 set_occupation(fn, txt, xtime)
231 int NDECL((*fn));
232 const char *txt;
233 int xtime;
235 if (xtime) {
236 occupation = timed_occupation;
237 timed_occ_fn = fn;
238 } else
239 occupation = fn;
240 occtxt = txt;
241 occtime = 0;
242 return;
245 STATIC_DCL char NDECL(popch);
247 /* Provide a means to redo the last command. The flag `in_doagain' is set
248 * to true while redoing the command. This flag is tested in commands that
249 * require additional input (like `throw' which requires a thing and a
250 * direction), and the input prompt is not shown. Also, while in_doagain is
251 * TRUE, no keystrokes can be saved into the saveq.
253 #define BSIZE 20
254 static char pushq[BSIZE], saveq[BSIZE];
255 static NEARDATA int phead, ptail, shead, stail;
257 STATIC_OVL char
258 popch()
260 /* If occupied, return '\0', letting tgetch know a character should
261 * be read from the keyboard. If the character read is not the
262 * ABORT character (as checked in pcmain.c), that character will be
263 * pushed back on the pushq.
265 if (occupation)
266 return '\0';
267 if (in_doagain)
268 return (char) ((shead != stail) ? saveq[stail++] : '\0');
269 else
270 return (char) ((phead != ptail) ? pushq[ptail++] : '\0');
273 char
274 pgetchar() /* courtesy of aeb@cwi.nl */
276 register int ch;
278 if (!(ch = popch()))
279 ch = nhgetch();
280 return (char) ch;
283 /* A ch == 0 resets the pushq */
284 void
285 pushch(ch)
286 char ch;
288 if (!ch)
289 phead = ptail = 0;
290 if (phead < BSIZE)
291 pushq[phead++] = ch;
292 return;
295 /* A ch == 0 resets the saveq. Only save keystrokes when not
296 * replaying a previous command.
298 void
299 savech(ch)
300 char ch;
302 if (!in_doagain) {
303 if (!ch)
304 phead = ptail = shead = stail = 0;
305 else if (shead < BSIZE)
306 saveq[shead++] = ch;
308 return;
311 /* here after # - now read a full-word command */
312 STATIC_PTR int
313 doextcmd(VOID_ARGS)
315 int idx, retval;
316 int NDECL((*func));
318 /* keep repeating until we don't run help or quit */
319 do {
320 idx = get_ext_cmd();
321 if (idx < 0)
322 return 0; /* quit */
324 func = extcmdlist[idx].ef_funct;
325 if (!wizard && (extcmdlist[idx].flags & WIZMODECMD)) {
326 You("can't do that.");
327 return 0;
329 if (iflags.menu_requested && !accept_menu_prefix(func)) {
330 pline("'%s' prefix has no effect for this command.",
331 visctrl(Cmd.spkeys[NHKF_REQMENU]));
332 iflags.menu_requested = FALSE;
334 retval = (*func)();
335 } while (func == doextlist);
337 return retval;
340 /* here after #? - now list all full-word commands */
342 doextlist(VOID_ARGS)
344 register const struct ext_func_tab *efp;
345 char buf[BUFSZ];
346 winid datawin;
348 datawin = create_nhwindow(NHW_TEXT);
349 putstr(datawin, 0, "");
350 putstr(datawin, 0, " Extended Commands List");
351 putstr(datawin, 0, "");
352 putstr(datawin, 0, " Press '#', then type:");
353 putstr(datawin, 0, "");
355 for (efp = extcmdlist; efp->ef_txt; efp++) {
356 if (!wizard && (efp->flags & WIZMODECMD))
357 continue;
358 Sprintf(buf, " %-15s %c %s.",
359 efp->ef_txt,
360 (efp->flags & AUTOCOMPLETE) ? '*' : ' ',
361 efp->ef_desc);
362 putstr(datawin, 0, buf);
364 putstr(datawin, 0, "");
365 putstr(datawin, 0, " Commands marked with a * will be autocompleted.");
366 display_nhwindow(datawin, FALSE);
367 destroy_nhwindow(datawin);
368 return 0;
371 #ifdef TTY_GRAPHICS
372 #define MAX_EXT_CMD 200 /* Change if we ever have more ext cmds */
375 * This is currently used only by the tty port and is
376 * controlled via runtime option 'extmenu'.
377 * ``# ?'' is counted towards the limit of the number of commands,
378 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
380 * Here after # - now show pick-list of possible commands.
383 extcmd_via_menu()
385 const struct ext_func_tab *efp;
386 menu_item *pick_list = (menu_item *) 0;
387 winid win;
388 anything any;
389 const struct ext_func_tab *choices[MAX_EXT_CMD + 1];
390 char buf[BUFSZ];
391 char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
392 int i, n, nchoices, acount;
393 int ret, biggest;
394 int accelerator, prevaccelerator;
395 int matchlevel = 0;
397 ret = 0;
398 cbuf[0] = '\0';
399 biggest = 0;
400 while (!ret) {
401 i = n = 0;
402 any = zeroany;
403 /* populate choices */
404 for (efp = extcmdlist; efp->ef_txt; efp++) {
405 if (!(efp->flags & AUTOCOMPLETE)
406 || (!wizard && (efp->flags & WIZMODECMD)))
407 continue;
408 if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
409 choices[i] = efp;
410 if ((int) strlen(efp->ef_desc) > biggest) {
411 biggest = strlen(efp->ef_desc);
412 Sprintf(fmtstr, "%%-%ds", biggest + 15);
414 if (++i > MAX_EXT_CMD) {
415 #if defined(BETA)
416 impossible(
417 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
418 MAX_EXT_CMD);
419 #endif /* BETA */
420 iflags.extmenu = 0;
421 return -1;
425 choices[i] = (struct ext_func_tab *) 0;
426 nchoices = i;
427 /* if we're down to one, we have our selection so get out of here */
428 if (nchoices == 1) {
429 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
430 if ((extcmdlist[i].flags & AUTOCOMPLETE)
431 && !(!wizard && (extcmdlist[i].flags & WIZMODECMD))
432 && !strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) {
433 ret = i;
434 break;
436 break;
439 /* otherwise... */
440 win = create_nhwindow(NHW_MENU);
441 start_menu(win);
442 accelerator = prevaccelerator = 0;
443 acount = 0;
444 for (i = 0; choices[i]; ++i) {
445 accelerator = choices[i]->ef_txt[matchlevel];
446 if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) {
447 if (acount) {
448 /* flush extended cmds for that letter already in buf */
449 Sprintf(buf, fmtstr, prompt);
450 any.a_char = prevaccelerator;
451 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE,
452 buf, FALSE);
453 acount = 0;
456 prevaccelerator = accelerator;
457 if (!acount || nchoices < (ROWNO - 3)) {
458 Sprintf(prompt, "%s [%s]", choices[i]->ef_txt,
459 choices[i]->ef_desc);
460 } else if (acount == 1) {
461 Sprintf(prompt, "%s or %s", choices[i - 1]->ef_txt,
462 choices[i]->ef_txt);
463 } else {
464 Strcat(prompt, " or ");
465 Strcat(prompt, choices[i]->ef_txt);
467 ++acount;
469 if (acount) {
470 /* flush buf */
471 Sprintf(buf, fmtstr, prompt);
472 any.a_char = prevaccelerator;
473 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf,
474 FALSE);
476 Sprintf(prompt, "Extended Command: %s", cbuf);
477 end_menu(win, prompt);
478 n = select_menu(win, PICK_ONE, &pick_list);
479 destroy_nhwindow(win);
480 if (n == 1) {
481 if (matchlevel > (QBUFSZ - 2)) {
482 free((genericptr_t) pick_list);
483 #if defined(BETA)
484 impossible("Too many chars (%d) entered in extcmd_via_menu()",
485 matchlevel);
486 #endif
487 ret = -1;
488 } else {
489 cbuf[matchlevel++] = pick_list[0].item.a_char;
490 cbuf[matchlevel] = '\0';
491 free((genericptr_t) pick_list);
493 } else {
494 if (matchlevel) {
495 ret = 0;
496 matchlevel = 0;
497 } else
498 ret = -1;
501 return ret;
503 #endif /* TTY_GRAPHICS */
505 /* #monster command - use special monster ability while polymorphed */
506 STATIC_PTR int
507 domonability(VOID_ARGS)
509 if (can_breathe(youmonst.data))
510 return dobreathe();
511 else if (attacktype(youmonst.data, AT_SPIT))
512 return dospit();
513 else if (youmonst.data->mlet == S_NYMPH)
514 return doremove();
515 else if (attacktype(youmonst.data, AT_GAZE))
516 return dogaze();
517 else if (is_were(youmonst.data))
518 return dosummon();
519 else if (webmaker(youmonst.data))
520 return dospinweb();
521 else if (is_hider(youmonst.data))
522 return dohide();
523 else if (is_mind_flayer(youmonst.data))
524 return domindblast();
525 else if (u.umonnum == PM_GREMLIN) {
526 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
527 if (split_mon(&youmonst, (struct monst *) 0))
528 dryup(u.ux, u.uy, TRUE);
529 } else
530 There("is no fountain here.");
531 } else if (is_unicorn(youmonst.data)) {
532 use_unicorn_horn((struct obj *) 0);
533 return 1;
534 } else if (youmonst.data->msound == MS_SHRIEK) {
535 You("shriek.");
536 if (u.uburied)
537 pline("Unfortunately sound does not carry well through rock.");
538 else
539 aggravate();
540 } else if (youmonst.data->mlet == S_VAMPIRE)
541 return dopoly();
542 else if (Upolyd)
543 pline("Any special ability you may have is purely reflexive.");
544 else
545 You("don't have a special ability in your normal form!");
546 return 0;
550 enter_explore_mode(VOID_ARGS)
552 if (wizard) {
553 You("are in debug mode.");
554 } else if (discover) {
555 You("are already in explore mode.");
556 } else {
557 #ifdef SYSCF
558 #if defined(UNIX)
559 if (!sysopt.explorers || !sysopt.explorers[0]
560 || !check_user_string(sysopt.explorers)) {
561 You("cannot access explore mode.");
562 return 0;
564 #endif
565 #endif
566 pline(
567 "Beware! From explore mode there will be no return to normal game.");
568 if (paranoid_query(ParanoidQuit,
569 "Do you want to enter explore mode?")) {
570 clear_nhwindow(WIN_MESSAGE);
571 You("are now in non-scoring explore mode.");
572 discover = TRUE;
573 } else {
574 clear_nhwindow(WIN_MESSAGE);
575 pline("Resuming normal game.");
578 return 0;
581 /* ^W command - wish for something */
582 STATIC_PTR int
583 wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */
585 if (wizard) {
586 boolean save_verbose = flags.verbose;
588 flags.verbose = FALSE;
589 makewish();
590 flags.verbose = save_verbose;
591 (void) encumber_msg();
592 } else
593 pline("Unavailable command '%s'.",
594 visctrl((int) cmd_from_func(wiz_wish)));
595 return 0;
598 /* ^I command - reveal and optionally identify hero's inventory */
599 STATIC_PTR int
600 wiz_identify(VOID_ARGS)
602 if (wizard) {
603 iflags.override_ID = (int) cmd_from_func(wiz_identify);
604 if (display_inventory((char *) 0, TRUE) == -1)
605 identify_pack(0, FALSE);
606 iflags.override_ID = 0;
607 } else
608 pline("Unavailable command '%s'.",
609 visctrl((int) cmd_from_func(wiz_identify)));
610 return 0;
613 /* ^F command - reveal the level map and any traps on it */
614 STATIC_PTR int
615 wiz_map(VOID_ARGS)
617 if (wizard) {
618 struct trap *t;
619 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
621 HConfusion = HHallucination = 0L;
622 for (t = ftrap; t != 0; t = t->ntrap) {
623 t->tseen = 1;
624 map_trap(t, TRUE);
626 do_mapping();
627 HConfusion = save_Hconf;
628 HHallucination = save_Hhallu;
629 } else
630 pline("Unavailable command '%s'.",
631 visctrl((int) cmd_from_func(wiz_map)));
632 return 0;
635 /* ^G command - generate monster(s); a count prefix will be honored */
636 STATIC_PTR int
637 wiz_genesis(VOID_ARGS)
639 if (wizard)
640 (void) create_particular();
641 else
642 pline("Unavailable command '%s'.",
643 visctrl((int) cmd_from_func(wiz_genesis)));
644 return 0;
647 /* ^O command - display dungeon layout */
648 STATIC_PTR int
649 wiz_where(VOID_ARGS)
651 if (wizard)
652 (void) print_dungeon(FALSE, (schar *) 0, (xchar *) 0);
653 else
654 pline("Unavailable command '%s'.",
655 visctrl((int) cmd_from_func(wiz_where)));
656 return 0;
659 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
660 STATIC_PTR int
661 wiz_detect(VOID_ARGS)
663 if (wizard)
664 (void) findit();
665 else
666 pline("Unavailable command '%s'.",
667 visctrl((int) cmd_from_func(wiz_detect)));
668 return 0;
671 /* ^V command - level teleport */
672 STATIC_PTR int
673 wiz_level_tele(VOID_ARGS)
675 if (wizard)
676 level_tele();
677 else
678 pline("Unavailable command '%s'.",
679 visctrl((int) cmd_from_func(wiz_level_tele)));
680 return 0;
683 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
684 STATIC_PTR int
685 wiz_mon_polycontrol(VOID_ARGS)
687 iflags.mon_polycontrol = !iflags.mon_polycontrol;
688 pline("Monster polymorph control is %s.",
689 iflags.mon_polycontrol ? "on" : "off");
690 return 0;
693 /* #levelchange command - adjust hero's experience level */
694 STATIC_PTR int
695 wiz_level_change(VOID_ARGS)
697 char buf[BUFSZ];
698 int newlevel;
699 int ret;
701 getlin("To what experience level do you want to be set?", buf);
702 (void) mungspaces(buf);
703 if (buf[0] == '\033' || buf[0] == '\0')
704 ret = 0;
705 else
706 ret = sscanf(buf, "%d", &newlevel);
708 if (ret != 1) {
709 pline1(Never_mind);
710 return 0;
712 if (newlevel == u.ulevel) {
713 You("are already that experienced.");
714 } else if (newlevel < u.ulevel) {
715 if (u.ulevel == 1) {
716 You("are already as inexperienced as you can get.");
717 return 0;
719 if (newlevel < 1)
720 newlevel = 1;
721 while (u.ulevel > newlevel)
722 losexp("#levelchange");
723 } else {
724 if (u.ulevel >= MAXULEV) {
725 You("are already as experienced as you can get.");
726 return 0;
728 if (newlevel > MAXULEV)
729 newlevel = MAXULEV;
730 while (u.ulevel < newlevel)
731 pluslvl(FALSE);
733 u.ulevelmax = u.ulevel;
734 return 0;
737 /* #panic command - test program's panic handling */
738 STATIC_PTR int
739 wiz_panic(VOID_ARGS)
741 if (yn("Do you want to call panic() and end your game?") == 'y')
742 panic("Crash test.");
743 return 0;
746 /* #polyself command - change hero's form */
747 STATIC_PTR int
748 wiz_polyself(VOID_ARGS)
750 polyself(1);
751 return 0;
754 /* #seenv command */
755 STATIC_PTR int
756 wiz_show_seenv(VOID_ARGS)
758 winid win;
759 int x, y, v, startx, stopx, curx;
760 char row[COLNO + 1];
762 win = create_nhwindow(NHW_TEXT);
764 * Each seenv description takes up 2 characters, so center
765 * the seenv display around the hero.
767 startx = max(1, u.ux - (COLNO / 4));
768 stopx = min(startx + (COLNO / 2), COLNO);
769 /* can't have a line exactly 80 chars long */
770 if (stopx - startx == COLNO / 2)
771 startx++;
773 for (y = 0; y < ROWNO; y++) {
774 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
775 if (x == u.ux && y == u.uy) {
776 row[curx] = row[curx + 1] = '@';
777 } else {
778 v = levl[x][y].seenv & 0xff;
779 if (v == 0)
780 row[curx] = row[curx + 1] = ' ';
781 else
782 Sprintf(&row[curx], "%02x", v);
785 /* remove trailing spaces */
786 for (x = curx - 1; x >= 0; x--)
787 if (row[x] != ' ')
788 break;
789 row[x + 1] = '\0';
791 putstr(win, 0, row);
793 display_nhwindow(win, TRUE);
794 destroy_nhwindow(win);
795 return 0;
798 /* #vision command */
799 STATIC_PTR int
800 wiz_show_vision(VOID_ARGS)
802 winid win;
803 int x, y, v;
804 char row[COLNO + 1];
806 win = create_nhwindow(NHW_TEXT);
807 Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
808 COULD_SEE, IN_SIGHT, TEMP_LIT);
809 putstr(win, 0, row);
810 putstr(win, 0, "");
811 for (y = 0; y < ROWNO; y++) {
812 for (x = 1; x < COLNO; x++) {
813 if (x == u.ux && y == u.uy)
814 row[x] = '@';
815 else {
816 v = viz_array[y][x]; /* data access should be hidden */
817 if (v == 0)
818 row[x] = ' ';
819 else
820 row[x] = '0' + viz_array[y][x];
823 /* remove trailing spaces */
824 for (x = COLNO - 1; x >= 1; x--)
825 if (row[x] != ' ')
826 break;
827 row[x + 1] = '\0';
829 putstr(win, 0, &row[1]);
831 display_nhwindow(win, TRUE);
832 destroy_nhwindow(win);
833 return 0;
836 /* #wmode command */
837 STATIC_PTR int
838 wiz_show_wmodes(VOID_ARGS)
840 winid win;
841 int x, y;
842 char row[COLNO + 1];
843 struct rm *lev;
844 boolean istty = !strcmp(windowprocs.name, "tty");
846 win = create_nhwindow(NHW_TEXT);
847 if (istty)
848 putstr(win, 0, ""); /* tty only: blank top line */
849 for (y = 0; y < ROWNO; y++) {
850 for (x = 0; x < COLNO; x++) {
851 lev = &levl[x][y];
852 if (x == u.ux && y == u.uy)
853 row[x] = '@';
854 else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
855 row[x] = '0' + (lev->wall_info & WM_MASK);
856 else if (lev->typ == CORR)
857 row[x] = '#';
858 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
859 row[x] = '.';
860 else
861 row[x] = 'x';
863 row[COLNO] = '\0';
864 /* map column 0, levl[0][], is off the left edge of the screen */
865 putstr(win, 0, &row[1]);
867 display_nhwindow(win, TRUE);
868 destroy_nhwindow(win);
869 return 0;
872 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
873 STATIC_OVL void
874 wiz_map_levltyp(VOID_ARGS)
876 winid win;
877 int x, y, terrain;
878 char row[COLNO + 1];
879 boolean istty = !strcmp(windowprocs.name, "tty");
881 win = create_nhwindow(NHW_TEXT);
882 /* map row 0, levl[][0], is drawn on the second line of tty screen */
883 if (istty)
884 putstr(win, 0, ""); /* tty only: blank top line */
885 for (y = 0; y < ROWNO; y++) {
886 /* map column 0, levl[0][], is off the left edge of the screen;
887 it should always have terrain type "undiggable stone" */
888 for (x = 1; x < COLNO; x++) {
889 terrain = levl[x][y].typ;
890 /* assumes there aren't more than 10+26+26 terrain types */
891 row[x - 1] = (char) ((terrain == 0 && !may_dig(x, y))
892 ? '*'
893 : (terrain < 10)
894 ? '0' + terrain
895 : (terrain < 36)
896 ? 'a' + terrain - 10
897 : 'A' + terrain - 36);
899 if (levl[0][y].typ != 0 || may_dig(0, y))
900 row[x++] = '!';
901 row[x] = '\0';
902 putstr(win, 0, row);
906 char dsc[BUFSZ];
907 s_level *slev = Is_special(&u.uz);
909 Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel);
910 /* [dungeon branch features currently omitted] */
911 /* special level features */
912 if (slev) {
913 Sprintf(eos(dsc), " \"%s\"", slev->proto);
914 /* special level flags (note: dungeon.def doesn't set `maze'
915 or `hell' for any specific levels so those never show up) */
916 if (slev->flags.maze_like)
917 Strcat(dsc, " mazelike");
918 if (slev->flags.hellish)
919 Strcat(dsc, " hellish");
920 if (slev->flags.town)
921 Strcat(dsc, " town");
922 if (slev->flags.rogue_like)
923 Strcat(dsc, " roguelike");
924 /* alignment currently omitted to save space */
926 /* level features */
927 if (level.flags.nfountains)
928 Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym,
929 (int) level.flags.nfountains);
930 if (level.flags.nsinks)
931 Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym,
932 (int) level.flags.nsinks);
933 if (level.flags.has_vault)
934 Strcat(dsc, " vault");
935 if (level.flags.has_shop)
936 Strcat(dsc, " shop");
937 if (level.flags.has_temple)
938 Strcat(dsc, " temple");
939 if (level.flags.has_court)
940 Strcat(dsc, " throne");
941 if (level.flags.has_zoo)
942 Strcat(dsc, " zoo");
943 if (level.flags.has_morgue)
944 Strcat(dsc, " morgue");
945 if (level.flags.has_barracks)
946 Strcat(dsc, " barracks");
947 if (level.flags.has_beehive)
948 Strcat(dsc, " hive");
949 if (level.flags.has_swamp)
950 Strcat(dsc, " swamp");
951 /* level flags */
952 if (level.flags.noteleport)
953 Strcat(dsc, " noTport");
954 if (level.flags.hardfloor)
955 Strcat(dsc, " noDig");
956 if (level.flags.nommap)
957 Strcat(dsc, " noMMap");
958 if (!level.flags.hero_memory)
959 Strcat(dsc, " noMem");
960 if (level.flags.shortsighted)
961 Strcat(dsc, " shortsight");
962 if (level.flags.graveyard)
963 Strcat(dsc, " graveyard");
964 if (level.flags.is_maze_lev)
965 Strcat(dsc, " maze");
966 if (level.flags.is_cavernous_lev)
967 Strcat(dsc, " cave");
968 if (level.flags.arboreal)
969 Strcat(dsc, " tree");
970 if (Sokoban)
971 Strcat(dsc, " sokoban-rules");
972 /* non-flag info; probably should include dungeon branching
973 checks (extra stairs and magic portals) here */
974 if (Invocation_lev(&u.uz))
975 Strcat(dsc, " invoke");
976 if (On_W_tower_level(&u.uz))
977 Strcat(dsc, " tower");
978 /* append a branch identifier for completeness' sake */
979 if (u.uz.dnum == 0)
980 Strcat(dsc, " dungeon");
981 else if (u.uz.dnum == mines_dnum)
982 Strcat(dsc, " mines");
983 else if (In_sokoban(&u.uz))
984 Strcat(dsc, " sokoban");
985 else if (u.uz.dnum == quest_dnum)
986 Strcat(dsc, " quest");
987 else if (Is_knox(&u.uz))
988 Strcat(dsc, " ludios");
989 else if (u.uz.dnum == 1)
990 Strcat(dsc, " gehennom");
991 else if (u.uz.dnum == tower_dnum)
992 Strcat(dsc, " vlad");
993 else if (In_endgame(&u.uz))
994 Strcat(dsc, " endgame");
995 else {
996 /* somebody's added a dungeon branch we're not expecting */
997 const char *brname = dungeons[u.uz.dnum].dname;
999 if (!brname || !*brname)
1000 brname = "unknown";
1001 if (!strncmpi(brname, "the ", 4))
1002 brname += 4;
1003 Sprintf(eos(dsc), " %s", brname);
1005 /* limit the line length to map width */
1006 if (strlen(dsc) >= COLNO)
1007 dsc[COLNO - 1] = '\0'; /* truncate */
1008 putstr(win, 0, dsc);
1011 display_nhwindow(win, TRUE);
1012 destroy_nhwindow(win);
1013 return;
1016 /* temporary? hack, since level type codes aren't the same as screen
1017 symbols and only the latter have easily accessible descriptions */
1018 static const char *levltyp[] = {
1019 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1020 "top-right corner wall", "bottom-left corner wall",
1021 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1022 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1023 "secret door", "secret corridor", "pool", "moat", "water",
1024 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1025 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1026 "drawbridge down", "air", "cloud",
1027 /* not a real terrain type, but used for undiggable stone
1028 by wiz_map_levltyp() */
1029 "unreachable/undiggable",
1030 /* padding in case the number of entries above is odd */
1034 /* explanation of base-36 output from wiz_map_levltyp() */
1035 STATIC_OVL void
1036 wiz_levltyp_legend(VOID_ARGS)
1038 winid win;
1039 int i, j, last, c;
1040 const char *dsc, *fmt;
1041 char buf[BUFSZ];
1043 win = create_nhwindow(NHW_TEXT);
1044 putstr(win, 0, "#terrain encodings:");
1045 putstr(win, 0, "");
1046 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1047 *buf = '\0';
1048 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1049 and right hand column holds [N/2],[N/2+1],...,[N-1];
1050 N ('last') will always be even, and may or may not include
1051 the empty string entry to pad out the final pair, depending
1052 upon how many other entries are present in levltyp[] */
1053 last = SIZE(levltyp) & ~1;
1054 for (i = 0; i < last / 2; ++i)
1055 for (j = i; j < last; j += last / 2) {
1056 dsc = levltyp[j];
1057 c = !*dsc ? ' '
1058 : !strncmp(dsc, "unreachable", 11) ? '*'
1059 /* same int-to-char conversion as wiz_map_levltyp() */
1060 : (j < 10) ? '0' + j
1061 : (j < 36) ? 'a' + j - 10
1062 : 'A' + j - 36;
1063 Sprintf(eos(buf), fmt, c, dsc);
1064 if (j > i) {
1065 putstr(win, 0, buf);
1066 *buf = '\0';
1069 display_nhwindow(win, TRUE);
1070 destroy_nhwindow(win);
1071 return;
1074 /* #wizsmell command - test usmellmon(). */
1075 STATIC_PTR int
1076 wiz_smell(VOID_ARGS)
1078 int ans = 0;
1079 int mndx; /* monster index */
1080 coord cc; /* screen pos of unknown glyph */
1081 int glyph; /* glyph at selected position */
1083 cc.x = u.ux;
1084 cc.y = u.uy;
1085 mndx = 0; /* gcc -Wall lint */
1086 if (!olfaction(youmonst.data)) {
1087 You("are incapable of detecting odors in your present form.");
1088 return 0;
1091 pline("You can move the cursor to a monster that you want to smell.");
1092 do {
1093 pline("Pick a monster to smell.");
1094 ans = getpos(&cc, TRUE, "a monster");
1095 if (ans < 0 || cc.x < 0) {
1096 return 0; /* done */
1098 /* Convert the glyph at the selected position to a mndxbol. */
1099 glyph = glyph_at(cc.x, cc.y);
1100 if (glyph_is_monster(glyph))
1101 mndx = glyph_to_mon(glyph);
1102 else
1103 mndx = 0;
1104 /* Is it a monster? */
1105 if (mndx) {
1106 if (!usmellmon(&mons[mndx]))
1107 pline("That monster seems to give off no smell.");
1108 } else
1109 pline("That is not a monster.");
1110 } while (TRUE);
1111 return 0;
1114 /* #wizinstrinsic command to set some intrinsics for testing */
1115 STATIC_PTR int
1116 wiz_intrinsic(VOID_ARGS)
1118 if (wizard) {
1119 winid win;
1120 anything any;
1121 int i, n, accelerator;
1122 menu_item *pick_list = (menu_item *) 0;
1124 static const char *const intrinsics[] = {
1125 "deafness",
1128 win = create_nhwindow(NHW_MENU);
1129 start_menu(win);
1130 accelerator = 0;
1132 for (i = 0; i < SIZE(intrinsics); ++i) {
1133 accelerator = intrinsics[i][0];
1134 any.a_int = i + 1;
1135 add_menu(win, NO_GLYPH, &any, accelerator, 0,
1136 ATR_NONE, intrinsics[i], FALSE);
1138 end_menu(win, "Which intrinsic?");
1139 n = select_menu(win, PICK_ONE, &pick_list);
1140 destroy_nhwindow(win);
1142 if (n >= 1) {
1143 i = pick_list[0].item.a_int-1;
1144 free((genericptr_t) pick_list);
1145 } else {
1146 return 0;
1149 if (!strcmp(intrinsics[i], "deafness")) {
1150 You("go deaf.");
1151 incr_itimeout(&HDeaf, 30);
1152 context.botl = TRUE;
1154 } else
1155 pline("Unavailable command '%s'.",
1156 visctrl((int) cmd_from_func(wiz_intrinsic)));
1157 return 0;
1160 /* #wizrumorcheck command - verify each rumor access */
1161 STATIC_PTR int
1162 wiz_rumor_check(VOID_ARGS)
1164 rumor_check();
1165 return 0;
1168 /* #terrain command -- show known map, inspired by crawl's '|' command */
1169 STATIC_PTR int
1170 doterrain(VOID_ARGS)
1172 winid men;
1173 menu_item *sel;
1174 anything any;
1175 int n;
1176 int which;
1179 * normal play: choose between known map without mons, obj, and traps
1180 * (to see underlying terrain only), or
1181 * known map without mons and objs (to see traps under mons and objs), or
1182 * known map without mons (to see objects under monsters);
1183 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1184 * wizard mode: normal and explore choices plus
1185 * a dump of the internal levl[][].typ codes w/ level flags, or
1186 * a legend for the levl[][].typ codes dump
1188 men = create_nhwindow(NHW_MENU);
1189 start_menu(men);
1190 any = zeroany;
1191 any.a_int = 1;
1192 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1193 "known map without monsters, objects, and traps",
1194 MENU_SELECTED);
1195 any.a_int = 2;
1196 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1197 "known map without monsters and objects",
1198 MENU_UNSELECTED);
1199 any.a_int = 3;
1200 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1201 "known map without monsters",
1202 MENU_UNSELECTED);
1203 if (discover || wizard) {
1204 any.a_int = 4;
1205 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1206 "full map without monsters, objects, and traps",
1207 MENU_UNSELECTED);
1208 if (wizard) {
1209 any.a_int = 5;
1210 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1211 "internal levl[][].typ codes in base-36",
1212 MENU_UNSELECTED);
1213 any.a_int = 6;
1214 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1215 "legend of base-36 levl[][].typ codes",
1216 MENU_UNSELECTED);
1219 end_menu(men, "View which?");
1221 n = select_menu(men, PICK_ONE, &sel);
1222 destroy_nhwindow(men);
1224 * n < 0: player used ESC to cancel;
1225 * n == 0: preselected entry was explicitly chosen and got toggled off;
1226 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1227 * n == 2: another entry was explicitly chosen, so skip preselected one.
1229 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1230 if (n > 1 && which == 1)
1231 which = sel[1].item.a_int;
1232 if (n > 0)
1233 free((genericptr_t) sel);
1235 switch (which) {
1236 case 1: /* known map */
1237 reveal_terrain(0, TER_MAP);
1238 break;
1239 case 2: /* known map with known traps */
1240 reveal_terrain(0, TER_MAP | TER_TRP);
1241 break;
1242 case 3: /* known map with known traps and objects */
1243 reveal_terrain(0, TER_MAP | TER_TRP | TER_OBJ);
1244 break;
1245 case 4: /* full map */
1246 reveal_terrain(1, TER_MAP);
1247 break;
1248 case 5: /* map internals */
1249 wiz_map_levltyp();
1250 break;
1251 case 6: /* internal details */
1252 wiz_levltyp_legend();
1253 break;
1254 default:
1255 break;
1257 return 0; /* no time elapses */
1260 /* -enlightenment and conduct- */
1261 static winid en_win = WIN_ERR;
1262 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1263 have[] = "have ", had[] = "had ", can[] = "can ",
1264 could[] = "could ";
1265 static const char have_been[] = "have been ", have_never[] = "have never ",
1266 never[] = "never ";
1268 #define enl_msg(prefix, present, past, suffix, ps) \
1269 enlght_line(prefix, final ? past : present, suffix, ps)
1270 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1271 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1272 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1273 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1274 #define you_have_never(badthing) \
1275 enl_msg(You_, have_never, never, badthing, "")
1276 #define you_have_X(something) \
1277 enl_msg(You_, have, (const char *) "", something, "")
1279 static void
1280 enlght_line(start, middle, end, ps)
1281 const char *start, *middle, *end, *ps;
1283 char buf[BUFSZ];
1285 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1286 putstr(en_win, 0, buf);
1289 /* format increased chance to hit or damage or defense (Protection) */
1290 static char *
1291 enlght_combatinc(inctyp, incamt, final, outbuf)
1292 const char *inctyp;
1293 int incamt, final;
1294 char *outbuf;
1296 const char *modif, *bonus;
1297 boolean invrt;
1298 int absamt;
1300 absamt = abs(incamt);
1301 /* Protection amount is typically larger than damage or to-hit;
1302 reduce magnitude by a third in order to stretch modifier ranges
1303 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1304 if (!strcmp(inctyp, "defense"))
1305 absamt = (absamt * 2) / 3;
1307 if (absamt <= 3)
1308 modif = "small";
1309 else if (absamt <= 6)
1310 modif = "moderate";
1311 else if (absamt <= 12)
1312 modif = "large";
1313 else
1314 modif = "huge";
1316 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1317 bonus = (incamt >= 0) ? "bonus" : "penalty";
1318 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1319 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1321 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1322 invrt ? bonus : inctyp);
1323 if (final || wizard)
1324 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1326 return outbuf;
1329 /* report half physical or half spell damage */
1330 STATIC_OVL void
1331 enlght_halfdmg(category, final)
1332 int category;
1333 int final;
1335 const char *category_name;
1336 char buf[BUFSZ];
1338 switch (category) {
1339 case HALF_PHDAM:
1340 category_name = "physical";
1341 break;
1342 case HALF_SPDAM:
1343 category_name = "spell";
1344 break;
1345 default:
1346 category_name = "unknown";
1347 break;
1349 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1350 category_name);
1351 enl_msg(You_, "take", "took", buf, from_what(category));
1354 /* is hero actively using water walking capability on water (or lava)? */
1355 STATIC_OVL boolean
1356 walking_on_water()
1358 if (u.uinwater || Levitation || Flying)
1359 return FALSE;
1360 return (boolean) (Wwalking
1361 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1364 /* check whether hero is wearing something that player definitely knows
1365 confers the target property; item must have been seen and its type
1366 discovered but it doesn't necessarily have to be fully identified */
1367 STATIC_OVL boolean
1368 cause_known(propindx)
1369 int propindx; /* index of a property which can be conveyed by worn item */
1371 register struct obj *o;
1372 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1374 /* simpler than from_what()/what_gives(); we don't attempt to
1375 handle artifacts and we deliberately ignore wielded items */
1376 for (o = invent; o; o = o->nobj) {
1377 if (!(o->owornmask & mask))
1378 continue;
1379 if ((int) objects[o->otyp].oc_oprop == propindx
1380 && objects[o->otyp].oc_name_known && o->dknown)
1381 return TRUE;
1383 return FALSE;
1386 /* format a characteristic value, accommodating Strength's strangeness */
1387 STATIC_OVL char *
1388 attrval(attrindx, attrvalue, resultbuf)
1389 int attrindx, attrvalue;
1390 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1392 if (attrindx != A_STR || attrvalue <= 18)
1393 Sprintf(resultbuf, "%d", attrvalue);
1394 else if (attrvalue > STR18(100)) /* 19 to 25 */
1395 Sprintf(resultbuf, "%d", attrvalue - 100);
1396 else /* simplify "18/ **" to be "18/100" */
1397 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1398 return resultbuf;
1401 void
1402 enlightenment(mode, final)
1403 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1404 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1406 char buf[BUFSZ], tmpbuf[BUFSZ];
1408 Strcpy(tmpbuf, plname);
1409 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1410 /* as in background_enlightenment, when poly'd we need to use the saved
1411 gender in u.mfemale rather than the current you-as-monster gender */
1412 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1413 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1414 ? urole.name.f
1415 : urole.name.m);
1417 en_win = create_nhwindow(NHW_MENU);
1418 /* title */
1419 putstr(en_win, 0, buf); /* "Conan the Archeologist's attributes:" */
1420 /* background and characteristics; ^X or end-of-game disclosure */
1421 if (mode & BASICENLIGHTENMENT) {
1422 /* role, race, alignment, deities */
1423 background_enlightenment(mode, final);
1424 /* strength, dexterity, &c */
1425 characteristics_enlightenment(mode, final);
1427 /* expanded status line information, including things which aren't
1428 included there due to space considerations--such as obvious
1429 alternative movement indicators (riding, levitation, &c), and
1430 various troubles (turning to stone, trapped, confusion, &c);
1431 shown for both basic and magic enlightenment */
1432 status_enlightenment(mode, final);
1433 /* remaining attributes; shown for potion,&c or wizard mode and
1434 explore mode ^X or end of game disclosure */
1435 if (mode & MAGICENLIGHTENMENT) {
1436 /* intrinsics and other traditional enlightenment feedback */
1437 attributes_enlightenment(mode, final);
1439 display_nhwindow(en_win, TRUE);
1440 destroy_nhwindow(en_win);
1441 en_win = WIN_ERR;
1444 /*ARGSUSED*/
1445 /* display role, race, alignment and such to en_win */
1446 STATIC_OVL void
1447 background_enlightenment(unused_mode, final)
1448 int unused_mode UNUSED;
1449 int final;
1451 const char *role_titl, *rank_titl;
1452 int innategend, difgend, difalgn;
1453 char buf[BUFSZ], tmpbuf[BUFSZ];
1455 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1456 to access hero's saved gender-as-human/elf/&c rather than current one */
1457 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1458 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1459 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1461 putstr(en_win, 0, ""); /* separator after title */
1462 putstr(en_win, 0, "Background:");
1464 /* if polymorphed, report current shape before underlying role;
1465 will be repeated as first status: "you are transformed" and also
1466 among various attributes: "you are in beast form" (after being
1467 told about lycanthropy) or "you are polymorphed into <a foo>"
1468 (with countdown timer appended for wizard mode); we really want
1469 the player to know he's not a samurai at the moment... */
1470 if (Upolyd) {
1471 struct permonst *uasmon = youmonst.data;
1473 tmpbuf[0] = '\0';
1474 /* here we always use current gender, not saved role gender */
1475 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1476 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1477 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1478 uasmon->mname);
1479 you_are(buf, "");
1482 /* report role; omit gender if it's redundant (eg, "female priestess") */
1483 tmpbuf[0] = '\0';
1484 if (!urole.name.f
1485 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1486 || innategend != flags.initgend))
1487 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1488 buf[0] = '\0';
1489 if (Upolyd)
1490 Strcpy(buf, "actually "); /* "You are actually a ..." */
1491 if (!strcmpi(rank_titl, role_titl)) {
1492 /* omit role when rank title matches it */
1493 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1494 tmpbuf, urace.noun);
1495 } else {
1496 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1497 tmpbuf, urace.adj, role_titl);
1499 you_are(buf, "");
1501 /* report alignment (bypass you_are() in order to omit ending period);
1502 adverb is used to distinguish between temporary change (helm of opp.
1503 alignment), permanent change (one-time conversion), and original */
1504 Sprintf(buf, " %s%s%s, %son a mission for %s",
1505 You_, !final ? are : were,
1506 align_str(u.ualign.type),
1507 /* helm of opposite alignment (might hide conversion) */
1508 (u.ualign.type != u.ualignbase[A_CURRENT])
1509 /* what's the past tense of "currently"? if we used "formerly"
1510 it would sound like a reference to the original alignment */
1511 ? (!final ? "currently " : "temporarily ")
1512 /* permanent conversion */
1513 : (u.ualign.type != u.ualignbase[A_ORIGINAL])
1514 /* and what's the past tense of "now"? certainly not "then"
1515 in a context like this...; "belatedly" == weren't that
1516 way sooner (in other words, didn't start that way) */
1517 ? (!final ? "now " : "belatedly ")
1518 /* atheist (ignored in very early game) */
1519 : (!u.uconduct.gnostic && moves > 1000L)
1520 ? "nominally "
1521 /* lastly, normal case */
1522 : "",
1523 u_gname());
1524 putstr(en_win, 0, buf);
1525 /* show the rest of this game's pantheon (finishes previous sentence)
1526 [appending "also Moloch" at the end would allow for straightforward
1527 trailing "and" on all three aligned entries but looks too verbose] */
1528 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1529 if (u.ualign.type != A_LAWFUL)
1530 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1531 align_str(A_LAWFUL));
1532 if (u.ualign.type != A_NEUTRAL)
1533 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1534 align_str(A_NEUTRAL),
1535 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1536 if (u.ualign.type != A_CHAOTIC)
1537 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1538 align_str(A_CHAOTIC));
1539 Strcat(buf, "."); /* terminate sentence */
1540 putstr(en_win, 0, buf);
1542 /* show original alignment,gender,race,role if any have been changed;
1543 giving separate message for temporary alignment change bypasses need
1544 for tricky phrasing otherwise necessitated by possibility of having
1545 helm of opposite alignment mask a permanent alignment conversion */
1546 difgend = (innategend != flags.initgend);
1547 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1548 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1549 ? 2 : 0));
1550 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1551 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1552 you_are(buf, "");
1553 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1555 if (difgend || difalgn) { /* sex change or perm align change or both */
1556 Sprintf(buf, " You started out %s%s%s.",
1557 difgend ? genders[flags.initgend].adj : "",
1558 (difgend && difalgn) ? " and " : "",
1559 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1560 putstr(en_win, 0, buf);
1564 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1565 STATIC_OVL void
1566 characteristics_enlightenment(mode, final)
1567 int mode;
1568 int final;
1570 putstr(en_win, 0, ""); /* separator after background */
1571 putstr(en_win, 0,
1572 final ? "Final Characteristics:" : "Current Characteristics:");
1574 /* bottom line order */
1575 one_characteristic(mode, final, A_STR); /* strength */
1576 one_characteristic(mode, final, A_DEX); /* dexterity */
1577 one_characteristic(mode, final, A_CON); /* constitution */
1578 one_characteristic(mode, final, A_INT); /* intelligence */
1579 one_characteristic(mode, final, A_WIS); /* wisdom */
1580 one_characteristic(mode, final, A_CHA); /* charisma */
1583 /* display one attribute value for characteristics_enlightenment() */
1584 STATIC_OVL void
1585 one_characteristic(mode, final, attrindx)
1586 int mode, final, attrindx;
1588 boolean hide_innate_value = FALSE, interesting_alimit;
1589 int acurrent, abase, apeak, alimit;
1590 const char *attrname, *paren_pfx;
1591 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
1593 /* being polymorphed or wearing certain cursed items prevents
1594 hero from reliably tracking changes to characteristics so
1595 we don't show base & peak values then; when the items aren't
1596 cursed, hero could take them off to check underlying values
1597 and we show those in such case so that player doesn't need
1598 to actually resort to doing that */
1599 if (Upolyd) {
1600 hide_innate_value = TRUE;
1601 } else if (Fixed_abil) {
1602 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
1603 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
1604 hide_innate_value = TRUE;
1606 switch (attrindx) {
1607 case A_STR:
1608 attrname = "strength";
1609 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
1610 hide_innate_value = TRUE;
1611 break;
1612 case A_DEX:
1613 attrname = "dexterity";
1614 break;
1615 case A_CON:
1616 attrname = "constitution";
1617 break;
1618 case A_INT:
1619 attrname = "intelligence";
1620 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1621 hide_innate_value = TRUE;
1622 break;
1623 case A_WIS:
1624 attrname = "wisdom";
1625 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1626 hide_innate_value = TRUE;
1627 break;
1628 case A_CHA:
1629 attrname = "charisma";
1630 break;
1631 default:
1632 return; /* impossible */
1634 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1635 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
1636 hide_innate_value = FALSE;
1638 acurrent = ACURR(attrindx);
1639 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
1640 Sprintf(subjbuf, "Your %s ", attrname);
1642 if (!hide_innate_value) {
1643 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1644 (a magic bonus or penalty is in effect) or abase doesn't match
1645 amax (some points have been lost to poison or exercise abuse
1646 and are restorable) or attrmax is different from normal human
1647 (while game is in progress; trying to reduce dependency on
1648 spoilers to keep track of such stuff) or attrmax was different
1649 from abase (at end of game; this attribute wasn't maxed out) */
1650 abase = ABASE(attrindx);
1651 apeak = AMAX(attrindx);
1652 alimit = ATTRMAX(attrindx);
1653 /* criterium for whether the limit is interesting varies */
1654 interesting_alimit =
1655 final ? TRUE /* was originally `(abase != alimit)' */
1656 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
1657 paren_pfx = final ? " (" : " (current; ";
1658 if (acurrent != abase) {
1659 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
1660 attrval(attrindx, abase, valstring));
1661 paren_pfx = ", ";
1663 if (abase != apeak) {
1664 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
1665 attrval(attrindx, apeak, valstring));
1666 paren_pfx = ", ";
1668 if (interesting_alimit) {
1669 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
1670 /* more verbose if exceeding 'limit' due to magic bonus */
1671 (acurrent > alimit) ? "innate " : "",
1672 attrval(attrindx, alimit, valstring));
1673 /* paren_pfx = ", "; */
1675 if (acurrent != abase || abase != apeak || interesting_alimit)
1676 Strcat(valubuf, ")");
1678 enl_msg(subjbuf, "is ", "was ", valubuf, "");
1681 /* status: selected obvious capabilities, assorted troubles */
1682 STATIC_OVL void
1683 status_enlightenment(mode, final)
1684 int mode;
1685 int final;
1687 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
1688 int cap;
1689 char buf[BUFSZ], youtoo[BUFSZ];
1690 boolean Riding = (u.usteed
1691 /* if hero dies while dismounting, u.usteed will still
1692 be set; we want to ignore steed in that situation */
1693 && !(final == ENL_GAMEOVERDEAD
1694 && !strcmp(killer.name, "riding accident")));
1695 const char *steedname = (!Riding ? (char *) 0
1696 : x_monnam(u.usteed,
1697 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
1698 (char *) 0,
1699 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
1700 FALSE));
1703 * Status (many are abbreviated on bottom line; others are or
1704 * should be discernible to the hero hence to the player)
1706 putstr(en_win, 0, ""); /* separator after title or characteristics */
1707 putstr(en_win, 0, final ? "Final Status:" : "Current Status:");
1709 Strcpy(youtoo, You_);
1710 /* not a traditional status but inherently obvious to player; more
1711 detail given below (attributes section) for magic enlightenment */
1712 if (Upolyd)
1713 you_are("transformed", "");
1714 /* not a trouble, but we want to display riding status before maybe
1715 reporting steed as trapped or hero stuck to cursed saddle */
1716 if (Riding) {
1717 Sprintf(buf, "riding %s", steedname);
1718 you_are(buf, "");
1719 Sprintf(eos(youtoo), "and %s ", steedname);
1721 /* other movement situations that hero should always know */
1722 if (Levitation) {
1723 if (Lev_at_will && magic)
1724 you_are("levitating, at will", "");
1725 else
1726 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
1727 } else if (Flying) { /* can only fly when not levitating */
1728 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
1730 if (Underwater) {
1731 you_are("underwater", "");
1732 } else if (u.uinwater) {
1733 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
1734 } else if (walking_on_water()) {
1735 /* show active Wwalking here, potential Wwalking elsewhere */
1736 Sprintf(buf, "walking on %s",
1737 is_pool(u.ux, u.uy) ? "water"
1738 : is_lava(u.ux, u.uy) ? "lava"
1739 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
1740 you_are(buf, from_what(WWALKING));
1742 if (Upolyd && (u.uundetected || youmonst.m_ap_type != M_AP_NOTHING))
1743 youhiding(TRUE, final);
1745 /* internal troubles, mostly in the order that prayer ranks them */
1746 if (Stoned)
1747 you_are("turning to stone", "");
1748 if (Slimed)
1749 you_are("turning into slime", "");
1750 if (Strangled) {
1751 if (u.uburied) {
1752 you_are("buried", "");
1753 } else {
1754 Strcpy(buf, "being strangled");
1755 if (wizard)
1756 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
1757 you_are(buf, from_what(STRANGLED));
1760 if (Sick) {
1761 /* prayer lumps these together; botl puts Ill before FoodPois */
1762 if (u.usick_type & SICK_NONVOMITABLE)
1763 you_are("terminally sick from illness", "");
1764 if (u.usick_type & SICK_VOMITABLE)
1765 you_are("terminally sick from food poisoning", "");
1767 if (Vomiting)
1768 you_are("nauseated", "");
1769 if (Stunned)
1770 you_are("stunned", "");
1771 if (Confusion)
1772 you_are("confused", "");
1773 if (Hallucination)
1774 you_are("hallucinating", "");
1775 if (Blind) {
1776 /* from_what() (currently wizard-mode only) checks !haseyes()
1777 before u.uroleplay.blind, so we should too */
1778 Sprintf(buf, "%s blind",
1779 !haseyes(youmonst.data) ? "innately"
1780 : u.uroleplay.blind ? "permanently"
1781 /* better phrasing desperately wanted... */
1782 : Blindfolded_only ? "deliberately"
1783 : "temporarily");
1784 if (wizard && (Blinded & TIMEOUT) != 0L
1785 && !u.uroleplay.blind && haseyes(youmonst.data))
1786 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
1787 /* !haseyes: avoid "you are innately blind innately" */
1788 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
1790 if (Deaf)
1791 you_are("deaf", from_what(DEAF));
1793 /* external troubles, more or less */
1794 if (Punished) {
1795 if (uball) {
1796 Sprintf(buf, "chained to %s", ansimpleoname(uball));
1797 } else {
1798 impossible("Punished without uball?");
1799 Strcpy(buf, "punished");
1801 you_are(buf, "");
1803 if (u.utrap) {
1804 char predicament[BUFSZ];
1805 struct trap *t;
1806 boolean anchored = (u.utraptype == TT_BURIEDBALL);
1808 if (anchored) {
1809 Strcpy(predicament, "tethered to something buried");
1810 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
1811 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
1812 } else {
1813 Strcpy(predicament, "trapped");
1814 if ((t = t_at(u.ux, u.uy)) != 0)
1815 Sprintf(eos(predicament), " in %s",
1816 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
1818 if (u.usteed) { /* not `Riding' here */
1819 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
1820 *buf = highc(*buf);
1821 enl_msg(buf, (anchored ? "are " : "is "),
1822 (anchored ? "were " : "was "), predicament, "");
1823 } else
1824 you_are(predicament, "");
1825 } /* (u.utrap) */
1826 if (u.uswallow) {
1827 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
1828 if (wizard)
1829 Sprintf(eos(buf), " (%u)", u.uswldtim);
1830 you_are(buf, "");
1831 } else if (u.ustuck) {
1832 Sprintf(buf, "%s %s",
1833 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
1834 a_monnam(u.ustuck));
1835 you_are(buf, "");
1837 if (Riding) {
1838 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
1840 if (saddle && saddle->cursed) {
1841 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
1842 simpleonames(saddle));
1843 you_are(buf, "");
1846 if (Wounded_legs) {
1847 /* when mounted, Wounded_legs applies to steed rather than to
1848 hero; we only report steed's wounded legs in wizard mode */
1849 if (u.usteed) { /* not `Riding' here */
1850 if (wizard && steedname) {
1851 Strcpy(buf, steedname);
1852 *buf = highc(*buf);
1853 enl_msg(buf, " has", " had", " wounded legs", "");
1855 } else {
1856 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
1857 you_have(buf, "");
1860 if (Glib) {
1861 Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
1862 you_have(buf, "");
1864 if (Fumbling) {
1865 if (magic || cause_known(FUMBLING))
1866 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
1868 if (Sleepy) {
1869 if (magic || cause_known(SLEEPY)) {
1870 Strcpy(buf, from_what(SLEEPY));
1871 if (wizard)
1872 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
1873 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
1876 /* hunger/nutrition */
1877 if (Hunger) {
1878 if (magic || cause_known(HUNGER))
1879 enl_msg(You_, "hunger", "hungered", " rapidly",
1880 from_what(HUNGER));
1882 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
1883 mungspaces(buf); /* strip trailing spaces */
1884 if (*buf) {
1885 *buf = lowc(*buf); /* override capitalization */
1886 if (!strcmp(buf, "weak"))
1887 Strcat(buf, " from severe hunger");
1888 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
1889 Strcat(buf, " due to starvation");
1890 you_are(buf, "");
1892 /* encumbrance */
1893 if ((cap = near_capacity()) > UNENCUMBERED) {
1894 const char *adj = "?_?"; /* (should always get overridden) */
1896 Strcpy(buf, enc_stat[cap]);
1897 *buf = lowc(*buf);
1898 switch (cap) {
1899 case SLT_ENCUMBER:
1900 adj = "slightly";
1901 break; /* burdened */
1902 case MOD_ENCUMBER:
1903 adj = "moderately";
1904 break; /* stressed */
1905 case HVY_ENCUMBER:
1906 adj = "very";
1907 break; /* strained */
1908 case EXT_ENCUMBER:
1909 adj = "extremely";
1910 break; /* overtaxed */
1911 case OVERLOADED:
1912 adj = "not possible";
1913 break;
1915 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
1916 (cap < OVERLOADED) ? " slowed" : "");
1917 you_are(buf, "");
1918 } else {
1919 /* last resort entry, guarantees Status section is non-empty
1920 (no longer needed for that purpose since weapon status added;
1921 still useful though) */
1922 you_are("unencumbered", "");
1924 /* report being weaponless; distinguish whether gloves are worn */
1925 if (!uwep) {
1926 you_are(uarmg ? "empty handed" /* gloves imply hands */
1927 : humanoid(youmonst.data)
1928 /* hands but no weapon and no gloves */
1929 ? "bare handed"
1930 /* alternate phrasing for paws or lack of hands */
1931 : "not wielding anything",
1932 "");
1933 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1934 } else if (u.twoweap) {
1935 you_are("wielding two weapons at once", "");
1936 /* report most weapons by their skill class (so a katana will be
1937 described as a long sword, for instance; mattock and hook are
1938 exceptions), or wielded non-weapon item by its object class */
1939 } else {
1940 const char *what = weapon_descr(uwep);
1942 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
1943 || !strcmpi(what, "venom"))
1944 Sprintf(buf, "wielding some %s", what);
1945 else
1946 Sprintf(buf, "wielding %s",
1947 (uwep->quan == 1L) ? an(what) : makeplural(what));
1948 you_are(buf, "");
1950 /* report 'nudity' */
1951 if (!uarm && !uarmu && !uarmc && !uarmg && !uarmf && !uarmh) {
1952 if (u.uroleplay.nudist)
1953 enl_msg(You_, "do", "did", " not wear any armor", "");
1954 else
1955 you_are("not wearing any armor", "");
1959 /* attributes: intrinsics and the like, other non-obvious capabilities */
1960 void
1961 attributes_enlightenment(unused_mode, final)
1962 int unused_mode UNUSED;
1963 int final;
1965 static NEARDATA const char if_surroundings_permitted[] =
1966 " if surroundings permitted";
1967 int ltmp, armpro;
1968 char buf[BUFSZ];
1971 * Attributes
1973 putstr(en_win, 0, "");
1974 putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
1976 if (u.uevent.uhand_of_elbereth) {
1977 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
1978 "the Envoy of Balance",
1979 "the Glory of Arioch" };
1980 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
1983 Sprintf(buf, "%s", piousness(TRUE, "aligned"));
1984 if (u.ualign.record >= 0)
1985 you_are(buf, "");
1986 else
1987 you_have(buf, "");
1989 if (wizard) {
1990 Sprintf(buf, " %d", u.ualign.record);
1991 enl_msg("Your alignment ", "is", "was", buf, "");
1994 /*** Resistances to troubles ***/
1995 if (Invulnerable)
1996 you_are("invulnerable", from_what(INVULNERABLE));
1997 if (Antimagic)
1998 you_are("magic-protected", from_what(ANTIMAGIC));
1999 if (Fire_resistance)
2000 you_are("fire resistant", from_what(FIRE_RES));
2001 if (Cold_resistance)
2002 you_are("cold resistant", from_what(COLD_RES));
2003 if (Sleep_resistance)
2004 you_are("sleep resistant", from_what(SLEEP_RES));
2005 if (Disint_resistance)
2006 you_are("disintegration-resistant", from_what(DISINT_RES));
2007 if (Shock_resistance)
2008 you_are("shock resistant", from_what(SHOCK_RES));
2009 if (Poison_resistance)
2010 you_are("poison resistant", from_what(POISON_RES));
2011 if (Acid_resistance)
2012 you_are("acid resistant", from_what(ACID_RES));
2013 if (Drain_resistance)
2014 you_are("level-drain resistant", from_what(DRAIN_RES));
2015 if (Sick_resistance)
2016 you_are("immune to sickness", from_what(SICK_RES));
2017 if (Stone_resistance)
2018 you_are("petrification resistant", from_what(STONE_RES));
2019 if (Halluc_resistance)
2020 enl_msg(You_, "resist", "resisted", " hallucinations",
2021 from_what(HALLUC_RES));
2022 if (u.uedibility)
2023 you_can("recognize detrimental food", "");
2025 /*** Vision and senses ***/
2026 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2027 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2028 if (See_invisible) {
2029 if (!Blind)
2030 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2031 else
2032 enl_msg(You_, "will see", "would have seen",
2033 " invisible when not blind", from_what(SEE_INVIS));
2035 if (Blind_telepat)
2036 you_are("telepathic", from_what(TELEPAT));
2037 if (Warning)
2038 you_are("warned", from_what(WARNING));
2039 if (Warn_of_mon && context.warntype.obj) {
2040 Sprintf(buf, "aware of the presence of %s",
2041 (context.warntype.obj & M2_ORC) ? "orcs"
2042 : (context.warntype.obj & M2_ELF) ? "elves"
2043 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2044 you_are(buf, from_what(WARN_OF_MON));
2046 if (Warn_of_mon && context.warntype.polyd) {
2047 Sprintf(buf, "aware of the presence of %s",
2048 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2049 == (M2_HUMAN | M2_ELF))
2050 ? "humans and elves"
2051 : (context.warntype.polyd & M2_HUMAN)
2052 ? "humans"
2053 : (context.warntype.polyd & M2_ELF)
2054 ? "elves"
2055 : (context.warntype.polyd & M2_ORC)
2056 ? "orcs"
2057 : (context.warntype.polyd & M2_DEMON)
2058 ? "demons"
2059 : "certain monsters");
2060 you_are(buf, "");
2062 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2063 Sprintf(buf, "aware of the presence of %s",
2064 makeplural(mons[context.warntype.speciesidx].mname));
2065 you_are(buf, from_what(WARN_OF_MON));
2067 if (Undead_warning)
2068 you_are("warned of undead", from_what(WARN_UNDEAD));
2069 if (Searching)
2070 you_have("automatic searching", from_what(SEARCHING));
2071 if (Clairvoyant)
2072 you_are("clairvoyant", from_what(CLAIRVOYANT));
2073 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2074 Strcpy(buf, from_what(-CLAIRVOYANT));
2075 if (!strncmp(buf, " because of ", 12))
2076 /* overwrite substring; strncpy doesn't add terminator */
2077 (void) strncpy(buf, " if not for ", 12);
2078 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2080 if (Infravision)
2081 you_have("infravision", from_what(INFRAVISION));
2082 if (Detect_monsters)
2083 you_are("sensing the presence of monsters", "");
2084 if (u.umconf)
2085 you_are("going to confuse monsters", "");
2087 /*** Appearance and behavior ***/
2088 if (Adornment) {
2089 int adorn = 0;
2091 if (uleft && uleft->otyp == RIN_ADORNMENT)
2092 adorn += uleft->spe;
2093 if (uright && uright->otyp == RIN_ADORNMENT)
2094 adorn += uright->spe;
2095 /* the sum might be 0 (+0 ring or two which negate each other);
2096 that yields "you are charismatic" (which isn't pointless
2097 because it potentially impacts seduction attacks) */
2098 Sprintf(buf, "%scharismatic",
2099 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2100 you_are(buf, from_what(ADORNED));
2102 if (Invisible)
2103 you_are("invisible", from_what(INVIS));
2104 else if (Invis)
2105 you_are("invisible to others", from_what(INVIS));
2106 /* ordinarily "visible" is redundant; this is a special case for
2107 the situation when invisibility would be an expected attribute */
2108 else if ((HInvis || EInvis) && BInvis)
2109 you_are("visible", from_what(-INVIS));
2110 if (Displaced)
2111 you_are("displaced", from_what(DISPLACED));
2112 if (Stealth)
2113 you_are("stealthy", from_what(STEALTH));
2114 if (Aggravate_monster)
2115 enl_msg("You aggravate", "", "d", " monsters",
2116 from_what(AGGRAVATE_MONSTER));
2117 if (Conflict)
2118 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2120 /*** Transportation ***/
2121 if (Jumping)
2122 you_can("jump", from_what(JUMPING));
2123 if (Teleportation)
2124 you_can("teleport", from_what(TELEPORT));
2125 if (Teleport_control)
2126 you_have("teleport control", from_what(TELEPORT_CONTROL));
2127 /* actively levitating handled earlier as a status condition */
2128 if (BLevitation) { /* levitation is blocked */
2129 long save_BLev = BLevitation;
2131 BLevitation = 0L;
2132 if (Levitation)
2133 enl_msg(You_, "would levitate", "would have levitated",
2134 if_surroundings_permitted, "");
2135 BLevitation = save_BLev;
2137 /* actively flying handled earlier as a status condition */
2138 if (BFlying) { /* flight is blocked */
2139 long save_BFly = BFlying;
2141 BFlying = 0L;
2142 if (Flying)
2143 enl_msg(You_, "would fly", "would have flown",
2144 Levitation
2145 ? "if you weren't levitating"
2146 : (save_BFly == FROMOUTSIDE)
2147 ? if_surroundings_permitted
2148 /* both surroundings and [latent] levitation */
2149 : " if circumstances permitted",
2150 "");
2151 BFlying = save_BFly;
2153 /* actively walking on water handled earlier as a status condition */
2154 if (Wwalking && !walking_on_water())
2155 you_can("walk on water", from_what(WWALKING));
2156 /* actively swimming (in water but not under it) handled earlier */
2157 if (Swimming && (Underwater || !u.uinwater))
2158 you_can("swim", from_what(SWIMMING));
2159 if (Breathless)
2160 you_can("survive without air", from_what(MAGICAL_BREATHING));
2161 else if (Amphibious)
2162 you_can("breathe water", from_what(MAGICAL_BREATHING));
2163 if (Passes_walls)
2164 you_can("walk through walls", from_what(PASSES_WALLS));
2166 /*** Physical attributes ***/
2167 if (Regeneration)
2168 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2169 if (Slow_digestion)
2170 you_have("slower digestion", from_what(SLOW_DIGESTION));
2171 if (u.uhitinc)
2172 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2173 if (u.udaminc)
2174 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2175 if (u.uspellprot || Protection) {
2176 int prot = 0;
2178 if (uleft && uleft->otyp == RIN_PROTECTION)
2179 prot += uleft->spe;
2180 if (uright && uright->otyp == RIN_PROTECTION)
2181 prot += uright->spe;
2182 if (HProtection & INTRINSIC)
2183 prot += u.ublessed;
2184 prot += u.uspellprot;
2185 if (prot)
2186 you_have(enlght_combatinc("defense", prot, final, buf), "");
2188 if ((armpro = magic_negation(&youmonst)) > 0) {
2189 /* magic cancellation factor, conferred by worn armor */
2190 static const char *const mc_types[] = {
2191 "" /*ordinary*/, "warded", "guarded", "protected",
2193 /* sanity check */
2194 if (armpro >= SIZE(mc_types))
2195 armpro = SIZE(mc_types) - 1;
2196 you_are(mc_types[armpro], "");
2198 if (Half_physical_damage)
2199 enlght_halfdmg(HALF_PHDAM, final);
2200 if (Half_spell_damage)
2201 enlght_halfdmg(HALF_SPDAM, final);
2202 /* polymorph and other shape change */
2203 if (Protection_from_shape_changers)
2204 you_are("protected from shape changers",
2205 from_what(PROT_FROM_SHAPE_CHANGERS));
2206 if (Unchanging) {
2207 const char *what = 0;
2209 if (!Upolyd) /* Upolyd handled below after current form */
2210 you_can("not change from your current form",
2211 from_what(UNCHANGING));
2212 /* blocked shape changes */
2213 if (Polymorph)
2214 what = !final ? "polymorph" : "have polymorphed";
2215 else if (u.ulycn >= LOW_PM)
2216 what = !final ? "change shape" : "have changed shape";
2217 if (what) {
2218 Sprintf(buf, "would %s periodically", what);
2219 /* omit from_what(UNCHANGING); too verbose */
2220 enl_msg(You_, buf, buf, " if not locked into your current form",
2221 "");
2223 } else if (Polymorph) {
2224 you_are("polymorphing periodically", from_what(POLYMORPH));
2226 if (Polymorph_control)
2227 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2228 if (Upolyd && u.umonnum != u.ulycn) {
2229 /* foreign shape (except were-form which is handled below) */
2230 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2231 if (wizard)
2232 Sprintf(eos(buf), " (%d)", u.mtimedone);
2233 you_are(buf, "");
2235 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2236 you_can("lay eggs", "");
2237 if (u.ulycn >= LOW_PM) {
2238 /* "you are a werecreature [in beast form]" */
2239 Strcpy(buf, an(mons[u.ulycn].mname));
2240 if (u.umonnum == u.ulycn) {
2241 Strcat(buf, " in beast form");
2242 if (wizard)
2243 Sprintf(eos(buf), " (%d)", u.mtimedone);
2245 you_are(buf, "");
2247 if (Unchanging && Upolyd) /* !Upolyd handled above */
2248 you_can("not change from your current form", from_what(UNCHANGING));
2249 if (Hate_silver)
2250 you_are("harmed by silver", "");
2251 /* movement and non-armor-based protection */
2252 if (Fast)
2253 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2254 if (Reflecting)
2255 you_have("reflection", from_what(REFLECTING));
2256 if (Free_action)
2257 you_have("free action", from_what(FREE_ACTION));
2258 if (Fixed_abil)
2259 you_have("fixed abilities", from_what(FIXED_ABIL));
2260 if (Lifesaved)
2261 enl_msg("Your life ", "will be", "would have been", " saved", "");
2263 /*** Miscellany ***/
2264 if (Luck) {
2265 ltmp = abs((int) Luck);
2266 Sprintf(buf, "%s%slucky",
2267 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2268 Luck < 0 ? "un" : "");
2269 if (wizard)
2270 Sprintf(eos(buf), " (%d)", Luck);
2271 you_are(buf, "");
2272 } else if (wizard)
2273 enl_msg("Your luck ", "is", "was", " zero", "");
2274 if (u.moreluck > 0)
2275 you_have("extra luck", "");
2276 else if (u.moreluck < 0)
2277 you_have("reduced luck", "");
2278 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2279 ltmp = stone_luck(0);
2280 if (ltmp <= 0)
2281 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2282 if (ltmp >= 0)
2283 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2286 if (u.ugangr) {
2287 Sprintf(buf, " %sangry with you",
2288 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2289 if (wizard)
2290 Sprintf(eos(buf), " (%d)", u.ugangr);
2291 enl_msg(u_gname(), " is", " was", buf, "");
2292 } else {
2294 * We need to suppress this when the game is over, because death
2295 * can change the value calculated by can_pray(), potentially
2296 * resulting in a false claim that you could have prayed safely.
2298 if (!final) {
2299 #if 0
2300 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2301 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2302 final ? "have " : "", final ? "ed" : "");
2303 #else
2304 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2305 #endif
2306 if (wizard)
2307 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2308 you_can(buf, "");
2312 #ifdef DEBUG
2313 /* named fruit debugging (doesn't really belong here...); to enable,
2314 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2315 if (wizard && explicitdebug("fruit")) {
2316 int fcount = 0;
2317 struct fruit *f;
2318 char buf2[BUFSZ];
2320 for (f = ffruit; f; f = f->nextf) {
2321 Sprintf(buf, "Fruit %d ", ++fcount);
2322 Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
2323 enl_msg(buf, "is ", "was ", buf2, "");
2325 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
2326 Sprintf(buf, "%d", flags.made_fruit);
2327 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
2329 #endif
2332 const char *p;
2334 buf[0] = '\0';
2335 if (final < 2) { /* still in progress, or quit/escaped/ascended */
2336 p = "survived after being killed ";
2337 switch (u.umortality) {
2338 case 0:
2339 p = !final ? (char *) 0 : "survived";
2340 break;
2341 case 1:
2342 Strcpy(buf, "once");
2343 break;
2344 case 2:
2345 Strcpy(buf, "twice");
2346 break;
2347 case 3:
2348 Strcpy(buf, "thrice");
2349 break;
2350 default:
2351 Sprintf(buf, "%d times", u.umortality);
2352 break;
2354 } else { /* game ended in character's death */
2355 p = "are dead";
2356 switch (u.umortality) {
2357 case 0:
2358 impossible("dead without dying?");
2359 case 1:
2360 break; /* just "are dead" */
2361 default:
2362 Sprintf(buf, " (%d%s time!)", u.umortality,
2363 ordin(u.umortality));
2364 break;
2367 if (p)
2368 enl_msg(You_, "have been killed ", p, buf, "");
2372 #if 0 /* no longer used */
2373 STATIC_DCL boolean NDECL(minimal_enlightenment);
2376 * Courtesy function for non-debug, non-explorer mode players
2377 * to help refresh them about who/what they are.
2378 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2380 STATIC_OVL boolean
2381 minimal_enlightenment()
2383 winid tmpwin;
2384 menu_item *selected;
2385 anything any;
2386 int genidx, n;
2387 char buf[BUFSZ], buf2[BUFSZ];
2388 static const char untabbed_fmtstr[] = "%-15s: %-12s";
2389 static const char untabbed_deity_fmtstr[] = "%-17s%s";
2390 static const char tabbed_fmtstr[] = "%s:\t%-12s";
2391 static const char tabbed_deity_fmtstr[] = "%s\t%s";
2392 static const char *fmtstr;
2393 static const char *deity_fmtstr;
2395 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
2396 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
2397 : untabbed_deity_fmtstr;
2398 any = zeroany;
2399 buf[0] = buf2[0] = '\0';
2400 tmpwin = create_nhwindow(NHW_MENU);
2401 start_menu(tmpwin);
2402 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2403 "Starting", FALSE);
2405 /* Starting name, race, role, gender */
2406 Sprintf(buf, fmtstr, "name", plname);
2407 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2408 Sprintf(buf, fmtstr, "race", urace.noun);
2409 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2410 Sprintf(buf, fmtstr, "role",
2411 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
2412 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2413 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
2414 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2416 /* Starting alignment */
2417 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
2418 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2420 /* Current name, race, role, gender */
2421 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2422 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2423 "Current", FALSE);
2424 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
2425 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2426 if (Upolyd) {
2427 Sprintf(buf, fmtstr, "role (base)",
2428 (u.mfemale && urole.name.f) ? urole.name.f
2429 : urole.name.m);
2430 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2431 } else {
2432 Sprintf(buf, fmtstr, "role",
2433 (flags.female && urole.name.f) ? urole.name.f
2434 : urole.name.m);
2435 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2437 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2438 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
2439 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
2440 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2441 if (Upolyd && (int) u.mfemale != genidx) {
2442 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
2443 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2446 /* Current alignment */
2447 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
2448 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2450 /* Deity list */
2451 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2452 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2453 "Deities", FALSE);
2454 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
2455 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2456 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
2457 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
2458 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
2459 Sprintf(buf, fmtstr, "Chaotic", buf2);
2460 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2462 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
2463 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2464 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
2465 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
2466 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
2467 Sprintf(buf, fmtstr, "Neutral", buf2);
2468 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2470 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
2471 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2472 && u.ualign.type == A_LAWFUL) ? " (s,c)"
2473 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
2474 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
2475 Sprintf(buf, fmtstr, "Lawful", buf2);
2476 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2478 end_menu(tmpwin, "Base Attributes");
2479 n = select_menu(tmpwin, PICK_NONE, &selected);
2480 destroy_nhwindow(tmpwin);
2481 return (boolean) (n != -1);
2483 #endif /*0*/
2485 /* ^X command */
2486 STATIC_PTR int
2487 doattributes(VOID_ARGS)
2489 int mode = BASICENLIGHTENMENT;
2491 /* show more--as if final disclosure--for wizard and explore modes */
2492 if (wizard || discover)
2493 mode |= MAGICENLIGHTENMENT;
2495 enlightenment(mode, ENL_GAMEINPROGRESS);
2496 return 0;
2499 void
2500 youhiding(via_enlghtmt, msgflag)
2501 boolean via_enlghtmt; /* englightment line vs topl message */
2502 int msgflag; /* for variant message phrasing */
2504 char *bp, buf[BUFSZ];
2506 Strcpy(buf, "hiding");
2507 if (youmonst.m_ap_type != M_AP_NOTHING) {
2508 /* mimic; hero is only able to mimic a strange object or gold
2509 or hallucinatory alternative to gold, so we skip the details
2510 for the hypothetical furniture and monster cases */
2511 bp = eos(strcpy(buf, "mimicking"));
2512 if (youmonst.m_ap_type == M_AP_OBJECT) {
2513 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
2514 } else if (youmonst.m_ap_type == M_AP_FURNITURE) {
2515 Strcpy(bp, " something");
2516 } else if (youmonst.m_ap_type == M_AP_MONSTER) {
2517 Strcpy(bp, " someone");
2518 } else {
2519 ; /* something unexpected; leave 'buf' as-is */
2521 } else if (u.uundetected) {
2522 bp = eos(buf); /* points past "hiding" */
2523 if (youmonst.data->mlet == S_EEL) {
2524 if (is_pool(u.ux, u.uy))
2525 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
2526 } else if (hides_under(youmonst.data)) {
2527 struct obj *o = level.objects[u.ux][u.uy];
2529 if (o)
2530 Sprintf(bp, " underneath %s", ansimpleoname(o));
2531 } else if (is_clinger(youmonst.data) || Flying) {
2532 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2533 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
2534 } else {
2535 /* on floor; is_hider() but otherwise not special: 'trapper' */
2536 if (u.utrap && u.utraptype == TT_PIT) {
2537 struct trap *t = t_at(u.ux, u.uy);
2539 Sprintf(bp, " in a %spit",
2540 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
2541 } else
2542 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
2544 } else {
2545 ; /* shouldn't happen; will result in generic "you are hiding" */
2548 if (via_enlghtmt) {
2549 int final = msgflag; /* 'final' is used by you_are() macro */
2551 you_are(buf, "");
2552 } else {
2553 /* for dohide(), when player uses '#monster' command */
2554 You("are %s %s.", msgflag ? "already" : "now", buf);
2558 /* KMH, #conduct
2559 * (shares enlightenment's tense handling)
2561 STATIC_PTR int
2562 doconduct(VOID_ARGS)
2564 show_conduct(0);
2565 return 0;
2568 void
2569 show_conduct(final)
2570 int final;
2572 char buf[BUFSZ];
2573 int ngenocided;
2575 /* Create the conduct window */
2576 en_win = create_nhwindow(NHW_MENU);
2577 putstr(en_win, 0, "Voluntary challenges:");
2579 if (u.uroleplay.blind)
2580 you_have_been("blind from birth");
2581 if (u.uroleplay.nudist)
2582 you_have_been("faithfully nudist");
2584 if (!u.uconduct.food)
2585 enl_msg(You_, "have gone", "went", " without food", "");
2586 /* But beverages are okay */
2587 else if (!u.uconduct.unvegan)
2588 you_have_X("followed a strict vegan diet");
2589 else if (!u.uconduct.unvegetarian)
2590 you_have_been("vegetarian");
2592 if (!u.uconduct.gnostic)
2593 you_have_been("an atheist");
2595 if (!u.uconduct.weaphit) {
2596 you_have_never("hit with a wielded weapon");
2597 } else if (wizard) {
2598 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
2599 plur(u.uconduct.weaphit));
2600 you_have_X(buf);
2602 if (!u.uconduct.killer)
2603 you_have_been("a pacifist");
2605 if (!u.uconduct.literate) {
2606 you_have_been("illiterate");
2607 } else if (wizard) {
2608 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
2609 plur(u.uconduct.literate));
2610 you_have_X(buf);
2613 ngenocided = num_genocides();
2614 if (ngenocided == 0) {
2615 you_have_never("genocided any monsters");
2616 } else {
2617 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
2618 plur(ngenocided), plur(ngenocided));
2619 you_have_X(buf);
2622 if (!u.uconduct.polypiles) {
2623 you_have_never("polymorphed an object");
2624 } else if (wizard) {
2625 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
2626 plur(u.uconduct.polypiles));
2627 you_have_X(buf);
2630 if (!u.uconduct.polyselfs) {
2631 you_have_never("changed form");
2632 } else if (wizard) {
2633 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
2634 plur(u.uconduct.polyselfs));
2635 you_have_X(buf);
2638 if (!u.uconduct.wishes) {
2639 you_have_X("used no wishes");
2640 } else {
2641 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
2642 (u.uconduct.wishes > 1L) ? "es" : "");
2643 if (u.uconduct.wisharti) {
2644 /* if wisharti == wishes
2645 * 1 wish (for an artifact)
2646 * 2 wishes (both for artifacts)
2647 * N wishes (all for artifacts)
2648 * else (N is at least 2 in order to get here; M < N)
2649 * N wishes (1 for an artifact)
2650 * N wishes (M for artifacts)
2652 if (u.uconduct.wisharti == u.uconduct.wishes)
2653 Sprintf(eos(buf), " (%s",
2654 (u.uconduct.wisharti > 2L) ? "all "
2655 : (u.uconduct.wisharti == 2L) ? "both " : "");
2656 else
2657 Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti);
2659 Sprintf(eos(buf), "for %s)",
2660 (u.uconduct.wisharti == 1L) ? "an artifact"
2661 : "artifacts");
2663 you_have_X(buf);
2665 if (!u.uconduct.wisharti)
2666 enl_msg(You_, "have not wished", "did not wish",
2667 " for any artifacts", "");
2670 /* Pop up the window and wait for a key */
2671 display_nhwindow(en_win, TRUE);
2672 destroy_nhwindow(en_win);
2673 en_win = WIN_ERR;
2676 /* Macros for meta and ctrl modifiers:
2677 * M and C return the meta/ctrl code for the given character;
2678 * e.g., (C('c') is ctrl-c
2679 * ISMETA and ISCTRL return TRUE iff the code is a meta/ctrl code
2680 * UNMETA and UNCTRL are the opposite of M/C and return the key for a given
2681 * meta/ctrl code. */
2682 #ifndef M
2683 #ifndef NHSTDC
2684 #define M(c) (0x80 | (c))
2685 #else
2686 #define M(c) ((c) -128)
2687 #endif /* NHSTDC */
2688 #endif
2689 #define ISMETA(c) (((c) & 0x80) != 0)
2690 #define UNMETA(c) ((c) & 0x7f)
2692 #ifndef C
2693 #define C(c) (0x1f & (c))
2694 #endif
2695 #define ISCTRL(c) ((uchar)(c) < 0x20)
2696 #define UNCTRL(c) (ISCTRL(c) ? (0x60 | (c)) : (c))
2698 struct ext_func_tab extcmdlist[] = {
2699 { '#', "#", "perform an extended command", doextcmd, IFBURIED|GENERALCMD },
2700 { M('?'), "?", "get this list of extended commands", doextlist, IFBURIED|AUTOCOMPLETE|GENERALCMD },
2701 { M('a'), "adjust", "adjust inventory letters", doorganize, IFBURIED|AUTOCOMPLETE },
2702 { M('A'), "annotate", "name current level", donamelevel, IFBURIED|AUTOCOMPLETE },
2703 { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply },
2704 { C('x'), "attributes", "show your attributes", doattributes, IFBURIED },
2705 { '@', "autopickup", "toggle the pickup option on/off", dotogglepickup, IFBURIED },
2706 { 'C', "call", "call (name) something", docallcmd, IFBURIED },
2707 { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED },
2708 { M('c'), "chat", "talk to someone", dotalk, IFBURIED|AUTOCOMPLETE },
2709 { 'c', "close", "close a door", doclose },
2710 { M('C'), "conduct", "list voluntary challenges you have maintained", doconduct, IFBURIED|AUTOCOMPLETE },
2711 { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE },
2712 { '>', "down", "go down a staircase", dodown },
2713 { 'd', "drop", "drop an item", dodrop },
2714 { 'D', "droptype", "drop specific item types", doddrop },
2715 { 'e', "eat", "eat something", doeat },
2716 { 'E', "engrave", "engrave writing on the floor", doengrave },
2717 { M('e'), "enhance", "advance or check weapon and spell skills", enhance_weapon_skill, IFBURIED|AUTOCOMPLETE },
2718 { '\0', "exploremode", "enter explore (discovery) mode", enter_explore_mode, IFBURIED },
2719 { 'f', "fire", "fire ammunition from quiver", dofire },
2720 { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE },
2721 { ';', "glance", "show what type of thing a map symbol corresponds to", doquickwhatis, IFBURIED|GENERALCMD },
2722 { '?', "help", "give a help message", dohelp, IFBURIED|GENERALCMD },
2723 { 'V', "history", "show long version and game history", dohistory, IFBURIED|GENERALCMD },
2724 { 'i', "inventory", "show your inventory", ddoinv, IFBURIED },
2725 { 'I', "inventtype", "inventory specific item types", dotypeinv, IFBURIED },
2726 { M('i'), "invoke", "invoke an object's special powers", doinvoke, IFBURIED|AUTOCOMPLETE },
2727 { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE },
2728 { C('d'), "kick", "kick something", dokick },
2729 { '\\', "known", "show what object types have been discovered", dodiscovered, IFBURIED|GENERALCMD },
2730 { '`', "knownclass", "show discovered types for one class of objects", doclassdisco, IFBURIED|GENERALCMD },
2731 { '\0', "levelchange", "change experience level", wiz_level_change, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2732 { '\0', "lightsources", "show mobile light sources", wiz_light_sources, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2733 { ':', "look", "look at what is here", dolook, IFBURIED },
2734 { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE },
2735 #ifdef DEBUG_MIGRATING_MONS
2736 { '\0', "migratemons", "migrate n random monsters", wiz_migrate_mons, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2737 #endif
2738 { '\0', "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2739 { M('m'), "monster", "use a monster's special ability", domonability, IFBURIED|AUTOCOMPLETE },
2740 { 'N', "name", "name a monster or an object", docallcmd, IFBURIED|AUTOCOMPLETE },
2741 { M('o'), "offer", "offer a sacrifice to the gods", dosacrifice, AUTOCOMPLETE },
2742 { 'o', "open", "open a door", doopen },
2743 { 'O', "options", "show option settings, possibly change them", doset, IFBURIED|GENERALCMD },
2744 { C('o'), "overview", "show a summary of the explored dungeon", dooverview, IFBURIED|AUTOCOMPLETE },
2745 { '\0', "panic", "test panic routine (fatal to game)", wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2746 { 'p', "pay", "pay your shopping bill", dopay },
2747 { ',', "pickup", "pick up things at the current location", dopickup },
2748 { '\0', "polyself", "polymorph self", wiz_polyself, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2749 #ifdef PORT_DEBUG
2750 { '\0', "portdebug", "wizard port debug command", wiz_port_debug, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2751 #endif
2752 { M('p'), "pray", "pray to the gods for help", dopray, IFBURIED|AUTOCOMPLETE },
2753 { C('p'), "prevmsg", "toggle through previously displayed game messages", doprev_message, IFBURIED|GENERALCMD },
2754 { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon },
2755 { 'q', "quaff", "quaff (drink) something", dodrink },
2756 { M('q'), "quit", "exit without saving current game", done2, IFBURIED|AUTOCOMPLETE|GENERALCMD },
2757 { 'Q', "quiver", "select ammunition for quiver", dowieldquiver },
2758 { 'r', "read", "read a scroll or spellbook", doread },
2759 { C('r'), "redraw", "redraw screen", doredraw, IFBURIED|GENERALCMD },
2760 { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring },
2761 { M('R'), "ride", "mount or dismount a saddled steed", doride, AUTOCOMPLETE },
2762 { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE },
2763 { 'S', "save", "save the game", dosave, IFBURIED|GENERALCMD },
2764 { 's', "search", "search for traps and secret doors", dosearch, IFBURIED, "searching" },
2765 { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED },
2766 { AMULET_SYM, "seeamulet", "show the amulet currently worn", dopramulet, IFBURIED },
2767 { ARMOR_SYM, "seearmor", "show the armor currently worn", doprarm, IFBURIED },
2768 { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED },
2769 { '\0', "seenv", "show seen vectors", wiz_show_seenv, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2770 { RING_SYM, "seerings", "show the ring(s) currently worn", doprring, IFBURIED },
2771 { SPBOOK_SYM, "seespells", "list and reorder known spells", dovspell, IFBURIED },
2772 { TOOL_SYM, "seetools", "show the tools currently in use", doprtool, IFBURIED },
2773 { '^', "seetrap", "show the type of a trap", doidtrap, IFBURIED },
2774 { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", doprwep, IFBURIED },
2775 #ifdef SHELL
2776 { '!', "shell", "do a shell escape", dosh, IFBURIED|GENERALCMD },
2777 #endif /* SHELL */
2778 { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE },
2779 { '\0', "stats", "show memory statistics", wiz_show_stats, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2780 #ifdef SUSPEND
2781 { C('z'), "suspend", "suspend the game", dosuspend_core, IFBURIED|GENERALCMD },
2782 #endif /* SUSPEND */
2783 { 'x', "swap", "swap wielded and secondary weapons", doswapweapon },
2784 { 'T', "takeoff", "take off one piece of armor", dotakeoff },
2785 { 'A', "takeoffall", "remove all armor", doddoremarm },
2786 { C('t'), "teleport", "teleport around the level", dotele, IFBURIED },
2787 { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED|AUTOCOMPLETE },
2788 { 't', "throw", "throw something", dothrow },
2789 { '\0', "timeout", "look at timeout queue", wiz_timeout_queue, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2790 { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE },
2791 { '_', "travel", "travel to a specific location on the map", dotravel },
2792 { M('t'), "turn", "turn undead away", doturn, IFBURIED|AUTOCOMPLETE },
2793 { 'X', "twoweapon", "toggle two-weapon combat", dotwoweapon, AUTOCOMPLETE },
2794 { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE },
2795 { '<', "up", "go up a staircase", doup },
2796 { '\0', "vanquished", "list vanquished monsters", dovanquished, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2797 { M('v'), "version", "list compile time options for this version of NetHack", doextversion, IFBURIED|AUTOCOMPLETE|GENERALCMD },
2798 { 'v', "versionshort", "show version", doversion, IFBURIED|GENERALCMD },
2799 { '\0', "vision", "show vision array", wiz_show_vision, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2800 { '.', "wait", "rest one move while doing nothing", donull, IFBURIED, "waiting" },
2801 { 'W', "wear", "wear a piece of armor", dowear },
2802 { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED },
2803 { '/', "whatis", "show what type of thing a symbol corresponds to", dowhatis, IFBURIED|GENERALCMD },
2804 { 'w', "wield", "wield (put in use) a weapon", dowield },
2805 { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE },
2806 #ifdef DEBUG
2807 { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you", wiz_debug_cmd_bury, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2808 { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display", wiz_debug_cmd_traveldisplay, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2809 #endif
2810 { C('e'), "wizdetect", "search a room", wiz_detect, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2811 { C('g'), "wizgenesis", "create a monster", wiz_genesis, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2812 { C('i'), "wizidentify", "identify all items in inventory", wiz_identify, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2813 { '\0', "wizintrinsic", "set intrinsic", wiz_intrinsic, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2814 { C('v'), "wizlevelport", "teleport to another level", wiz_level_tele, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2815 { C('f'), "wizmap", "map the level", wiz_map, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2816 { '\0', "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2817 { '\0', "wizsmell", "smell monster", wiz_smell, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2818 { '\0', "wizwhere", "show locations of special levels", wiz_where, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2819 { C('w'), "wizwish", "wish for something", wiz_wish, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2820 { '\0', "wmode", "show wall modes", wiz_show_wmodes, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2821 { 'z', "zap", "zap a wand", dozap },
2823 { '\0', (char *)0, (char *)0, donull } /* sentinel */
2826 const char *
2827 key2extcmddesc(key)
2828 uchar key;
2830 if (Cmd.commands[key] && Cmd.commands[key]->ef_txt)
2831 return Cmd.commands[key]->ef_desc;
2832 return NULL;
2835 void
2836 bind_key(key, command)
2837 uchar key;
2838 char* command;
2840 struct ext_func_tab * extcmd;
2842 /* special case: "nothing" is reserved for unbinding */
2843 if (!strcmp(command, "nothing")) {
2844 Cmd.commands[key] = NULL;
2845 return;
2848 for(extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
2849 if (strcmp(command, extcmd->ef_txt)) continue;
2850 Cmd.commands[key] = extcmd;
2851 return;
2854 pline("Bad command %s matched with key %c (ASCII %i). "
2855 "Ignoring command.\n", command, key, key);
2858 /* initialize all keyboard commands */
2859 void
2860 commands_init()
2862 struct ext_func_tab *extcmd;
2864 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++)
2865 if (extcmd->key)
2866 Cmd.commands[extcmd->key] = extcmd;
2868 bind_key(C('l'), "redraw"); /* if number_pad is set */
2869 /* 'b', 'B' : go sw */
2870 /* 'F' : fight (one time) */
2871 /* 'g', 'G' : multiple go */
2872 /* 'h', 'H' : go west */
2873 bind_key('h', "help"); /* if number_pad is set */
2874 bind_key('j', "jump"); /* if number_pad is on */
2875 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2876 bind_key('k', "kick"); /* if number_pad is on */
2877 bind_key('l', "loot"); /* if number_pad is on */
2878 bind_key(C('n'), "annotate"); /* if number_pad is on */
2879 bind_key(M('n'), "name");
2880 bind_key(M('N'), "name");
2881 bind_key('u', "untrap"); /* if number_pad is on */
2883 /* alt keys: */
2884 bind_key(M('O'), "overview");
2885 bind_key(M('2'), "twoweapon");
2887 /* wait_on_space */
2888 bind_key(' ', "wait");
2892 dokeylist_putcmds(datawin, docount, cmdflags, exflags, keys_used)
2893 winid datawin;
2894 boolean docount;
2895 int cmdflags, exflags;
2896 boolean *keys_used; /* boolean keys_used[256] */
2898 int i;
2899 char buf[BUFSZ];
2900 char buf2[QBUFSZ];
2901 int count = 0;
2903 for (i = 0; i < 256; i++) {
2904 const struct ext_func_tab * extcmd;
2905 uchar key = (uchar)i;
2907 if (keys_used[i]) continue;
2908 if (key == ' ' && !flags.rest_on_space) continue;
2909 if ((extcmd = Cmd.commands[i]) != NULL) {
2910 if ((cmdflags && !(extcmd->flags & cmdflags))
2911 || (exflags && (extcmd->flags & exflags)))
2912 continue;
2913 if (docount) {
2914 count++;
2915 continue;
2917 Sprintf(buf, "%-8s %-12s %s", key2txt(key, buf2),
2918 extcmd->ef_txt,
2919 extcmd->ef_desc);
2920 putstr(datawin, 0, buf);
2921 keys_used[i] = TRUE;
2924 return count;
2927 /* list all keys and their bindings, like dat/hh but dynamic */
2928 void
2929 dokeylist(VOID_ARGS)
2931 char buf[BUFSZ], buf2[BUFSZ];
2932 uchar key;
2933 boolean keys_used[256] = {0};
2934 winid datawin;
2935 int i;
2936 const struct {
2937 int nhkf;
2938 const char *desc;
2939 boolean numpad;
2940 } misc_keys[] = {
2941 { NHKF_ESC, "escape from the current query/action" },
2942 { NHKF_RUSH, "Prefix: rush until something interesting is seen" },
2943 { NHKF_RUN, "Prefix: run until something extremely interesting is seen" },
2944 { NHKF_RUN2, "Prefix: run until something extremely interesting is seen", TRUE },
2945 { NHKF_FIGHT, "Prefix: force fight even if you don't see a monster" },
2946 { NHKF_FIGHT2, "Prefix: force fight even if you don't see a monster", TRUE },
2947 { NHKF_NOPICKUP, "Prefix: move without picking up objects/fighting" },
2948 { NHKF_RUN_NOPICKUP, "Prefix: run without picking up objects/fighting" },
2949 { NHKF_DOINV, "inventory (same as #inventory)", TRUE },
2950 { NHKF_REQMENU, "Prefix: request a menu" },
2951 #ifdef REDO
2952 { NHKF_DOAGAIN , "redo the previous command" },
2953 #endif
2954 { 0, NULL }
2957 datawin = create_nhwindow(NHW_TEXT);
2958 putstr(datawin, 0, "");
2959 putstr(datawin, 0, " Full Current Key Bindings List");
2961 /* directional keys */
2962 putstr(datawin, 0, "");
2963 putstr(datawin, 0, "Directional keys:");
2964 show_direction_keys(datawin, FALSE);
2966 keys_used[(uchar)Cmd.move_NW] = keys_used[(uchar)Cmd.move_N]
2967 = keys_used[(uchar)Cmd.move_NE] = keys_used[(uchar)Cmd.move_W]
2968 = keys_used[(uchar)Cmd.move_E] = keys_used[(uchar)Cmd.move_SW]
2969 = keys_used[(uchar)Cmd.move_S] = keys_used[(uchar)Cmd.move_SE] = TRUE;
2971 if (!iflags.num_pad) {
2972 keys_used[(uchar)highc(Cmd.move_NW)]
2973 = keys_used[(uchar)highc(Cmd.move_N)]
2974 = keys_used[(uchar)highc(Cmd.move_NE)]
2975 = keys_used[(uchar)highc(Cmd.move_W)]
2976 = keys_used[(uchar)highc(Cmd.move_E)]
2977 = keys_used[(uchar)highc(Cmd.move_SW)]
2978 = keys_used[(uchar)highc(Cmd.move_S)]
2979 = keys_used[(uchar)highc(Cmd.move_SE)] = TRUE;
2980 keys_used[(uchar)C(Cmd.move_NW)]
2981 = keys_used[(uchar)C(Cmd.move_N)]
2982 = keys_used[(uchar)C(Cmd.move_NE)]
2983 = keys_used[(uchar)C(Cmd.move_W)]
2984 = keys_used[(uchar)C(Cmd.move_E)]
2985 = keys_used[(uchar)C(Cmd.move_SW)]
2986 = keys_used[(uchar)C(Cmd.move_S)]
2987 = keys_used[(uchar)C(Cmd.move_SE)] = TRUE;
2988 putstr(datawin, 0, "");
2989 putstr(datawin, 0,
2990 "Shift-<direction> will move in specified direction until you hit");
2991 putstr(datawin, 0, " a wall or run into something.");
2992 putstr(datawin, 0,
2993 "Ctrl-<direction> will run in specified direction until something");
2994 putstr(datawin, 0, " very interesting is seen.");
2997 putstr(datawin, 0, "");
2998 putstr(datawin, 0, "Miscellaneous keys:");
2999 for (i = 0; misc_keys[i].desc; i++) {
3000 key = Cmd.spkeys[misc_keys[i].nhkf];
3001 if (key && ((misc_keys[i].numpad && iflags.num_pad)
3002 || !misc_keys[i].numpad)) {
3003 keys_used[(uchar)key] = TRUE;
3004 Sprintf(buf, "%-8s %s", key2txt(key, buf2), misc_keys[i].desc);
3005 putstr(datawin, 0, buf);
3008 #ifndef NO_SIGNAL
3009 putstr(datawin, 0, "^c break out of NetHack (SIGINT)");
3010 keys_used[(uchar)C('c')] = TRUE;
3011 #endif
3013 putstr(datawin, 0, "");
3014 show_menu_controls(datawin, TRUE);
3016 if (dokeylist_putcmds(datawin, TRUE, GENERALCMD, WIZMODECMD, &keys_used)) {
3017 putstr(datawin, 0, "");
3018 putstr(datawin, 0, "General commands:");
3019 (void) dokeylist_putcmds(datawin, FALSE, GENERALCMD, WIZMODECMD, &keys_used);
3022 if (dokeylist_putcmds(datawin, TRUE, 0, WIZMODECMD, &keys_used)) {
3023 putstr(datawin, 0, "");
3024 putstr(datawin, 0, "Game commands:");
3025 (void) dokeylist_putcmds(datawin, FALSE, 0, WIZMODECMD, &keys_used);
3028 if (wizard && dokeylist_putcmds(datawin, TRUE, WIZMODECMD, 0, &keys_used)) {
3029 putstr(datawin, 0, "");
3030 putstr(datawin, 0, "Wizard-mode commands:");
3031 (void) dokeylist_putcmds(datawin, FALSE, WIZMODECMD, 0, &keys_used);
3034 display_nhwindow(datawin, FALSE);
3035 destroy_nhwindow(datawin);
3038 STATIC_OVL char
3039 cmd_from_func(fn)
3040 int NDECL((*fn));
3042 int i;
3043 for (i = 0; i < 256; ++i)
3044 if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn)
3045 return (char)i;
3046 return 0;
3049 static const char template[] = "%-27s %4ld %6ld";
3050 static const char stats_hdr[] = " count bytes";
3051 static const char stats_sep[] = "--------------------------- ----- -------";
3053 STATIC_OVL int
3054 size_obj(otmp)
3055 struct obj *otmp;
3057 int sz = (int) sizeof(struct obj);
3059 if (otmp->oextra) {
3060 sz += (int) sizeof(struct oextra);
3061 if (ONAME(otmp))
3062 sz += (int) strlen(ONAME(otmp)) + 1;
3063 if (OMONST(otmp))
3064 sz += (int) sizeof(struct monst);
3065 if (OMID(otmp))
3066 sz += (int) sizeof(unsigned);
3067 if (OLONG(otmp))
3068 sz += (int) sizeof(long);
3069 if (OMAILCMD(otmp))
3070 sz += (int) strlen(OMAILCMD(otmp)) + 1;
3072 return sz;
3075 STATIC_OVL void
3076 count_obj(chain, total_count, total_size, top, recurse)
3077 struct obj *chain;
3078 long *total_count;
3079 long *total_size;
3080 boolean top;
3081 boolean recurse;
3083 long count, size;
3084 struct obj *obj;
3086 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
3087 if (top) {
3088 count++;
3089 size += size_obj(obj);
3091 if (recurse && obj->cobj)
3092 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
3094 *total_count += count;
3095 *total_size += size;
3098 STATIC_OVL void
3099 obj_chain(win, src, chain, force, total_count, total_size)
3100 winid win;
3101 const char *src;
3102 struct obj *chain;
3103 boolean force;
3104 long *total_count;
3105 long *total_size;
3107 char buf[BUFSZ];
3108 long count = 0L, size = 0L;
3110 count_obj(chain, &count, &size, TRUE, FALSE);
3112 if (count || size || force) {
3113 *total_count += count;
3114 *total_size += size;
3115 Sprintf(buf, template, src, count, size);
3116 putstr(win, 0, buf);
3120 STATIC_OVL void
3121 mon_invent_chain(win, src, chain, total_count, total_size)
3122 winid win;
3123 const char *src;
3124 struct monst *chain;
3125 long *total_count;
3126 long *total_size;
3128 char buf[BUFSZ];
3129 long count = 0, size = 0;
3130 struct monst *mon;
3132 for (mon = chain; mon; mon = mon->nmon)
3133 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
3135 if (count || size) {
3136 *total_count += count;
3137 *total_size += size;
3138 Sprintf(buf, template, src, count, size);
3139 putstr(win, 0, buf);
3143 STATIC_OVL void
3144 contained_stats(win, src, total_count, total_size)
3145 winid win;
3146 const char *src;
3147 long *total_count;
3148 long *total_size;
3150 char buf[BUFSZ];
3151 long count = 0, size = 0;
3152 struct monst *mon;
3154 count_obj(invent, &count, &size, FALSE, TRUE);
3155 count_obj(fobj, &count, &size, FALSE, TRUE);
3156 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3157 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3158 /* DEADMONSTER check not required in this loop since they have no
3159 * inventory */
3160 for (mon = fmon; mon; mon = mon->nmon)
3161 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3162 for (mon = migrating_mons; mon; mon = mon->nmon)
3163 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3165 if (count || size) {
3166 *total_count += count;
3167 *total_size += size;
3168 Sprintf(buf, template, src, count, size);
3169 putstr(win, 0, buf);
3173 STATIC_OVL int
3174 size_monst(mtmp, incl_wsegs)
3175 struct monst *mtmp;
3176 boolean incl_wsegs;
3178 int sz = (int) sizeof (struct monst);
3180 if (mtmp->wormno && incl_wsegs)
3181 sz += size_wseg(mtmp);
3183 if (mtmp->mextra) {
3184 sz += (int) sizeof (struct mextra);
3185 if (MNAME(mtmp))
3186 sz += (int) strlen(MNAME(mtmp)) + 1;
3187 if (EGD(mtmp))
3188 sz += (int) sizeof (struct egd);
3189 if (EPRI(mtmp))
3190 sz += (int) sizeof (struct epri);
3191 if (ESHK(mtmp))
3192 sz += (int) sizeof (struct eshk);
3193 if (EMIN(mtmp))
3194 sz += (int) sizeof (struct emin);
3195 if (EDOG(mtmp))
3196 sz += (int) sizeof (struct edog);
3197 /* mextra->mcorpsenm doesn't point to more memory */
3199 return sz;
3202 STATIC_OVL void
3203 mon_chain(win, src, chain, force, total_count, total_size)
3204 winid win;
3205 const char *src;
3206 struct monst *chain;
3207 boolean force;
3208 long *total_count;
3209 long *total_size;
3211 char buf[BUFSZ];
3212 long count, size;
3213 struct monst *mon;
3214 /* mon->wormno means something different for migrating_mons and mydogs */
3215 boolean incl_wsegs = !strcmpi(src, "fmon");
3217 count = size = 0L;
3218 for (mon = chain; mon; mon = mon->nmon) {
3219 count++;
3220 size += size_monst(mon, incl_wsegs);
3222 if (count || size || force) {
3223 *total_count += count;
3224 *total_size += size;
3225 Sprintf(buf, template, src, count, size);
3226 putstr(win, 0, buf);
3230 STATIC_OVL void
3231 misc_stats(win, total_count, total_size)
3232 winid win;
3233 long *total_count;
3234 long *total_size;
3236 char buf[BUFSZ], hdrbuf[QBUFSZ];
3237 long count, size;
3238 int idx;
3239 struct trap *tt;
3240 struct damage *sd; /* shop damage */
3241 struct cemetery *bi; /* bones info */
3243 /* traps and engravings are output unconditionally;
3244 * others only if nonzero
3246 count = size = 0L;
3247 for (tt = ftrap; tt; tt = tt->ntrap) {
3248 ++count;
3249 size += (long) sizeof *tt;
3251 *total_count += count;
3252 *total_size += size;
3253 Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap));
3254 Sprintf(buf, template, hdrbuf, count, size);
3255 putstr(win, 0, buf);
3257 count = size = 0L;
3258 engr_stats("engravings, size %ld+text", hdrbuf, &count, &size);
3259 *total_count += count;
3260 *total_size += size;
3261 Sprintf(buf, template, hdrbuf, count, size);
3262 putstr(win, 0, buf);
3264 count = size = 0L;
3265 light_stats("light sources, size %ld", hdrbuf, &count, &size);
3266 if (count || size) {
3267 *total_count += count;
3268 *total_size += size;
3269 Sprintf(buf, template, hdrbuf, count, size);
3270 putstr(win, 0, buf);
3273 count = size = 0L;
3274 timer_stats("timers, size %ld", hdrbuf, &count, &size);
3275 if (count || size) {
3276 *total_count += count;
3277 *total_size += size;
3278 Sprintf(buf, template, hdrbuf, count, size);
3279 putstr(win, 0, buf);
3282 count = size = 0L;
3283 for (sd = level.damagelist; sd; sd = sd->next) {
3284 ++count;
3285 size += (long) sizeof *sd;
3287 if (count || size) {
3288 *total_count += count;
3289 *total_size += size;
3290 Sprintf(hdrbuf, "shop damage, size %ld",
3291 (long) sizeof (struct damage));
3292 Sprintf(buf, template, hdrbuf, count, size);
3293 putstr(win, 0, buf);
3296 count = size = 0L;
3297 region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size);
3298 if (count || size) {
3299 *total_count += count;
3300 *total_size += size;
3301 Sprintf(buf, template, hdrbuf, count, size);
3302 putstr(win, 0, buf);
3305 count = size = 0L;
3306 for (bi = level.bonesinfo; bi; bi = bi->next) {
3307 ++count;
3308 size += (long) sizeof *bi;
3310 if (count || size) {
3311 *total_count += count;
3312 *total_size += size;
3313 Sprintf(hdrbuf, "bones history, size %ld",
3314 (long) sizeof (struct cemetery));
3315 Sprintf(buf, template, hdrbuf, count, size);
3316 putstr(win, 0, buf);
3319 count = size = 0L;
3320 for (idx = 0; idx < NUM_OBJECTS; ++idx)
3321 if (objects[idx].oc_uname) {
3322 ++count;
3323 size += (long) (strlen(objects[idx].oc_uname) + 1);
3325 if (count || size) {
3326 *total_count += count;
3327 *total_size += size;
3328 Strcpy(hdrbuf, "object type names, text");
3329 Sprintf(buf, template, hdrbuf, count, size);
3330 putstr(win, 0, buf);
3335 * Display memory usage of all monsters and objects on the level.
3337 static int
3338 wiz_show_stats()
3340 char buf[BUFSZ];
3341 winid win;
3342 long total_obj_size, total_obj_count,
3343 total_mon_size, total_mon_count,
3344 total_ovr_size, total_ovr_count,
3345 total_misc_size, total_misc_count;
3347 win = create_nhwindow(NHW_TEXT);
3348 putstr(win, 0, "Current memory statistics:");
3350 total_obj_count = total_obj_size = 0L;
3351 putstr(win, 0, stats_hdr);
3352 Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj));
3353 putstr(win, 0, buf);
3354 obj_chain(win, "invent", invent, TRUE, &total_obj_count, &total_obj_size);
3355 obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size);
3356 obj_chain(win, "buried", level.buriedobjlist, FALSE,
3357 &total_obj_count, &total_obj_size);
3358 obj_chain(win, "migrating obj", migrating_objs, FALSE,
3359 &total_obj_count, &total_obj_size);
3360 obj_chain(win, "billobjs", billobjs, FALSE,
3361 &total_obj_count, &total_obj_size);
3362 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3363 mon_invent_chain(win, "migrating minvent", migrating_mons,
3364 &total_obj_count, &total_obj_size);
3365 contained_stats(win, "contained", &total_obj_count, &total_obj_size);
3366 putstr(win, 0, stats_sep);
3367 Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size);
3368 putstr(win, 0, buf);
3370 total_mon_count = total_mon_size = 0L;
3371 putstr(win, 0, "");
3372 Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst));
3373 putstr(win, 0, buf);
3374 mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size);
3375 mon_chain(win, "migrating", migrating_mons, FALSE,
3376 &total_mon_count, &total_mon_size);
3377 /* 'mydogs' is only valid during level change or end of game disclosure,
3378 but conceivably we've been called from within debugger at such time */
3379 if (mydogs) /* monsters accompanying hero */
3380 mon_chain(win, "mydogs", mydogs, FALSE,
3381 &total_mon_count, &total_mon_size);
3382 putstr(win, 0, stats_sep);
3383 Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size);
3384 putstr(win, 0, buf);
3386 total_ovr_count = total_ovr_size = 0L;
3387 putstr(win, 0, "");
3388 putstr(win, 0, " Overview");
3389 overview_stats(win, template, &total_ovr_count, &total_ovr_size);
3390 putstr(win, 0, stats_sep);
3391 Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size);
3392 putstr(win, 0, buf);
3394 total_misc_count = total_misc_size = 0L;
3395 putstr(win, 0, "");
3396 putstr(win, 0, " Miscellaneous");
3397 misc_stats(win, &total_misc_count, &total_misc_size);
3398 putstr(win, 0, stats_sep);
3399 Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size);
3400 putstr(win, 0, buf);
3402 putstr(win, 0, "");
3403 putstr(win, 0, stats_sep);
3404 Sprintf(buf, template, " Grand total",
3405 (total_obj_count + total_mon_count
3406 + total_ovr_count + total_misc_count),
3407 (total_obj_size + total_mon_size
3408 + total_ovr_size + total_misc_size));
3409 putstr(win, 0, buf);
3411 #if defined(__BORLANDC__) && !defined(_WIN32)
3412 show_borlandc_stats(win);
3413 #endif
3415 display_nhwindow(win, FALSE);
3416 destroy_nhwindow(win);
3417 return 0;
3420 void
3421 sanity_check()
3423 obj_sanity_check();
3424 timer_sanity_check();
3425 mon_sanity_check();
3426 light_sources_sanity_check();
3429 #ifdef DEBUG_MIGRATING_MONS
3430 static int
3431 wiz_migrate_mons()
3433 int mcount = 0;
3434 char inbuf[BUFSZ];
3435 struct permonst *ptr;
3436 struct monst *mtmp;
3437 d_level tolevel;
3439 getlin("How many random monsters to migrate? [0]", inbuf);
3440 if (*inbuf == '\033')
3441 return 0;
3442 mcount = atoi(inbuf);
3443 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3444 return 0;
3445 while (mcount > 0) {
3446 if (Is_stronghold(&u.uz))
3447 assign_level(&tolevel, &valley_level);
3448 else
3449 get_level(&tolevel, depth(&u.uz) + 1);
3450 ptr = rndmonst();
3451 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3452 if (mtmp)
3453 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3454 (coord *) 0);
3455 mcount--;
3457 return 0;
3459 #endif
3461 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3462 #define unmeta(c) (0x7f & (c))
3464 struct {
3465 int nhkf;
3466 char key;
3467 const char *name;
3468 } const spkeys_binds[] = {
3469 { NHKF_ESC, '\033', NULL }, /* no binding */
3470 { NHKF_DOAGAIN, DOAGAIN, "repeat" },
3471 { NHKF_REQMENU, 'm', "reqmenu" },
3472 { NHKF_RUN, 'G', "run" },
3473 { NHKF_RUN2, '5', "run.numpad" },
3474 { NHKF_RUSH, 'g', "rush" },
3475 { NHKF_FIGHT, 'F', "fight" },
3476 { NHKF_FIGHT2, '-', "fight.numpad" },
3477 { NHKF_NOPICKUP, 'm', "nopickup" },
3478 { NHKF_RUN_NOPICKUP, 'M', "run.nopickup" },
3479 { NHKF_DOINV, '0', "doinv" },
3480 { NHKF_TRAVEL, CMD_TRAVEL, NULL }, /* no binding */
3481 { NHKF_CLICKLOOK, CMD_CLICKLOOK, NULL }, /* no binding */
3482 { NHKF_REDRAW, C('r'), "redraw" },
3483 { NHKF_REDRAW2, C('l'), "redraw.numpad" },
3484 { NHKF_GETDIR_SELF, '.', "getdir.self" },
3485 { NHKF_GETDIR_SELF2, 's', "getdir.self2" },
3486 { NHKF_GETDIR_HELP, '?', "getdir.help" },
3487 { NHKF_COUNT, 'n', "count" },
3488 { NHKF_GETPOS_SELF, '@', "getpos.self" },
3489 { NHKF_GETPOS_PICK, '.', "getpos.pick" },
3490 { NHKF_GETPOS_PICK_Q, ',', "getpos.pick.quick" },
3491 { NHKF_GETPOS_PICK_O, ';', "getpos.pick.once" },
3492 { NHKF_GETPOS_PICK_V, ':', "getpos.pick.verbose" },
3493 { NHKF_GETPOS_SHOWVALID, '$', "getpos.valid" },
3494 { NHKF_GETPOS_AUTODESC, '#', "getpos.autodescribe" },
3495 { NHKF_GETPOS_MON_NEXT, 'm', "getpos.mon.next" },
3496 { NHKF_GETPOS_MON_PREV, 'M', "getpos.mon.prev" },
3497 { NHKF_GETPOS_OBJ_NEXT, 'o', "getpos.obj.next" },
3498 { NHKF_GETPOS_OBJ_PREV, 'O', "getpos.obj.prev" },
3499 { NHKF_GETPOS_DOOR_NEXT, 'd', "getpos.door.next" },
3500 { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" },
3501 { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" },
3502 { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" },
3503 { NHKF_GETPOS_HELP, '?', "getpos.help" },
3504 { NHKF_GETPOS_MENU, 'A', "getpos.menu" },
3505 { NHKF_GETPOS_MENU_FOV, 'a', "getpos.menu.cansee" }
3508 boolean
3509 bind_specialkey(key, command)
3510 uchar key;
3511 char *command;
3513 int i;
3514 for (i = 0; i < SIZE(spkeys_binds); i++) {
3515 if (!spkeys_binds[i].name || strcmp(command, spkeys_binds[i].name))
3516 continue;
3517 Cmd.spkeys[spkeys_binds[i].nhkf] = key;
3518 return TRUE;
3520 return FALSE;
3523 /* returns a one-byte character from the text (it may massacre the txt
3524 * buffer) */
3525 char
3526 txt2key(txt)
3527 char *txt;
3529 txt = trimspaces(txt);
3530 if (!*txt) return 0;
3532 /* simple character */
3533 if (!txt[1]) return txt[0];
3535 /* a few special entries */
3536 if (!strcmp(txt, "<enter>")) return '\n';
3537 if (!strcmp(txt, "<space>")) return ' ';
3538 if (!strcmp(txt, "<esc>")) return '\033';
3540 /* control and meta keys */
3541 switch (*txt) {
3542 case 'm': /* can be mx, Mx, m-x, M-x */
3543 case 'M':
3544 txt++;
3545 if(*txt == '-' && txt[1]) txt++;
3546 if (txt[1]) return 0;
3547 return M( *txt );
3548 case 'c': /* can be cx, Cx, ^x, c-x, C-x, ^-x */
3549 case 'C':
3550 case '^':
3551 txt++;
3552 if(*txt == '-' && txt[1]) txt++;
3553 if (txt[1]) return 0;
3554 return C( *txt );
3557 /* ascii codes: must be three-digit decimal */
3558 if (*txt >= '0' && *txt <= '9') {
3559 uchar key = 0;
3560 int i;
3561 for(i = 0; i < 3; i++) {
3562 if(txt[i]<'0' || txt[i]>'9') return 0;
3563 key = 10 * key + txt[i]-'0';
3565 return key;
3568 return 0;
3571 /* returns the text for a one-byte encoding
3572 * must be shorter than a tab for proper formatting */
3573 char*
3574 key2txt(c, txt)
3575 uchar c;
3576 char* txt; /* sufficiently long buffer */
3578 if (c == ' ')
3579 Sprintf(txt, "<space>");
3580 else if (c == '\033')
3581 Sprintf(txt, "<esc>");
3582 else if (c == '\n')
3583 Sprintf(txt, "<enter>");
3584 else if (ISCTRL(c))
3585 Sprintf(txt, "^%c", UNCTRL(c));
3586 else if (ISMETA(c))
3587 Sprintf(txt, "M-%c", UNMETA(c));
3588 else if (c >= 33 && c <= 126)
3589 Sprintf(txt, "%c", c); /* regular keys: ! through ~ */
3590 else
3591 Sprintf(txt, "A-%i", c); /* arbitrary ascii combinations */
3592 return txt;
3596 void
3597 parseautocomplete(autocomplete,condition)
3598 char* autocomplete;
3599 boolean condition;
3601 struct ext_func_tab *efp;
3602 register char *autoc;
3604 /* break off first autocomplete from the rest; parse the rest */
3605 if ((autoc = index(autocomplete, ','))
3606 || (autoc = index(autocomplete, ':'))) {
3607 *autoc++ = 0;
3608 parseautocomplete(autoc, condition);
3611 /* strip leading and trailing white space */
3612 autocomplete = trimspaces(autocomplete);
3614 if (!*autocomplete) return;
3616 /* take off negation */
3617 if (*autocomplete == '!') {
3618 /* unlike most options, a leading "no" might actually be a part of
3619 * the extended command. Thus you have to use ! */
3620 autocomplete++;
3621 autocomplete = trimspaces(autocomplete);
3622 condition = !condition;
3625 /* find and modify the extended command */
3626 for (efp = extcmdlist; efp->ef_txt; efp++) {
3627 if (!strcmp(autocomplete, efp->ef_txt)) {
3628 if (condition)
3629 efp->flags |= AUTOCOMPLETE;
3630 else
3631 efp->flags &= ~AUTOCOMPLETE;
3632 return;
3636 /* not a real extended command */
3637 raw_printf("Bad autocomplete: invalid extended command '%s'.", autocomplete);
3638 wait_synch();
3641 /* called at startup and after number_pad is twiddled */
3642 void
3643 reset_commands(initial)
3644 boolean initial;
3646 static const char sdir[] = "hykulnjb><",
3647 sdir_swap_yz[] = "hzkulnjb><",
3648 ndir[] = "47896321><",
3649 ndir_phone_layout[] = "41236987><";
3650 static const int ylist[] = {
3651 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3653 static struct ext_func_tab *back_dir_cmd[10];
3654 const struct ext_func_tab *cmdtmp;
3655 boolean flagtemp;
3656 int c, i, updated = 0;
3657 static boolean backed_dir_cmd = FALSE;
3659 if (initial) {
3660 updated = 1;
3661 Cmd.num_pad = FALSE;
3662 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3663 for (i = 0; i < SIZE(spkeys_binds); i++)
3664 Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key;
3665 commands_init();
3666 } else {
3668 if (backed_dir_cmd) {
3669 for (i = 0; i < 10; i++) {
3670 Cmd.commands[(uchar)Cmd.dirchars[i]] = back_dir_cmd[i];
3674 /* basic num_pad */
3675 flagtemp = iflags.num_pad;
3676 if (flagtemp != Cmd.num_pad) {
3677 Cmd.num_pad = flagtemp;
3678 ++updated;
3680 /* swap_yz mode (only applicable for !num_pad); intended for
3681 QWERTZ keyboard used in Central Europe, particularly Germany */
3682 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3683 if (flagtemp != Cmd.swap_yz) {
3684 Cmd.swap_yz = flagtemp;
3685 ++updated;
3686 /* Cmd.swap_yz has been toggled;
3687 perform the swap (or reverse previous one) */
3688 for (i = 0; i < SIZE(ylist); i++) {
3689 c = ylist[i] & 0xff;
3690 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3691 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3692 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3695 /* MSDOS compatibility mode (only applicable for num_pad) */
3696 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3697 if (flagtemp != Cmd.pcHack_compat) {
3698 Cmd.pcHack_compat = flagtemp;
3699 ++updated;
3700 /* pcHack_compat has been toggled */
3701 c = M('5') & 0xff;
3702 cmdtmp = Cmd.commands['5'];
3703 Cmd.commands['5'] = Cmd.commands[c];
3704 Cmd.commands[c] = cmdtmp;
3705 c = M('0') & 0xff;
3706 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3708 /* phone keypad layout (only applicable for num_pad) */
3709 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3710 if (flagtemp != Cmd.phone_layout) {
3711 Cmd.phone_layout = flagtemp;
3712 ++updated;
3713 /* phone_layout has been toggled */
3714 for (i = 0; i < 3; i++) {
3715 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3716 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3717 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3718 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3719 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3720 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3721 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3722 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3725 } /*?initial*/
3727 if (updated)
3728 Cmd.serialno++;
3729 Cmd.dirchars = !Cmd.num_pad
3730 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3731 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3732 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3734 Cmd.move_W = Cmd.dirchars[0];
3735 Cmd.move_NW = Cmd.dirchars[1];
3736 Cmd.move_N = Cmd.dirchars[2];
3737 Cmd.move_NE = Cmd.dirchars[3];
3738 Cmd.move_E = Cmd.dirchars[4];
3739 Cmd.move_SE = Cmd.dirchars[5];
3740 Cmd.move_S = Cmd.dirchars[6];
3741 Cmd.move_SW = Cmd.dirchars[7];
3743 if (!initial) {
3744 for (i = 0; i < 10; i++) {
3745 back_dir_cmd[i] =
3746 (struct ext_func_tab *)Cmd.commands[(uchar)Cmd.dirchars[i]];
3747 Cmd.commands[(uchar)Cmd.dirchars[i]] = NULL;
3749 backed_dir_cmd = TRUE;
3750 for (i = 0; i < 8; i++)
3751 bind_key(Cmd.dirchars[i], "nothing");
3755 /* non-movement commands which accept 'm' prefix to request menu operation */
3756 STATIC_OVL boolean
3757 accept_menu_prefix(cmd_func)
3758 int NDECL((*cmd_func));
3760 if (cmd_func == dopickup || cmd_func == dotip
3761 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
3762 an item on floor or in invent; 'm' skips picking from floor
3763 (ie, inventory only) rather than request use of menu operation */
3764 || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
3765 /* 'm' for removing saddle from adjacent monster without checking
3766 for containers at <u.ux,u.uy> */
3767 || cmd_func == doloot
3768 /* travel: pop up a menu of interesting targets in view */
3769 || cmd_func == dotravel
3770 /* 'm' prefix allowed for some extended commands */
3771 || cmd_func == doextcmd || cmd_func == doextlist)
3772 return TRUE;
3773 return FALSE;
3777 ch2spkeys(c, start,end)
3778 char c;
3779 int start,end;
3781 int i;
3782 for (i = start; i <= end; i++)
3783 if (Cmd.spkeys[i] == c)
3784 return i;
3785 return NHKF_ESC;
3788 void
3789 rhack(cmd)
3790 register char *cmd;
3792 boolean do_walk, do_rush, prefix_seen, bad_command,
3793 firsttime = (cmd == 0);
3795 iflags.menu_requested = FALSE;
3796 #ifdef SAFERHANGUP
3797 if (program_state.done_hup)
3798 end_of_input();
3799 #endif
3800 if (firsttime) {
3801 context.nopick = 0;
3802 cmd = parse();
3804 if (*cmd == Cmd.spkeys[NHKF_ESC]) {
3805 context.move = FALSE;
3806 return;
3808 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3809 in_doagain = TRUE;
3810 stail = 0;
3811 rhack((char *) 0); /* read and execute command */
3812 in_doagain = FALSE;
3813 return;
3815 /* Special case of *cmd == ' ' handled better below */
3816 if (!*cmd || *cmd == (char) 0377) {
3817 nhbell();
3818 context.move = FALSE;
3819 return; /* probably we just had an interrupt */
3822 /* handle most movement commands */
3823 do_walk = do_rush = prefix_seen = FALSE;
3824 context.travel = context.travel1 = 0;
3825 switch (ch2spkeys(*cmd, NHKF_RUN,NHKF_CLICKLOOK)) {
3826 case NHKF_RUSH:
3827 if (movecmd(cmd[1])) {
3828 context.run = 2;
3829 do_rush = TRUE;
3830 } else
3831 prefix_seen = TRUE;
3832 break;
3833 case NHKF_RUN2:
3834 if (!Cmd.num_pad)
3835 break; /* else FALLTHRU */
3836 case NHKF_RUN:
3837 if (movecmd(lowc(cmd[1]))) {
3838 context.run = 3;
3839 do_rush = TRUE;
3840 } else
3841 prefix_seen = TRUE;
3842 break;
3843 case NHKF_FIGHT2:
3844 if (!Cmd.num_pad)
3845 break; /* else FALLTHRU */
3846 /* Effects of movement commands and invisible monsters:
3847 * m: always move onto space (even if 'I' remembered)
3848 * F: always attack space (even if 'I' not remembered)
3849 * normal movement: attack if 'I', move otherwise.
3851 case NHKF_FIGHT:
3852 if (movecmd(cmd[1])) {
3853 context.forcefight = 1;
3854 do_walk = TRUE;
3855 } else
3856 prefix_seen = TRUE;
3857 break;
3858 case NHKF_NOPICKUP:
3859 if (movecmd(cmd[1]) || u.dz) {
3860 context.run = 0;
3861 context.nopick = 1;
3862 if (!u.dz)
3863 do_walk = TRUE;
3864 else
3865 cmd[0] = cmd[1]; /* "m<" or "m>" */
3866 } else
3867 prefix_seen = TRUE;
3868 break;
3869 case NHKF_RUN_NOPICKUP:
3870 if (movecmd(lowc(cmd[1]))) {
3871 context.run = 1;
3872 context.nopick = 1;
3873 do_rush = TRUE;
3874 } else
3875 prefix_seen = TRUE;
3876 break;
3877 case NHKF_DOINV:
3878 if (!Cmd.num_pad)
3879 break;
3880 (void) ddoinv(); /* a convenience borrowed from the PC */
3881 context.move = FALSE;
3882 multi = 0;
3883 return;
3884 case NHKF_CLICKLOOK:
3885 if (iflags.clicklook) {
3886 context.move = FALSE;
3887 do_look(2, &clicklook_cc);
3889 return;
3890 case NHKF_TRAVEL:
3891 if (flags.travelcmd) {
3892 context.travel = 1;
3893 context.travel1 = 1;
3894 context.run = 8;
3895 context.nopick = 1;
3896 do_rush = TRUE;
3897 break;
3899 /*FALLTHRU*/
3900 default:
3901 if (movecmd(*cmd)) { /* ordinary movement */
3902 context.run = 0; /* only matters here if it was 8 */
3903 do_walk = TRUE;
3904 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
3905 context.run = 1;
3906 do_rush = TRUE;
3907 } else if (movecmd(unctrl(*cmd))) {
3908 context.run = 3;
3909 do_rush = TRUE;
3911 break;
3914 /* some special prefix handling */
3915 /* overload 'm' prefix to mean "request a menu" */
3916 if (prefix_seen && cmd[0] == Cmd.spkeys[NHKF_REQMENU]) {
3917 /* (for func_tab cast, see below) */
3918 const struct ext_func_tab *ft = Cmd.commands[cmd[1] & 0xff];
3919 int NDECL((*func)) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0;
3921 if (func && accept_menu_prefix(func)) {
3922 iflags.menu_requested = TRUE;
3923 ++cmd;
3927 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
3928 /* trying to move diagonally as a grid bug;
3929 this used to be treated by movecmd() as not being
3930 a movement attempt, but that didn't provide for any
3931 feedback and led to strangeness if the key pressed
3932 ('u' in particular) was overloaded for num_pad use */
3933 You_cant("get there from here...");
3934 context.run = 0;
3935 context.nopick = context.forcefight = FALSE;
3936 context.move = context.mv = FALSE;
3937 multi = 0;
3938 return;
3941 if (do_walk) {
3942 if (multi)
3943 context.mv = TRUE;
3944 domove();
3945 context.forcefight = 0;
3946 return;
3947 } else if (do_rush) {
3948 if (firsttime) {
3949 if (!multi)
3950 multi = max(COLNO, ROWNO);
3951 u.last_str_turn = 0;
3953 context.mv = TRUE;
3954 domove();
3955 return;
3956 } else if (prefix_seen && cmd[1] == Cmd.spkeys[NHKF_ESC]) {
3957 /* <prefix><escape> */
3958 /* don't report "unknown command" for change of heart... */
3959 bad_command = FALSE;
3960 } else if (*cmd == ' ' && !flags.rest_on_space) {
3961 bad_command = TRUE; /* skip cmdlist[] loop */
3963 /* handle all other commands */
3964 } else {
3965 register const struct ext_func_tab *tlist;
3966 int res, NDECL((*func));
3968 /* current - use *cmd to directly index cmdlist array */
3969 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
3970 if (!wizard && (tlist->flags & WIZMODECMD)) {
3971 You_cant("do that!");
3972 res = 0;
3973 } else if (u.uburied && !(tlist->flags & IFBURIED)) {
3974 You_cant("do that while you are buried!");
3975 res = 0;
3976 } else {
3977 /* we discard 'const' because some compilers seem to have
3978 trouble with the pointer passed to set_occupation() */
3979 func = ((struct ext_func_tab *) tlist)->ef_funct;
3980 if (tlist->f_text && !occupation && multi)
3981 set_occupation(func, tlist->f_text, multi);
3982 res = (*func)(); /* perform the command */
3984 if (!res) {
3985 context.move = FALSE;
3986 multi = 0;
3988 return;
3990 /* if we reach here, cmd wasn't found in cmdlist[] */
3991 bad_command = TRUE;
3994 if (bad_command) {
3995 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3996 register char c;
3998 expcmd[0] = '\0';
3999 while ((c = *cmd++) != '\0')
4000 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
4002 if (!prefix_seen || !iflags.cmdassist
4003 || !help_dir(0, "Invalid direction key!"))
4004 Norep("Unknown command '%s'.", expcmd);
4006 /* didn't move */
4007 context.move = FALSE;
4008 multi = 0;
4009 return;
4012 /* convert an x,y pair into a direction code */
4014 xytod(x, y)
4015 schar x, y;
4017 register int dd;
4019 for (dd = 0; dd < 8; dd++)
4020 if (x == xdir[dd] && y == ydir[dd])
4021 return dd;
4022 return -1;
4025 /* convert a direction code into an x,y pair */
4026 void
4027 dtoxy(cc, dd)
4028 coord *cc;
4029 register int dd;
4031 cc->x = xdir[dd];
4032 cc->y = ydir[dd];
4033 return;
4036 /* also sets u.dz, but returns false for <> */
4038 movecmd(sym)
4039 char sym;
4041 register const char *dp = index(Cmd.dirchars, sym);
4043 u.dz = 0;
4044 if (!dp || !*dp)
4045 return 0;
4046 u.dx = xdir[dp - Cmd.dirchars];
4047 u.dy = ydir[dp - Cmd.dirchars];
4048 u.dz = zdir[dp - Cmd.dirchars];
4049 #if 0 /* now handled elsewhere */
4050 if (u.dx && u.dy && NODIAG(u.umonnum)) {
4051 u.dx = u.dy = 0;
4052 return 0;
4054 #endif
4055 return !u.dz;
4058 /* grid bug handling which used to be in movecmd() */
4060 dxdy_moveok()
4062 if (u.dx && u.dy && NODIAG(u.umonnum))
4063 u.dx = u.dy = 0;
4064 return u.dx || u.dy;
4067 /* decide whether a character (user input keystroke) requests screen repaint */
4068 boolean
4069 redraw_cmd(c)
4070 char c;
4072 return (boolean) (c == Cmd.spkeys[NHKF_REDRAW]
4073 || (Cmd.num_pad && c == Cmd.spkeys[NHKF_REDRAW2]));
4076 boolean
4077 prefix_cmd(c)
4078 char c;
4080 return (c == Cmd.spkeys[NHKF_RUSH]
4081 || c == Cmd.spkeys[NHKF_RUN]
4082 || c == Cmd.spkeys[NHKF_NOPICKUP]
4083 || c == Cmd.spkeys[NHKF_RUN_NOPICKUP]
4084 || c == Cmd.spkeys[NHKF_FIGHT]
4085 || (Cmd.num_pad && (c == Cmd.spkeys[NHKF_RUN2]
4086 || c == Cmd.spkeys[NHKF_FIGHT2])));
4090 * uses getdir() but unlike getdir() it specifically
4091 * produces coordinates using the direction from getdir()
4092 * and verifies that those coordinates are ok.
4094 * If the call to getdir() returns 0, Never_mind is displayed.
4095 * If the resulting coordinates are not okay, emsg is displayed.
4097 * Returns non-zero if coordinates in cc are valid.
4100 get_adjacent_loc(prompt, emsg, x, y, cc)
4101 const char *prompt, *emsg;
4102 xchar x, y;
4103 coord *cc;
4105 xchar new_x, new_y;
4106 if (!getdir(prompt)) {
4107 pline1(Never_mind);
4108 return 0;
4110 new_x = x + u.dx;
4111 new_y = y + u.dy;
4112 if (cc && isok(new_x, new_y)) {
4113 cc->x = new_x;
4114 cc->y = new_y;
4115 } else {
4116 if (emsg)
4117 pline1(emsg);
4118 return 0;
4120 return 1;
4124 getdir(s)
4125 const char *s;
4127 char dirsym;
4128 int is_mov;
4130 retry:
4131 if (in_doagain || *readchar_queue)
4132 dirsym = readchar();
4133 else
4134 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
4135 (char *) 0, '\0');
4136 /* remove the prompt string so caller won't have to */
4137 clear_nhwindow(WIN_MESSAGE);
4139 if (redraw_cmd(dirsym)) { /* ^R */
4140 docrt(); /* redraw */
4141 goto retry;
4143 savech(dirsym);
4145 if (dirsym == Cmd.spkeys[NHKF_GETDIR_SELF]
4146 || dirsym == Cmd.spkeys[NHKF_GETDIR_SELF2]) {
4147 u.dx = u.dy = u.dz = 0;
4148 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
4149 boolean did_help = FALSE, help_requested;
4151 if (!index(quitchars, dirsym)) {
4152 help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]);
4153 if (help_requested || iflags.cmdassist) {
4154 did_help = help_dir((s && *s == '^') ? dirsym : 0,
4155 help_requested ? (const char *) 0
4156 : "Invalid direction key!");
4157 if (help_requested)
4158 goto retry;
4160 if (!did_help)
4161 pline("What a strange direction!");
4163 return 0;
4164 } else if (is_mov && !dxdy_moveok()) {
4165 You_cant("orient yourself that direction.");
4166 return 0;
4168 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
4169 confdir();
4170 return 1;
4173 STATIC_OVL void
4174 show_direction_keys(win, nodiag)
4175 winid win;
4176 boolean nodiag;
4178 char buf[BUFSZ];
4180 if (nodiag) {
4181 Sprintf(buf, " %c ", Cmd.move_N);
4182 putstr(win, 0, buf);
4183 putstr(win, 0, " | ");
4184 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4185 putstr(win, 0, buf);
4186 putstr(win, 0, " | ");
4187 Sprintf(buf, " %c ", Cmd.move_S);
4188 putstr(win, 0, buf);
4189 } else {
4190 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
4191 Cmd.move_NE);
4192 putstr(win, 0, buf);
4193 putstr(win, 0, " \\ | / ");
4194 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4195 putstr(win, 0, buf);
4196 putstr(win, 0, " / | \\ ");
4197 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
4198 Cmd.move_SE);
4199 putstr(win, 0, buf);
4203 STATIC_OVL boolean
4204 help_dir(sym, msg)
4205 char sym;
4206 const char *msg;
4208 static const char wiz_only_list[] = "EFGIVW";
4209 char ctrl;
4210 winid win;
4211 char buf[BUFSZ], buf2[BUFSZ], *explain;
4213 win = create_nhwindow(NHW_TEXT);
4214 if (!win)
4215 return FALSE;
4216 if (msg) {
4217 Sprintf(buf, "cmdassist: %s", msg);
4218 putstr(win, 0, buf);
4219 putstr(win, 0, "");
4221 if (letter(sym) || sym == '[') { /* 'dat/cmdhelp' shows ESC as ^[ */
4222 sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */
4223 ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
4224 if ((explain = dowhatdoes_core(ctrl, buf2)) != 0
4225 && (!index(wiz_only_list, sym) || wizard)) {
4226 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
4227 index(wiz_only_list, sym)
4228 ? ""
4229 : " as specified in the Guidebook");
4230 putstr(win, 0, buf);
4231 putstr(win, 0, "");
4232 putstr(win, 0, explain);
4233 putstr(win, 0, "");
4234 putstr(win, 0,
4235 "To use that command, hold down the <Ctrl> key as a shift");
4236 Sprintf(buf, "and press the <%c> key.", sym);
4237 putstr(win, 0, buf);
4238 putstr(win, 0, "");
4242 Sprintf(buf, "Valid direction keys %sare:",
4243 NODIAG(u.umonnum) ? "in your current form " : "");
4244 putstr(win, 0, buf);
4245 show_direction_keys(win, NODIAG(u.umonnum));
4247 putstr(win, 0, "");
4248 putstr(win, 0, " < up");
4249 putstr(win, 0, " > down");
4250 Sprintf(buf, " %4s direct at yourself",
4251 visctrl(Cmd.spkeys[NHKF_GETDIR_SELF]));
4252 putstr(win, 0, buf);
4253 if (msg) {
4254 /* non-null msg means that this wasn't an explicit user request */
4255 putstr(win, 0, "");
4256 putstr(win, 0,
4257 "(Suppress this message with !cmdassist in config file.)");
4259 display_nhwindow(win, FALSE);
4260 destroy_nhwindow(win);
4261 return TRUE;
4264 void
4265 confdir()
4267 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
4269 u.dx = xdir[x];
4270 u.dy = ydir[x];
4271 return;
4274 const char *
4275 directionname(dir)
4276 int dir;
4278 static NEARDATA const char *const dirnames[] = {
4279 "west", "northwest", "north", "northeast", "east",
4280 "southeast", "south", "southwest", "down", "up",
4283 if (dir < 0 || dir >= SIZE(dirnames))
4284 return "invalid";
4285 return dirnames[dir];
4289 isok(x, y)
4290 register int x, y;
4292 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
4293 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
4296 static NEARDATA int last_multi;
4299 * convert a MAP window position into a movecmd
4301 const char *
4302 click_to_cmd(x, y, mod)
4303 int x, y, mod;
4305 int dir;
4306 static char cmd[4];
4307 cmd[1] = 0;
4309 if (iflags.clicklook && mod == CLICK_2) {
4310 clicklook_cc.x = x;
4311 clicklook_cc.y = y;
4312 cmd[0] = Cmd.spkeys[NHKF_CLICKLOOK];
4313 return cmd;
4316 x -= u.ux;
4317 y -= u.uy;
4319 if (flags.travelcmd) {
4320 if (abs(x) <= 1 && abs(y) <= 1) {
4321 x = sgn(x), y = sgn(y);
4322 } else {
4323 u.tx = u.ux + x;
4324 u.ty = u.uy + y;
4325 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4326 return cmd;
4329 if (x == 0 && y == 0) {
4330 /* here */
4331 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
4332 || IS_SINK(levl[u.ux][u.uy].typ)) {
4333 cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip);
4334 return cmd;
4335 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
4336 cmd[0] = cmd_from_func(dosit);
4337 return cmd;
4338 } else if ((u.ux == xupstair && u.uy == yupstair)
4339 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4340 && sstairs.up)
4341 || (u.ux == xupladder && u.uy == yupladder)) {
4342 cmd[0] = cmd_from_func(doup);
4343 return cmd;
4344 } else if ((u.ux == xdnstair && u.uy == ydnstair)
4345 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4346 && !sstairs.up)
4347 || (u.ux == xdnladder && u.uy == ydnladder)) {
4348 cmd[0] = cmd_from_func(dodown);
4349 return cmd;
4350 } else if (OBJ_AT(u.ux, u.uy)) {
4351 cmd[0] = cmd_from_func(Is_container(level.objects[u.ux][u.uy])
4352 ? doloot : dopickup);
4353 return cmd;
4354 } else {
4355 cmd[0] = cmd_from_func(donull); /* just rest */
4356 return cmd;
4360 /* directional commands */
4362 dir = xytod(x, y);
4364 if (!m_at(u.ux + x, u.uy + y)
4365 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
4366 cmd[1] = Cmd.dirchars[dir];
4367 cmd[2] = '\0';
4368 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
4369 /* slight assistance to the player: choose kick/open for them
4371 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
4372 cmd[0] = cmd_from_func(dokick);
4373 return cmd;
4375 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
4376 cmd[0] = cmd_from_func(doopen);
4377 return cmd;
4380 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
4381 cmd[0] = cmd_from_func(dosearch);
4382 cmd[1] = 0;
4383 return cmd;
4386 } else {
4387 /* convert without using floating point, allowing sloppy clicking */
4388 if (x > 2 * abs(y))
4389 x = 1, y = 0;
4390 else if (y > 2 * abs(x))
4391 x = 0, y = 1;
4392 else if (x < -2 * abs(y))
4393 x = -1, y = 0;
4394 else if (y < -2 * abs(x))
4395 x = 0, y = -1;
4396 else
4397 x = sgn(x), y = sgn(y);
4399 if (x == 0 && y == 0) {
4400 /* map click on player to "rest" command */
4401 cmd[0] = cmd_from_func(donull);
4402 return cmd;
4404 dir = xytod(x, y);
4407 /* move, attack, etc. */
4408 cmd[1] = 0;
4409 if (mod == CLICK_1) {
4410 cmd[0] = Cmd.dirchars[dir];
4411 } else {
4412 cmd[0] = (Cmd.num_pad
4413 ? M(Cmd.dirchars[dir])
4414 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
4417 return cmd;
4420 char
4421 get_count(allowchars, inkey, maxcount, count)
4422 char *allowchars;
4423 char inkey;
4424 long maxcount;
4425 long *count;
4427 char qbuf[QBUFSZ];
4428 int key;
4429 long cnt = 0L;
4430 boolean backspaced = FALSE;
4431 /* this should be done in port code so that we have erase_char
4432 and kill_char available; we can at least fake erase_char */
4433 #define STANDBY_erase_char '\177'
4435 for (;;) {
4436 if (inkey) {
4437 key = inkey;
4438 inkey = '\0';
4439 } else
4440 key = readchar();
4442 if (digit(key)) {
4443 cnt = 10L * cnt + (long) (key - '0');
4444 if (cnt < 0)
4445 cnt = 0;
4446 else if (maxcount > 0 && cnt > maxcount)
4447 cnt = maxcount;
4448 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
4449 cnt = cnt / 10;
4450 backspaced = TRUE;
4451 } else if (key == Cmd.spkeys[NHKF_ESC]) {
4452 break;
4453 } else if (!allowchars || index(allowchars, key)) {
4454 *count = cnt;
4455 break;
4458 if (cnt > 9 || backspaced) {
4459 clear_nhwindow(WIN_MESSAGE);
4460 if (backspaced && !cnt) {
4461 Sprintf(qbuf, "Count: ");
4462 } else {
4463 Sprintf(qbuf, "Count: %ld", cnt);
4464 backspaced = FALSE;
4466 pline1(qbuf);
4467 mark_synch();
4470 return key;
4474 STATIC_OVL char *
4475 parse()
4477 #ifdef LINT /* static char in_line[COLNO]; */
4478 char in_line[COLNO];
4479 #else
4480 static char in_line[COLNO];
4481 #endif
4482 register int foo;
4483 boolean prezero = FALSE;
4485 multi = 0;
4486 context.move = 1;
4487 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
4489 #ifdef ALTMETA
4490 alt_esc = iflags.altmeta; /* readchar() hack */
4491 #endif
4492 if (!Cmd.num_pad || (foo = readchar()) == Cmd.spkeys[NHKF_COUNT]) {
4493 long tmpmulti = multi;
4495 foo = get_count(NULL, '\0', LARGEST_INT, &tmpmulti);
4496 last_multi = multi = tmpmulti;
4498 #ifdef ALTMETA
4499 alt_esc = FALSE; /* readchar() reset */
4500 #endif
4502 if (foo == Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */
4503 clear_nhwindow(WIN_MESSAGE);
4504 multi = last_multi = 0;
4505 } else if (foo == Cmd.spkeys[NHKF_DOAGAIN] || in_doagain) {
4506 multi = last_multi;
4507 } else {
4508 last_multi = multi;
4509 savech(0); /* reset input queue */
4510 savech((char) foo);
4513 if (multi) {
4514 multi--;
4515 save_cm = in_line;
4516 } else {
4517 save_cm = (char *) 0;
4519 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
4520 if (Cmd.pcHack_compat) {
4521 /* This handles very old inconsistent DOS/Windows behaviour
4522 in a different way: earlier, the keyboard handler mapped
4523 these, which caused counts to be strange when entered
4524 from the number pad. Now do not map them until here. */
4525 switch (foo) {
4526 case '5':
4527 foo = Cmd.spkeys[NHKF_RUSH];
4528 break;
4529 case M('5'):
4530 foo = Cmd.spkeys[NHKF_RUN];
4531 break;
4532 case M('0'):
4533 foo = Cmd.spkeys[NHKF_DOINV];
4534 break;
4535 default:
4536 break; /* as is */
4540 in_line[0] = foo;
4541 in_line[1] = '\0';
4542 if (prefix_cmd(foo)) {
4543 foo = readchar();
4544 savech((char) foo);
4545 in_line[1] = foo;
4546 in_line[2] = 0;
4548 clear_nhwindow(WIN_MESSAGE);
4549 if (prezero)
4550 in_line[0] = Cmd.spkeys[NHKF_ESC];
4551 return in_line;
4554 #ifdef HANGUPHANDLING
4555 /* some very old systems, or descendents of such systems, expect signal
4556 handlers to have return type `int', but they don't actually inspect
4557 the return value so we should be safe using `void' unconditionally */
4558 /*ARGUSED*/
4559 void
4560 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4561 int sig_unused UNUSED;
4563 if (program_state.exiting)
4564 program_state.in_moveloop = 0;
4565 nhwindows_hangup();
4566 #ifdef SAFERHANGUP
4567 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4568 and a couple of other places; actual hangup handling occurs then.
4569 This is 'safer' because it disallows certain cheats and also
4570 protects against losing objects in the process of being thrown,
4571 but also potentially riskier because the disconnected program
4572 must continue running longer before attempting a hangup save. */
4573 program_state.done_hup++;
4574 /* defer hangup iff game appears to be in progress */
4575 if (program_state.in_moveloop && program_state.something_worth_saving)
4576 return;
4577 #endif /* SAFERHANGUP */
4578 end_of_input();
4581 void
4582 end_of_input()
4584 #ifdef NOSAVEONHANGUP
4585 #ifdef INSURANCE
4586 if (flags.ins_chkpt && program_state.something_worth_saving)
4587 program_statue.preserve_locks = 1; /* keep files for recovery */
4588 #endif
4589 program_state.something_worth_saving = 0; /* don't save */
4590 #endif
4592 #ifndef SAFERHANGUP
4593 if (!program_state.done_hup++)
4594 #endif
4595 if (program_state.something_worth_saving)
4596 (void) dosave0();
4597 if (iflags.window_inited)
4598 exit_nhwindows((char *) 0);
4599 clearlocks();
4600 terminate(EXIT_SUCCESS);
4601 /*NOTREACHED*/ /* not necessarily true for vms... */
4602 return;
4604 #endif /* HANGUPHANDLING */
4606 char
4607 readchar()
4609 register int sym;
4610 int x = u.ux, y = u.uy, mod = 0;
4612 if (*readchar_queue)
4613 sym = *readchar_queue++;
4614 else
4615 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4617 #ifdef NR_OF_EOFS
4618 if (sym == EOF) {
4619 register int cnt = NR_OF_EOFS;
4621 * Some SYSV systems seem to return EOFs for various reasons
4622 * (?like when one hits break or for interrupted systemcalls?),
4623 * and we must see several before we quit.
4625 do {
4626 clearerr(stdin); /* omit if clearerr is undefined */
4627 sym = pgetchar();
4628 } while (--cnt && sym == EOF);
4630 #endif /* NR_OF_EOFS */
4632 if (sym == EOF) {
4633 #ifdef HANGUPHANDLING
4634 hangup(0); /* call end_of_input() or set program_state.done_hup */
4635 #endif
4636 sym = '\033';
4637 #ifdef ALTMETA
4638 } else if (sym == '\033' && alt_esc) {
4639 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4640 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4641 if (sym == EOF || sym == 0)
4642 sym = '\033';
4643 else if (sym != '\033')
4644 sym |= 0200; /* force 8th bit on */
4645 #endif /*ALTMETA*/
4646 } else if (sym == 0) {
4647 /* click event */
4648 readchar_queue = click_to_cmd(x, y, mod);
4649 sym = *readchar_queue++;
4651 return (char) sym;
4654 STATIC_PTR int
4655 dotravel(VOID_ARGS)
4657 /* Keyboard travel command */
4658 static char cmd[2];
4659 coord cc;
4661 if (!flags.travelcmd)
4662 return 0;
4663 cmd[1] = 0;
4664 cc.x = iflags.travelcc.x;
4665 cc.y = iflags.travelcc.y;
4666 if (cc.x == -1 && cc.y == -1) {
4667 /* No cached destination, start attempt from current position */
4668 cc.x = u.ux;
4669 cc.y = u.uy;
4671 iflags.getloc_travelmode = TRUE;
4672 if (iflags.menu_requested) {
4673 if (!getpos_menu(&cc, TRUE)) {
4674 iflags.getloc_travelmode = FALSE;
4675 return 0;
4677 } else {
4678 pline("Where do you want to travel to?");
4679 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4680 /* user pressed ESC */
4681 iflags.getloc_travelmode = FALSE;
4682 return 0;
4685 iflags.getloc_travelmode = FALSE;
4686 iflags.travelcc.x = u.tx = cc.x;
4687 iflags.travelcc.y = u.ty = cc.y;
4688 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4689 readchar_queue = cmd;
4690 return 0;
4693 #ifdef PORT_DEBUG
4694 extern void NDECL(win32con_debug_keystrokes);
4695 extern void NDECL(win32con_handler_info);
4698 wiz_port_debug()
4700 int n, k;
4701 winid win;
4702 anything any;
4703 int item = 'a';
4704 int num_menu_selections;
4705 struct menu_selection_struct {
4706 char *menutext;
4707 void NDECL((*fn));
4708 } menu_selections[] = {
4709 #ifdef WIN32
4710 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4711 { "show keystroke handler information (tty only)",
4712 win32con_handler_info },
4713 #endif
4714 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4717 num_menu_selections = SIZE(menu_selections) - 1;
4718 if (num_menu_selections > 0) {
4719 menu_item *pick_list;
4720 win = create_nhwindow(NHW_MENU);
4721 start_menu(win);
4722 for (k = 0; k < num_menu_selections; ++k) {
4723 any.a_int = k + 1;
4724 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4725 menu_selections[k].menutext, MENU_UNSELECTED);
4727 end_menu(win, "Which port debugging feature?");
4728 n = select_menu(win, PICK_ONE, &pick_list);
4729 destroy_nhwindow(win);
4730 if (n > 0) {
4731 n = pick_list[0].item.a_int - 1;
4732 free((genericptr_t) pick_list);
4733 /* execute the function */
4734 (*menu_selections[n].fn)();
4736 } else
4737 pline("No port-specific debug capability defined.");
4738 return 0;
4740 #endif /*PORT_DEBUG*/
4743 * Parameter validator for generic yes/no function to prevent
4744 * the core from sending too long a prompt string to the
4745 * window port causing a buffer overflow there.
4747 char
4748 yn_function(query, resp, def)
4749 const char *query, *resp;
4750 char def;
4752 char qbuf[QBUFSZ];
4754 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4756 /* maximum acceptable length is QBUFSZ-1 */
4757 if (strlen(query) >= QBUFSZ) {
4758 /* caller shouldn't have passed anything this long */
4759 paniclog("Query truncated: ", query);
4760 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4761 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4762 query = qbuf;
4764 return (*windowprocs.win_yn_function)(query, resp, def);
4767 /* for paranoid_confirm:quit,die,attack prompting */
4768 boolean
4769 paranoid_query(be_paranoid, prompt)
4770 boolean be_paranoid;
4771 const char *prompt;
4773 boolean confirmed_ok;
4775 /* when paranoid, player must respond with "yes" rather than just 'y'
4776 to give the go-ahead for this query; default is "no" unless the
4777 ParanoidConfirm flag is set in which case there's no default */
4778 if (be_paranoid) {
4779 char qbuf[QBUFSZ], ans[BUFSZ];
4780 const char *promptprefix = "", *responsetype = ParanoidConfirm
4781 ? "(yes|no)"
4782 : "(yes) [no]";
4783 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4785 /* in addition to being paranoid about this particular
4786 query, we might be even more paranoid about all paranoia
4787 responses (ie, ParanoidConfirm is set) in which case we
4788 require "no" to reject in addition to "yes" to confirm
4789 (except we won't loop if response is ESC; it means no) */
4790 do {
4791 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4792 getlin(qbuf, ans);
4793 (void) mungspaces(ans);
4794 confirmed_ok = !strcmpi(ans, "yes");
4795 if (confirmed_ok || *ans == '\033')
4796 break;
4797 promptprefix = "\"Yes\" or \"No\": ";
4798 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4799 } else
4800 confirmed_ok = (yn(prompt) == 'y');
4802 return confirmed_ok;
4806 dosuspend_core()
4808 #ifdef SUSPEND
4809 /* Does current window system support suspend? */
4810 if ((*windowprocs.win_can_suspend)()) {
4811 /* NB: SYSCF SHELLERS handled in port code. */
4812 dosuspend();
4813 } else
4814 #endif
4815 Norep("Suspend command not available.");
4816 return 0;
4819 /*cmd.c*/