Show control key combos with uppercase alpha
[aNetHack.git] / src / cmd.c
blob79e0962499c017e1bce89fd5d7a7e41da887caa1
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 "lev.h"
7 #include "func_tab.h"
9 #ifdef ALTMETA
10 STATIC_VAR boolean alt_esc = FALSE;
11 #endif
13 struct cmd Cmd = { 0 }; /* flag.h */
15 extern const char *hu_stat[]; /* hunger status from eat.c */
16 extern const char *enc_stat[]; /* encumbrance status from botl.c */
18 #ifdef UNIX
20 * Some systems may have getchar() return EOF for various reasons, and
21 * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
23 #if defined(SYSV) || defined(DGUX) || defined(HPUX)
24 #define NR_OF_EOFS 20
25 #endif
26 #endif
28 #define CMD_TRAVEL (char) 0x90
29 #define CMD_CLICKLOOK (char) 0x8F
31 #ifdef DEBUG
32 extern int NDECL(wiz_debug_cmd_bury);
33 extern int NDECL(wiz_debug_cmd_traveldisplay);
34 #endif
36 #ifdef DUMB /* stuff commented out in extern.h, but needed here */
37 extern int NDECL(doapply); /**/
38 extern int NDECL(dorub); /**/
39 extern int NDECL(dojump); /**/
40 extern int NDECL(doextlist); /**/
41 extern int NDECL(enter_explore_mode); /**/
42 extern int NDECL(dodrop); /**/
43 extern int NDECL(doddrop); /**/
44 extern int NDECL(dodown); /**/
45 extern int NDECL(doup); /**/
46 extern int NDECL(donull); /**/
47 extern int NDECL(dowipe); /**/
48 extern int NDECL(docallcnd); /**/
49 extern int NDECL(dotakeoff); /**/
50 extern int NDECL(doremring); /**/
51 extern int NDECL(dowear); /**/
52 extern int NDECL(doputon); /**/
53 extern int NDECL(doddoremarm); /**/
54 extern int NDECL(dokick); /**/
55 extern int NDECL(dofire); /**/
56 extern int NDECL(dothrow); /**/
57 extern int NDECL(doeat); /**/
58 extern int NDECL(done2); /**/
59 extern int NDECL(vanquished); /**/
60 extern int NDECL(doengrave); /**/
61 extern int NDECL(dopickup); /**/
62 extern int NDECL(ddoinv); /**/
63 extern int NDECL(dotypeinv); /**/
64 extern int NDECL(dolook); /**/
65 extern int NDECL(doprgold); /**/
66 extern int NDECL(doprwep); /**/
67 extern int NDECL(doprarm); /**/
68 extern int NDECL(doprring); /**/
69 extern int NDECL(dopramulet); /**/
70 extern int NDECL(doprtool); /**/
71 extern int NDECL(dosuspend); /**/
72 extern int NDECL(doforce); /**/
73 extern int NDECL(doopen); /**/
74 extern int NDECL(doclose); /**/
75 extern int NDECL(dosh); /**/
76 extern int NDECL(dodiscovered); /**/
77 extern int NDECL(doclassdisco); /**/
78 extern int NDECL(doset); /**/
79 extern int NDECL(dotogglepickup); /**/
80 extern int NDECL(dowhatis); /**/
81 extern int NDECL(doquickwhatis); /**/
82 extern int NDECL(dowhatdoes); /**/
83 extern int NDECL(dohelp); /**/
84 extern int NDECL(dohistory); /**/
85 extern int NDECL(doloot); /**/
86 extern int NDECL(dodrink); /**/
87 extern int NDECL(dodip); /**/
88 extern int NDECL(dosacrifice); /**/
89 extern int NDECL(dopray); /**/
90 extern int NDECL(dotip); /**/
91 extern int NDECL(doturn); /**/
92 extern int NDECL(doredraw); /**/
93 extern int NDECL(doread); /**/
94 extern int NDECL(dosave); /**/
95 extern int NDECL(dosearch); /**/
96 extern int NDECL(doidtrap); /**/
97 extern int NDECL(dopay); /**/
98 extern int NDECL(dosit); /**/
99 extern int NDECL(dotalk); /**/
100 extern int NDECL(docast); /**/
101 extern int NDECL(dovspell); /**/
102 extern int NDECL(dotele); /**/
103 extern int NDECL(dountrap); /**/
104 extern int NDECL(doversion); /**/
105 extern int NDECL(doextversion); /**/
106 extern int NDECL(doswapweapon); /**/
107 extern int NDECL(dowield); /**/
108 extern int NDECL(dowieldquiver); /**/
109 extern int NDECL(dozap); /**/
110 extern int NDECL(doorganize); /**/
111 #endif /* DUMB */
113 static int NDECL(dosuspend_core); /**/
115 static int NDECL((*timed_occ_fn));
117 STATIC_PTR int NDECL(doprev_message);
118 STATIC_PTR int NDECL(timed_occupation);
119 STATIC_PTR int NDECL(doextcmd);
120 STATIC_PTR int NDECL(domonability);
121 STATIC_PTR int NDECL(dotravel);
122 STATIC_PTR int NDECL(doterrain);
123 STATIC_PTR int NDECL(wiz_wish);
124 STATIC_PTR int NDECL(wiz_identify);
125 STATIC_PTR int NDECL(wiz_intrinsic);
126 STATIC_PTR int NDECL(wiz_map);
127 STATIC_PTR int NDECL(wiz_makemap);
128 STATIC_PTR int NDECL(wiz_genesis);
129 STATIC_PTR int NDECL(wiz_where);
130 STATIC_PTR int NDECL(wiz_detect);
131 STATIC_PTR int NDECL(wiz_panic);
132 STATIC_PTR int NDECL(wiz_polyself);
133 STATIC_PTR int NDECL(wiz_level_tele);
134 STATIC_PTR int NDECL(wiz_level_change);
135 STATIC_PTR int NDECL(wiz_show_seenv);
136 STATIC_PTR int NDECL(wiz_show_vision);
137 STATIC_PTR int NDECL(wiz_smell);
138 STATIC_PTR int NDECL(wiz_mon_polycontrol);
139 STATIC_PTR int NDECL(wiz_show_wmodes);
140 STATIC_DCL void NDECL(wiz_map_levltyp);
141 STATIC_DCL void NDECL(wiz_levltyp_legend);
142 #if defined(__BORLANDC__) && !defined(_WIN32)
143 extern void FDECL(show_borlandc_stats, (winid));
144 #endif
145 #ifdef DEBUG_MIGRATING_MONS
146 STATIC_PTR int NDECL(wiz_migrate_mons);
147 #endif
148 STATIC_DCL int FDECL(size_monst, (struct monst *, BOOLEAN_P));
149 STATIC_DCL int FDECL(size_obj, (struct obj *));
150 STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *,
151 BOOLEAN_P, BOOLEAN_P));
152 STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *,
153 BOOLEAN_P, long *, long *));
154 STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *,
155 long *, long *));
156 STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *,
157 BOOLEAN_P, long *, long *));
158 STATIC_DCL void FDECL(contained_stats, (winid, const char *, long *, long *));
159 STATIC_DCL void FDECL(misc_stats, (winid, long *, long *));
160 STATIC_PTR int NDECL(wiz_show_stats);
161 STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*))));
162 #ifdef PORT_DEBUG
163 STATIC_DCL int NDECL(wiz_port_debug);
164 #endif
165 STATIC_PTR int NDECL(wiz_rumor_check);
166 STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*))));
167 STATIC_PTR int NDECL(doattributes);
168 STATIC_PTR int NDECL(doconduct); /**/
170 STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *,
171 const char *));
172 STATIC_DCL char *FDECL(enlght_combatinc, (const char *, int, int, char *));
173 STATIC_DCL void FDECL(enlght_halfdmg, (int, int));
174 STATIC_DCL boolean NDECL(walking_on_water);
175 STATIC_DCL boolean FDECL(cause_known, (int));
176 STATIC_DCL char *FDECL(attrval, (int, int, char *));
177 STATIC_DCL void FDECL(background_enlightenment, (int, int));
178 STATIC_DCL void FDECL(characteristics_enlightenment, (int, int));
179 STATIC_DCL void FDECL(one_characteristic, (int, int, int));
180 STATIC_DCL void FDECL(status_enlightenment, (int, int));
181 STATIC_DCL void FDECL(attributes_enlightenment, (int, int));
183 static const char *readchar_queue = "";
184 static coord clicklook_cc;
186 STATIC_DCL char *NDECL(parse);
187 STATIC_DCL void FDECL(show_direction_keys, (winid, BOOLEAN_P));
188 STATIC_DCL boolean FDECL(help_dir, (CHAR_P, const char *));
190 STATIC_PTR int
191 doprev_message(VOID_ARGS)
193 return nh_doprev_message();
196 /* Count down by decrementing multi */
197 STATIC_PTR int
198 timed_occupation(VOID_ARGS)
200 (*timed_occ_fn)();
201 if (multi > 0)
202 multi--;
203 return multi > 0;
206 /* If you have moved since initially setting some occupations, they
207 * now shouldn't be able to restart.
209 * The basic rule is that if you are carrying it, you can continue
210 * since it is with you. If you are acting on something at a distance,
211 * your orientation to it must have changed when you moved.
213 * The exception to this is taking off items, since they can be taken
214 * off in a number of ways in the intervening time, screwing up ordering.
216 * Currently: Take off all armor.
217 * Picking Locks / Forcing Chests.
218 * Setting traps.
220 void
221 reset_occupations()
223 reset_remarm();
224 reset_pick();
225 reset_trapset();
228 /* If a time is given, use it to timeout this function, otherwise the
229 * function times out by its own means.
231 void
232 set_occupation(fn, txt, xtime)
233 int NDECL((*fn));
234 const char *txt;
235 int xtime;
237 if (xtime) {
238 occupation = timed_occupation;
239 timed_occ_fn = fn;
240 } else
241 occupation = fn;
242 occtxt = txt;
243 occtime = 0;
244 return;
247 STATIC_DCL char NDECL(popch);
249 /* Provide a means to redo the last command. The flag `in_doagain' is set
250 * to true while redoing the command. This flag is tested in commands that
251 * require additional input (like `throw' which requires a thing and a
252 * direction), and the input prompt is not shown. Also, while in_doagain is
253 * TRUE, no keystrokes can be saved into the saveq.
255 #define BSIZE 20
256 static char pushq[BSIZE], saveq[BSIZE];
257 static NEARDATA int phead, ptail, shead, stail;
259 STATIC_OVL char
260 popch()
262 /* If occupied, return '\0', letting tgetch know a character should
263 * be read from the keyboard. If the character read is not the
264 * ABORT character (as checked in pcmain.c), that character will be
265 * pushed back on the pushq.
267 if (occupation)
268 return '\0';
269 if (in_doagain)
270 return (char) ((shead != stail) ? saveq[stail++] : '\0');
271 else
272 return (char) ((phead != ptail) ? pushq[ptail++] : '\0');
275 char
276 pgetchar() /* courtesy of aeb@cwi.nl */
278 register int ch;
280 if (!(ch = popch()))
281 ch = nhgetch();
282 return (char) ch;
285 /* A ch == 0 resets the pushq */
286 void
287 pushch(ch)
288 char ch;
290 if (!ch)
291 phead = ptail = 0;
292 if (phead < BSIZE)
293 pushq[phead++] = ch;
294 return;
297 /* A ch == 0 resets the saveq. Only save keystrokes when not
298 * replaying a previous command.
300 void
301 savech(ch)
302 char ch;
304 if (!in_doagain) {
305 if (!ch)
306 phead = ptail = shead = stail = 0;
307 else if (shead < BSIZE)
308 saveq[shead++] = ch;
310 return;
313 /* here after # - now read a full-word command */
314 STATIC_PTR int
315 doextcmd(VOID_ARGS)
317 int idx, retval;
318 int NDECL((*func));
320 /* keep repeating until we don't run help or quit */
321 do {
322 idx = get_ext_cmd();
323 if (idx < 0)
324 return 0; /* quit */
326 func = extcmdlist[idx].ef_funct;
327 if (!wizard && (extcmdlist[idx].flags & WIZMODECMD)) {
328 You("can't do that.");
329 return 0;
331 if (iflags.menu_requested && !accept_menu_prefix(func)) {
332 pline("'%s' prefix has no effect for the %s command.",
333 visctrl(Cmd.spkeys[NHKF_REQMENU]),
334 extcmdlist[idx].ef_txt);
335 iflags.menu_requested = FALSE;
337 retval = (*func)();
338 } while (func == doextlist);
340 return retval;
343 /* here after #? - now list all full-word commands */
345 doextlist(VOID_ARGS)
347 register const struct ext_func_tab *efp;
348 char buf[BUFSZ];
349 winid datawin;
350 char ch = cmd_from_func(doextcmd);
352 datawin = create_nhwindow(NHW_TEXT);
353 putstr(datawin, 0, "");
354 putstr(datawin, 0, " Extended Commands List");
355 putstr(datawin, 0, "");
356 if (ch) {
357 Sprintf(buf, " Press '%s', then type:",
358 visctrl(ch));
359 putstr(datawin, 0, buf);
360 putstr(datawin, 0, "");
363 for (efp = extcmdlist; efp->ef_txt; efp++) {
364 if (!wizard && (efp->flags & WIZMODECMD))
365 continue;
366 Sprintf(buf, " %-15s %c %s.",
367 efp->ef_txt,
368 (efp->flags & AUTOCOMPLETE) ? '*' : ' ',
369 efp->ef_desc);
370 putstr(datawin, 0, buf);
372 putstr(datawin, 0, "");
373 putstr(datawin, 0, " Commands marked with a * will be autocompleted.");
374 display_nhwindow(datawin, FALSE);
375 destroy_nhwindow(datawin);
376 return 0;
379 #ifdef TTY_GRAPHICS
380 #define MAX_EXT_CMD 200 /* Change if we ever have more ext cmds */
383 * This is currently used only by the tty port and is
384 * controlled via runtime option 'extmenu'.
385 * ``# ?'' is counted towards the limit of the number of commands,
386 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
388 * Here after # - now show pick-list of possible commands.
391 extcmd_via_menu()
393 const struct ext_func_tab *efp;
394 menu_item *pick_list = (menu_item *) 0;
395 winid win;
396 anything any;
397 const struct ext_func_tab *choices[MAX_EXT_CMD + 1];
398 char buf[BUFSZ];
399 char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
400 int i, n, nchoices, acount;
401 int ret, biggest;
402 int accelerator, prevaccelerator;
403 int matchlevel = 0;
405 ret = 0;
406 cbuf[0] = '\0';
407 biggest = 0;
408 while (!ret) {
409 i = n = 0;
410 any = zeroany;
411 /* populate choices */
412 for (efp = extcmdlist; efp->ef_txt; efp++) {
413 if (!(efp->flags & AUTOCOMPLETE)
414 || (!wizard && (efp->flags & WIZMODECMD)))
415 continue;
416 if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
417 choices[i] = efp;
418 if ((int) strlen(efp->ef_desc) > biggest) {
419 biggest = strlen(efp->ef_desc);
420 Sprintf(fmtstr, "%%-%ds", biggest + 15);
422 if (++i > MAX_EXT_CMD) {
423 #if defined(BETA)
424 impossible(
425 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
426 MAX_EXT_CMD);
427 #endif /* BETA */
428 iflags.extmenu = 0;
429 return -1;
433 choices[i] = (struct ext_func_tab *) 0;
434 nchoices = i;
435 /* if we're down to one, we have our selection so get out of here */
436 if (nchoices == 1) {
437 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
438 if ((extcmdlist[i].flags & AUTOCOMPLETE)
439 && !(!wizard && (extcmdlist[i].flags & WIZMODECMD))
440 && !strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) {
441 ret = i;
442 break;
444 break;
447 /* otherwise... */
448 win = create_nhwindow(NHW_MENU);
449 start_menu(win);
450 accelerator = prevaccelerator = 0;
451 acount = 0;
452 for (i = 0; choices[i]; ++i) {
453 accelerator = choices[i]->ef_txt[matchlevel];
454 if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) {
455 if (acount) {
456 /* flush extended cmds for that letter already in buf */
457 Sprintf(buf, fmtstr, prompt);
458 any.a_char = prevaccelerator;
459 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE,
460 buf, FALSE);
461 acount = 0;
464 prevaccelerator = accelerator;
465 if (!acount || nchoices < (ROWNO - 3)) {
466 Sprintf(prompt, "%s [%s]", choices[i]->ef_txt,
467 choices[i]->ef_desc);
468 } else if (acount == 1) {
469 Sprintf(prompt, "%s or %s", choices[i - 1]->ef_txt,
470 choices[i]->ef_txt);
471 } else {
472 Strcat(prompt, " or ");
473 Strcat(prompt, choices[i]->ef_txt);
475 ++acount;
477 if (acount) {
478 /* flush buf */
479 Sprintf(buf, fmtstr, prompt);
480 any.a_char = prevaccelerator;
481 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf,
482 FALSE);
484 Sprintf(prompt, "Extended Command: %s", cbuf);
485 end_menu(win, prompt);
486 n = select_menu(win, PICK_ONE, &pick_list);
487 destroy_nhwindow(win);
488 if (n == 1) {
489 if (matchlevel > (QBUFSZ - 2)) {
490 free((genericptr_t) pick_list);
491 #if defined(BETA)
492 impossible("Too many chars (%d) entered in extcmd_via_menu()",
493 matchlevel);
494 #endif
495 ret = -1;
496 } else {
497 cbuf[matchlevel++] = pick_list[0].item.a_char;
498 cbuf[matchlevel] = '\0';
499 free((genericptr_t) pick_list);
501 } else {
502 if (matchlevel) {
503 ret = 0;
504 matchlevel = 0;
505 } else
506 ret = -1;
509 return ret;
511 #endif /* TTY_GRAPHICS */
513 /* #monster command - use special monster ability while polymorphed */
514 STATIC_PTR int
515 domonability(VOID_ARGS)
517 if (can_breathe(youmonst.data))
518 return dobreathe();
519 else if (attacktype(youmonst.data, AT_SPIT))
520 return dospit();
521 else if (youmonst.data->mlet == S_NYMPH)
522 return doremove();
523 else if (attacktype(youmonst.data, AT_GAZE))
524 return dogaze();
525 else if (is_were(youmonst.data))
526 return dosummon();
527 else if (webmaker(youmonst.data))
528 return dospinweb();
529 else if (is_hider(youmonst.data))
530 return dohide();
531 else if (is_mind_flayer(youmonst.data))
532 return domindblast();
533 else if (u.umonnum == PM_GREMLIN) {
534 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
535 if (split_mon(&youmonst, (struct monst *) 0))
536 dryup(u.ux, u.uy, TRUE);
537 } else
538 There("is no fountain here.");
539 } else if (is_unicorn(youmonst.data)) {
540 use_unicorn_horn((struct obj *) 0);
541 return 1;
542 } else if (youmonst.data->msound == MS_SHRIEK) {
543 You("shriek.");
544 if (u.uburied)
545 pline("Unfortunately sound does not carry well through rock.");
546 else
547 aggravate();
548 } else if (youmonst.data->mlet == S_VAMPIRE)
549 return dopoly();
550 else if (Upolyd)
551 pline("Any special ability you may have is purely reflexive.");
552 else
553 You("don't have a special ability in your normal form!");
554 return 0;
558 enter_explore_mode(VOID_ARGS)
560 if (wizard) {
561 You("are in debug mode.");
562 } else if (discover) {
563 You("are already in explore mode.");
564 } else {
565 #ifdef SYSCF
566 #if defined(UNIX)
567 if (!sysopt.explorers || !sysopt.explorers[0]
568 || !check_user_string(sysopt.explorers)) {
569 You("cannot access explore mode.");
570 return 0;
572 #endif
573 #endif
574 pline(
575 "Beware! From explore mode there will be no return to normal game.");
576 if (paranoid_query(ParanoidQuit,
577 "Do you want to enter explore mode?")) {
578 clear_nhwindow(WIN_MESSAGE);
579 You("are now in non-scoring explore mode.");
580 discover = TRUE;
581 } else {
582 clear_nhwindow(WIN_MESSAGE);
583 pline("Resuming normal game.");
586 return 0;
589 /* ^W command - wish for something */
590 STATIC_PTR int
591 wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */
593 if (wizard) {
594 boolean save_verbose = flags.verbose;
596 flags.verbose = FALSE;
597 makewish();
598 flags.verbose = save_verbose;
599 (void) encumber_msg();
600 } else
601 pline("Unavailable command '%s'.",
602 visctrl((int) cmd_from_func(wiz_wish)));
603 return 0;
606 /* ^I command - reveal and optionally identify hero's inventory */
607 STATIC_PTR int
608 wiz_identify(VOID_ARGS)
610 if (wizard) {
611 iflags.override_ID = (int) cmd_from_func(wiz_identify);
612 if (display_inventory((char *) 0, TRUE) == -1)
613 identify_pack(0, FALSE);
614 iflags.override_ID = 0;
615 } else
616 pline("Unavailable command '%s'.",
617 visctrl((int) cmd_from_func(wiz_identify)));
618 return 0;
621 STATIC_PTR int
622 wiz_makemap(VOID_ARGS)
624 if (wizard) {
625 savelev(-1, ledger_no(&u.uz), FREE_SAVE);
626 mklev();
627 reglyph_darkroom();
628 vision_reset();
629 vision_full_recalc = 1;
630 (void) safe_teleds(TRUE);
631 docrt();
632 flush_screen(1);
634 return 0;
637 /* ^F command - reveal the level map and any traps on it */
638 STATIC_PTR int
639 wiz_map(VOID_ARGS)
641 if (wizard) {
642 struct trap *t;
643 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
645 HConfusion = HHallucination = 0L;
646 for (t = ftrap; t != 0; t = t->ntrap) {
647 t->tseen = 1;
648 map_trap(t, TRUE);
650 do_mapping();
651 HConfusion = save_Hconf;
652 HHallucination = save_Hhallu;
653 } else
654 pline("Unavailable command '%s'.",
655 visctrl((int) cmd_from_func(wiz_map)));
656 return 0;
659 /* ^G command - generate monster(s); a count prefix will be honored */
660 STATIC_PTR int
661 wiz_genesis(VOID_ARGS)
663 if (wizard)
664 (void) create_particular();
665 else
666 pline("Unavailable command '%s'.",
667 visctrl((int) cmd_from_func(wiz_genesis)));
668 return 0;
671 /* ^O command - display dungeon layout */
672 STATIC_PTR int
673 wiz_where(VOID_ARGS)
675 if (wizard)
676 (void) print_dungeon(FALSE, (schar *) 0, (xchar *) 0);
677 else
678 pline("Unavailable command '%s'.",
679 visctrl((int) cmd_from_func(wiz_where)));
680 return 0;
683 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
684 STATIC_PTR int
685 wiz_detect(VOID_ARGS)
687 if (wizard)
688 (void) findit();
689 else
690 pline("Unavailable command '%s'.",
691 visctrl((int) cmd_from_func(wiz_detect)));
692 return 0;
695 /* ^V command - level teleport */
696 STATIC_PTR int
697 wiz_level_tele(VOID_ARGS)
699 if (wizard)
700 level_tele();
701 else
702 pline("Unavailable command '%s'.",
703 visctrl((int) cmd_from_func(wiz_level_tele)));
704 return 0;
707 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
708 STATIC_PTR int
709 wiz_mon_polycontrol(VOID_ARGS)
711 iflags.mon_polycontrol = !iflags.mon_polycontrol;
712 pline("Monster polymorph control is %s.",
713 iflags.mon_polycontrol ? "on" : "off");
714 return 0;
717 /* #levelchange command - adjust hero's experience level */
718 STATIC_PTR int
719 wiz_level_change(VOID_ARGS)
721 char buf[BUFSZ];
722 int newlevel;
723 int ret;
725 getlin("To what experience level do you want to be set?", buf);
726 (void) mungspaces(buf);
727 if (buf[0] == '\033' || buf[0] == '\0')
728 ret = 0;
729 else
730 ret = sscanf(buf, "%d", &newlevel);
732 if (ret != 1) {
733 pline1(Never_mind);
734 return 0;
736 if (newlevel == u.ulevel) {
737 You("are already that experienced.");
738 } else if (newlevel < u.ulevel) {
739 if (u.ulevel == 1) {
740 You("are already as inexperienced as you can get.");
741 return 0;
743 if (newlevel < 1)
744 newlevel = 1;
745 while (u.ulevel > newlevel)
746 losexp("#levelchange");
747 } else {
748 if (u.ulevel >= MAXULEV) {
749 You("are already as experienced as you can get.");
750 return 0;
752 if (newlevel > MAXULEV)
753 newlevel = MAXULEV;
754 while (u.ulevel < newlevel)
755 pluslvl(FALSE);
757 u.ulevelmax = u.ulevel;
758 return 0;
761 /* #panic command - test program's panic handling */
762 STATIC_PTR int
763 wiz_panic(VOID_ARGS)
765 if (yn("Do you want to call panic() and end your game?") == 'y')
766 panic("Crash test.");
767 return 0;
770 /* #polyself command - change hero's form */
771 STATIC_PTR int
772 wiz_polyself(VOID_ARGS)
774 polyself(1);
775 return 0;
778 /* #seenv command */
779 STATIC_PTR int
780 wiz_show_seenv(VOID_ARGS)
782 winid win;
783 int x, y, v, startx, stopx, curx;
784 char row[COLNO + 1];
786 win = create_nhwindow(NHW_TEXT);
788 * Each seenv description takes up 2 characters, so center
789 * the seenv display around the hero.
791 startx = max(1, u.ux - (COLNO / 4));
792 stopx = min(startx + (COLNO / 2), COLNO);
793 /* can't have a line exactly 80 chars long */
794 if (stopx - startx == COLNO / 2)
795 startx++;
797 for (y = 0; y < ROWNO; y++) {
798 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
799 if (x == u.ux && y == u.uy) {
800 row[curx] = row[curx + 1] = '@';
801 } else {
802 v = levl[x][y].seenv & 0xff;
803 if (v == 0)
804 row[curx] = row[curx + 1] = ' ';
805 else
806 Sprintf(&row[curx], "%02x", v);
809 /* remove trailing spaces */
810 for (x = curx - 1; x >= 0; x--)
811 if (row[x] != ' ')
812 break;
813 row[x + 1] = '\0';
815 putstr(win, 0, row);
817 display_nhwindow(win, TRUE);
818 destroy_nhwindow(win);
819 return 0;
822 /* #vision command */
823 STATIC_PTR int
824 wiz_show_vision(VOID_ARGS)
826 winid win;
827 int x, y, v;
828 char row[COLNO + 1];
830 win = create_nhwindow(NHW_TEXT);
831 Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
832 COULD_SEE, IN_SIGHT, TEMP_LIT);
833 putstr(win, 0, row);
834 putstr(win, 0, "");
835 for (y = 0; y < ROWNO; y++) {
836 for (x = 1; x < COLNO; x++) {
837 if (x == u.ux && y == u.uy)
838 row[x] = '@';
839 else {
840 v = viz_array[y][x]; /* data access should be hidden */
841 if (v == 0)
842 row[x] = ' ';
843 else
844 row[x] = '0' + viz_array[y][x];
847 /* remove trailing spaces */
848 for (x = COLNO - 1; x >= 1; x--)
849 if (row[x] != ' ')
850 break;
851 row[x + 1] = '\0';
853 putstr(win, 0, &row[1]);
855 display_nhwindow(win, TRUE);
856 destroy_nhwindow(win);
857 return 0;
860 /* #wmode command */
861 STATIC_PTR int
862 wiz_show_wmodes(VOID_ARGS)
864 winid win;
865 int x, y;
866 char row[COLNO + 1];
867 struct rm *lev;
868 boolean istty = !strcmp(windowprocs.name, "tty");
870 win = create_nhwindow(NHW_TEXT);
871 if (istty)
872 putstr(win, 0, ""); /* tty only: blank top line */
873 for (y = 0; y < ROWNO; y++) {
874 for (x = 0; x < COLNO; x++) {
875 lev = &levl[x][y];
876 if (x == u.ux && y == u.uy)
877 row[x] = '@';
878 else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
879 row[x] = '0' + (lev->wall_info & WM_MASK);
880 else if (lev->typ == CORR)
881 row[x] = '#';
882 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
883 row[x] = '.';
884 else
885 row[x] = 'x';
887 row[COLNO] = '\0';
888 /* map column 0, levl[0][], is off the left edge of the screen */
889 putstr(win, 0, &row[1]);
891 display_nhwindow(win, TRUE);
892 destroy_nhwindow(win);
893 return 0;
896 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
897 STATIC_OVL void
898 wiz_map_levltyp(VOID_ARGS)
900 winid win;
901 int x, y, terrain;
902 char row[COLNO + 1];
903 boolean istty = !strcmp(windowprocs.name, "tty");
905 win = create_nhwindow(NHW_TEXT);
906 /* map row 0, levl[][0], is drawn on the second line of tty screen */
907 if (istty)
908 putstr(win, 0, ""); /* tty only: blank top line */
909 for (y = 0; y < ROWNO; y++) {
910 /* map column 0, levl[0][], is off the left edge of the screen;
911 it should always have terrain type "undiggable stone" */
912 for (x = 1; x < COLNO; x++) {
913 terrain = levl[x][y].typ;
914 /* assumes there aren't more than 10+26+26 terrain types */
915 row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y))
916 ? '*'
917 : (terrain < 10)
918 ? '0' + terrain
919 : (terrain < 36)
920 ? 'a' + terrain - 10
921 : 'A' + terrain - 36);
923 x--;
924 if (levl[0][y].typ != STONE || may_dig(0, y))
925 row[x++] = '!';
926 row[x] = '\0';
927 putstr(win, 0, row);
931 char dsc[BUFSZ];
932 s_level *slev = Is_special(&u.uz);
934 Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel);
935 /* [dungeon branch features currently omitted] */
936 /* special level features */
937 if (slev) {
938 Sprintf(eos(dsc), " \"%s\"", slev->proto);
939 /* special level flags (note: dungeon.def doesn't set `maze'
940 or `hell' for any specific levels so those never show up) */
941 if (slev->flags.maze_like)
942 Strcat(dsc, " mazelike");
943 if (slev->flags.hellish)
944 Strcat(dsc, " hellish");
945 if (slev->flags.town)
946 Strcat(dsc, " town");
947 if (slev->flags.rogue_like)
948 Strcat(dsc, " roguelike");
949 /* alignment currently omitted to save space */
951 /* level features */
952 if (level.flags.nfountains)
953 Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym,
954 (int) level.flags.nfountains);
955 if (level.flags.nsinks)
956 Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym,
957 (int) level.flags.nsinks);
958 if (level.flags.has_vault)
959 Strcat(dsc, " vault");
960 if (level.flags.has_shop)
961 Strcat(dsc, " shop");
962 if (level.flags.has_temple)
963 Strcat(dsc, " temple");
964 if (level.flags.has_court)
965 Strcat(dsc, " throne");
966 if (level.flags.has_zoo)
967 Strcat(dsc, " zoo");
968 if (level.flags.has_morgue)
969 Strcat(dsc, " morgue");
970 if (level.flags.has_barracks)
971 Strcat(dsc, " barracks");
972 if (level.flags.has_beehive)
973 Strcat(dsc, " hive");
974 if (level.flags.has_swamp)
975 Strcat(dsc, " swamp");
976 /* level flags */
977 if (level.flags.noteleport)
978 Strcat(dsc, " noTport");
979 if (level.flags.hardfloor)
980 Strcat(dsc, " noDig");
981 if (level.flags.nommap)
982 Strcat(dsc, " noMMap");
983 if (!level.flags.hero_memory)
984 Strcat(dsc, " noMem");
985 if (level.flags.shortsighted)
986 Strcat(dsc, " shortsight");
987 if (level.flags.graveyard)
988 Strcat(dsc, " graveyard");
989 if (level.flags.is_maze_lev)
990 Strcat(dsc, " maze");
991 if (level.flags.is_cavernous_lev)
992 Strcat(dsc, " cave");
993 if (level.flags.arboreal)
994 Strcat(dsc, " tree");
995 if (Sokoban)
996 Strcat(dsc, " sokoban-rules");
997 /* non-flag info; probably should include dungeon branching
998 checks (extra stairs and magic portals) here */
999 if (Invocation_lev(&u.uz))
1000 Strcat(dsc, " invoke");
1001 if (On_W_tower_level(&u.uz))
1002 Strcat(dsc, " tower");
1003 /* append a branch identifier for completeness' sake */
1004 if (u.uz.dnum == 0)
1005 Strcat(dsc, " dungeon");
1006 else if (u.uz.dnum == mines_dnum)
1007 Strcat(dsc, " mines");
1008 else if (In_sokoban(&u.uz))
1009 Strcat(dsc, " sokoban");
1010 else if (u.uz.dnum == quest_dnum)
1011 Strcat(dsc, " quest");
1012 else if (Is_knox(&u.uz))
1013 Strcat(dsc, " ludios");
1014 else if (u.uz.dnum == 1)
1015 Strcat(dsc, " gehennom");
1016 else if (u.uz.dnum == tower_dnum)
1017 Strcat(dsc, " vlad");
1018 else if (In_endgame(&u.uz))
1019 Strcat(dsc, " endgame");
1020 else {
1021 /* somebody's added a dungeon branch we're not expecting */
1022 const char *brname = dungeons[u.uz.dnum].dname;
1024 if (!brname || !*brname)
1025 brname = "unknown";
1026 if (!strncmpi(brname, "the ", 4))
1027 brname += 4;
1028 Sprintf(eos(dsc), " %s", brname);
1030 /* limit the line length to map width */
1031 if (strlen(dsc) >= COLNO)
1032 dsc[COLNO - 1] = '\0'; /* truncate */
1033 putstr(win, 0, dsc);
1036 display_nhwindow(win, TRUE);
1037 destroy_nhwindow(win);
1038 return;
1041 /* temporary? hack, since level type codes aren't the same as screen
1042 symbols and only the latter have easily accessible descriptions */
1043 static const char *levltyp[] = {
1044 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1045 "top-right corner wall", "bottom-left corner wall",
1046 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1047 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1048 "secret door", "secret corridor", "pool", "moat", "water",
1049 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1050 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1051 "drawbridge down", "air", "cloud",
1052 /* not a real terrain type, but used for undiggable stone
1053 by wiz_map_levltyp() */
1054 "unreachable/undiggable",
1055 /* padding in case the number of entries above is odd */
1059 /* explanation of base-36 output from wiz_map_levltyp() */
1060 STATIC_OVL void
1061 wiz_levltyp_legend(VOID_ARGS)
1063 winid win;
1064 int i, j, last, c;
1065 const char *dsc, *fmt;
1066 char buf[BUFSZ];
1068 win = create_nhwindow(NHW_TEXT);
1069 putstr(win, 0, "#terrain encodings:");
1070 putstr(win, 0, "");
1071 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1072 *buf = '\0';
1073 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1074 and right hand column holds [N/2],[N/2+1],...,[N-1];
1075 N ('last') will always be even, and may or may not include
1076 the empty string entry to pad out the final pair, depending
1077 upon how many other entries are present in levltyp[] */
1078 last = SIZE(levltyp) & ~1;
1079 for (i = 0; i < last / 2; ++i)
1080 for (j = i; j < last; j += last / 2) {
1081 dsc = levltyp[j];
1082 c = !*dsc ? ' '
1083 : !strncmp(dsc, "unreachable", 11) ? '*'
1084 /* same int-to-char conversion as wiz_map_levltyp() */
1085 : (j < 10) ? '0' + j
1086 : (j < 36) ? 'a' + j - 10
1087 : 'A' + j - 36;
1088 Sprintf(eos(buf), fmt, c, dsc);
1089 if (j > i) {
1090 putstr(win, 0, buf);
1091 *buf = '\0';
1094 display_nhwindow(win, TRUE);
1095 destroy_nhwindow(win);
1096 return;
1099 /* #wizsmell command - test usmellmon(). */
1100 STATIC_PTR int
1101 wiz_smell(VOID_ARGS)
1103 int ans = 0;
1104 int mndx; /* monster index */
1105 coord cc; /* screen pos of unknown glyph */
1106 int glyph; /* glyph at selected position */
1108 cc.x = u.ux;
1109 cc.y = u.uy;
1110 mndx = 0; /* gcc -Wall lint */
1111 if (!olfaction(youmonst.data)) {
1112 You("are incapable of detecting odors in your present form.");
1113 return 0;
1116 pline("You can move the cursor to a monster that you want to smell.");
1117 do {
1118 pline("Pick a monster to smell.");
1119 ans = getpos(&cc, TRUE, "a monster");
1120 if (ans < 0 || cc.x < 0) {
1121 return 0; /* done */
1123 /* Convert the glyph at the selected position to a mndxbol. */
1124 glyph = glyph_at(cc.x, cc.y);
1125 if (glyph_is_monster(glyph))
1126 mndx = glyph_to_mon(glyph);
1127 else
1128 mndx = 0;
1129 /* Is it a monster? */
1130 if (mndx) {
1131 if (!usmellmon(&mons[mndx]))
1132 pline("That monster seems to give off no smell.");
1133 } else
1134 pline("That is not a monster.");
1135 } while (TRUE);
1136 return 0;
1139 /* #wizinstrinsic command to set some intrinsics for testing */
1140 STATIC_PTR int
1141 wiz_intrinsic(VOID_ARGS)
1143 if (wizard) {
1144 winid win;
1145 anything any;
1146 int i, n, accelerator;
1147 menu_item *pick_list = (menu_item *) 0;
1149 static const char *const intrinsics[] = {
1150 "deafness",
1153 win = create_nhwindow(NHW_MENU);
1154 start_menu(win);
1155 accelerator = 0;
1157 for (i = 0; i < SIZE(intrinsics); ++i) {
1158 accelerator = intrinsics[i][0];
1159 any.a_int = i + 1;
1160 add_menu(win, NO_GLYPH, &any, accelerator, 0,
1161 ATR_NONE, intrinsics[i], FALSE);
1163 end_menu(win, "Which intrinsic?");
1164 n = select_menu(win, PICK_ONE, &pick_list);
1165 destroy_nhwindow(win);
1167 if (n >= 1) {
1168 i = pick_list[0].item.a_int-1;
1169 free((genericptr_t) pick_list);
1170 } else {
1171 return 0;
1174 if (!strcmp(intrinsics[i], "deafness")) {
1175 You("go deaf.");
1176 incr_itimeout(&HDeaf, 30);
1177 context.botl = TRUE;
1179 } else
1180 pline("Unavailable command '%s'.",
1181 visctrl((int) cmd_from_func(wiz_intrinsic)));
1182 return 0;
1185 /* #wizrumorcheck command - verify each rumor access */
1186 STATIC_PTR int
1187 wiz_rumor_check(VOID_ARGS)
1189 rumor_check();
1190 return 0;
1193 /* #terrain command -- show known map, inspired by crawl's '|' command */
1194 STATIC_PTR int
1195 doterrain(VOID_ARGS)
1197 winid men;
1198 menu_item *sel;
1199 anything any;
1200 int n;
1201 int which;
1204 * normal play: choose between known map without mons, obj, and traps
1205 * (to see underlying terrain only), or
1206 * known map without mons and objs (to see traps under mons and objs), or
1207 * known map without mons (to see objects under monsters);
1208 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1209 * wizard mode: normal and explore choices plus
1210 * a dump of the internal levl[][].typ codes w/ level flags, or
1211 * a legend for the levl[][].typ codes dump
1213 men = create_nhwindow(NHW_MENU);
1214 start_menu(men);
1215 any = zeroany;
1216 any.a_int = 1;
1217 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1218 "known map without monsters, objects, and traps",
1219 MENU_SELECTED);
1220 any.a_int = 2;
1221 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1222 "known map without monsters and objects",
1223 MENU_UNSELECTED);
1224 any.a_int = 3;
1225 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1226 "known map without monsters",
1227 MENU_UNSELECTED);
1228 if (discover || wizard) {
1229 any.a_int = 4;
1230 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1231 "full map without monsters, objects, and traps",
1232 MENU_UNSELECTED);
1233 if (wizard) {
1234 any.a_int = 5;
1235 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1236 "internal levl[][].typ codes in base-36",
1237 MENU_UNSELECTED);
1238 any.a_int = 6;
1239 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1240 "legend of base-36 levl[][].typ codes",
1241 MENU_UNSELECTED);
1244 end_menu(men, "View which?");
1246 n = select_menu(men, PICK_ONE, &sel);
1247 destroy_nhwindow(men);
1249 * n < 0: player used ESC to cancel;
1250 * n == 0: preselected entry was explicitly chosen and got toggled off;
1251 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1252 * n == 2: another entry was explicitly chosen, so skip preselected one.
1254 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1255 if (n > 1 && which == 1)
1256 which = sel[1].item.a_int;
1257 if (n > 0)
1258 free((genericptr_t) sel);
1260 switch (which) {
1261 case 1: /* known map */
1262 reveal_terrain(0, TER_MAP);
1263 break;
1264 case 2: /* known map with known traps */
1265 reveal_terrain(0, TER_MAP | TER_TRP);
1266 break;
1267 case 3: /* known map with known traps and objects */
1268 reveal_terrain(0, TER_MAP | TER_TRP | TER_OBJ);
1269 break;
1270 case 4: /* full map */
1271 reveal_terrain(1, TER_MAP);
1272 break;
1273 case 5: /* map internals */
1274 wiz_map_levltyp();
1275 break;
1276 case 6: /* internal details */
1277 wiz_levltyp_legend();
1278 break;
1279 default:
1280 break;
1282 return 0; /* no time elapses */
1285 /* -enlightenment and conduct- */
1286 static winid en_win = WIN_ERR;
1287 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1288 have[] = "have ", had[] = "had ", can[] = "can ",
1289 could[] = "could ";
1290 static const char have_been[] = "have been ", have_never[] = "have never ",
1291 never[] = "never ";
1293 #define enl_msg(prefix, present, past, suffix, ps) \
1294 enlght_line(prefix, final ? past : present, suffix, ps)
1295 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1296 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1297 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1298 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1299 #define you_have_never(badthing) \
1300 enl_msg(You_, have_never, never, badthing, "")
1301 #define you_have_X(something) \
1302 enl_msg(You_, have, (const char *) "", something, "")
1304 static void
1305 enlght_line(start, middle, end, ps)
1306 const char *start, *middle, *end, *ps;
1308 char buf[BUFSZ];
1310 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1311 putstr(en_win, 0, buf);
1314 /* format increased chance to hit or damage or defense (Protection) */
1315 static char *
1316 enlght_combatinc(inctyp, incamt, final, outbuf)
1317 const char *inctyp;
1318 int incamt, final;
1319 char *outbuf;
1321 const char *modif, *bonus;
1322 boolean invrt;
1323 int absamt;
1325 absamt = abs(incamt);
1326 /* Protection amount is typically larger than damage or to-hit;
1327 reduce magnitude by a third in order to stretch modifier ranges
1328 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1329 if (!strcmp(inctyp, "defense"))
1330 absamt = (absamt * 2) / 3;
1332 if (absamt <= 3)
1333 modif = "small";
1334 else if (absamt <= 6)
1335 modif = "moderate";
1336 else if (absamt <= 12)
1337 modif = "large";
1338 else
1339 modif = "huge";
1341 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1342 bonus = (incamt >= 0) ? "bonus" : "penalty";
1343 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1344 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1346 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1347 invrt ? bonus : inctyp);
1348 if (final || wizard)
1349 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1351 return outbuf;
1354 /* report half physical or half spell damage */
1355 STATIC_OVL void
1356 enlght_halfdmg(category, final)
1357 int category;
1358 int final;
1360 const char *category_name;
1361 char buf[BUFSZ];
1363 switch (category) {
1364 case HALF_PHDAM:
1365 category_name = "physical";
1366 break;
1367 case HALF_SPDAM:
1368 category_name = "spell";
1369 break;
1370 default:
1371 category_name = "unknown";
1372 break;
1374 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1375 category_name);
1376 enl_msg(You_, "take", "took", buf, from_what(category));
1379 /* is hero actively using water walking capability on water (or lava)? */
1380 STATIC_OVL boolean
1381 walking_on_water()
1383 if (u.uinwater || Levitation || Flying)
1384 return FALSE;
1385 return (boolean) (Wwalking
1386 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1389 /* check whether hero is wearing something that player definitely knows
1390 confers the target property; item must have been seen and its type
1391 discovered but it doesn't necessarily have to be fully identified */
1392 STATIC_OVL boolean
1393 cause_known(propindx)
1394 int propindx; /* index of a property which can be conveyed by worn item */
1396 register struct obj *o;
1397 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1399 /* simpler than from_what()/what_gives(); we don't attempt to
1400 handle artifacts and we deliberately ignore wielded items */
1401 for (o = invent; o; o = o->nobj) {
1402 if (!(o->owornmask & mask))
1403 continue;
1404 if ((int) objects[o->otyp].oc_oprop == propindx
1405 && objects[o->otyp].oc_name_known && o->dknown)
1406 return TRUE;
1408 return FALSE;
1411 /* format a characteristic value, accommodating Strength's strangeness */
1412 STATIC_OVL char *
1413 attrval(attrindx, attrvalue, resultbuf)
1414 int attrindx, attrvalue;
1415 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1417 if (attrindx != A_STR || attrvalue <= 18)
1418 Sprintf(resultbuf, "%d", attrvalue);
1419 else if (attrvalue > STR18(100)) /* 19 to 25 */
1420 Sprintf(resultbuf, "%d", attrvalue - 100);
1421 else /* simplify "18/ **" to be "18/100" */
1422 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1423 return resultbuf;
1426 void
1427 enlightenment(mode, final)
1428 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1429 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1431 char buf[BUFSZ], tmpbuf[BUFSZ];
1433 Strcpy(tmpbuf, plname);
1434 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1435 /* as in background_enlightenment, when poly'd we need to use the saved
1436 gender in u.mfemale rather than the current you-as-monster gender */
1437 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1438 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1439 ? urole.name.f
1440 : urole.name.m);
1442 en_win = create_nhwindow(NHW_MENU);
1443 /* title */
1444 putstr(en_win, 0, buf); /* "Conan the Archeologist's attributes:" */
1445 /* background and characteristics; ^X or end-of-game disclosure */
1446 if (mode & BASICENLIGHTENMENT) {
1447 /* role, race, alignment, deities */
1448 background_enlightenment(mode, final);
1449 /* strength, dexterity, &c */
1450 characteristics_enlightenment(mode, final);
1452 /* expanded status line information, including things which aren't
1453 included there due to space considerations--such as obvious
1454 alternative movement indicators (riding, levitation, &c), and
1455 various troubles (turning to stone, trapped, confusion, &c);
1456 shown for both basic and magic enlightenment */
1457 status_enlightenment(mode, final);
1458 /* remaining attributes; shown for potion,&c or wizard mode and
1459 explore mode ^X or end of game disclosure */
1460 if (mode & MAGICENLIGHTENMENT) {
1461 /* intrinsics and other traditional enlightenment feedback */
1462 attributes_enlightenment(mode, final);
1464 display_nhwindow(en_win, TRUE);
1465 destroy_nhwindow(en_win);
1466 en_win = WIN_ERR;
1469 /*ARGSUSED*/
1470 /* display role, race, alignment and such to en_win */
1471 STATIC_OVL void
1472 background_enlightenment(unused_mode, final)
1473 int unused_mode UNUSED;
1474 int final;
1476 const char *role_titl, *rank_titl;
1477 int innategend, difgend, difalgn;
1478 char buf[BUFSZ], tmpbuf[BUFSZ];
1480 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1481 to access hero's saved gender-as-human/elf/&c rather than current one */
1482 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1483 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1484 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1486 putstr(en_win, 0, ""); /* separator after title */
1487 putstr(en_win, 0, "Background:");
1489 /* if polymorphed, report current shape before underlying role;
1490 will be repeated as first status: "you are transformed" and also
1491 among various attributes: "you are in beast form" (after being
1492 told about lycanthropy) or "you are polymorphed into <a foo>"
1493 (with countdown timer appended for wizard mode); we really want
1494 the player to know he's not a samurai at the moment... */
1495 if (Upolyd) {
1496 struct permonst *uasmon = youmonst.data;
1498 tmpbuf[0] = '\0';
1499 /* here we always use current gender, not saved role gender */
1500 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1501 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1502 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1503 uasmon->mname);
1504 you_are(buf, "");
1507 /* report role; omit gender if it's redundant (eg, "female priestess") */
1508 tmpbuf[0] = '\0';
1509 if (!urole.name.f
1510 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1511 || innategend != flags.initgend))
1512 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1513 buf[0] = '\0';
1514 if (Upolyd)
1515 Strcpy(buf, "actually "); /* "You are actually a ..." */
1516 if (!strcmpi(rank_titl, role_titl)) {
1517 /* omit role when rank title matches it */
1518 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1519 tmpbuf, urace.noun);
1520 } else {
1521 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1522 tmpbuf, urace.adj, role_titl);
1524 you_are(buf, "");
1526 /* report alignment (bypass you_are() in order to omit ending period);
1527 adverb is used to distinguish between temporary change (helm of opp.
1528 alignment), permanent change (one-time conversion), and original */
1529 Sprintf(buf, " %s%s%s, %son a mission for %s",
1530 You_, !final ? are : were,
1531 align_str(u.ualign.type),
1532 /* helm of opposite alignment (might hide conversion) */
1533 (u.ualign.type != u.ualignbase[A_CURRENT])
1534 /* what's the past tense of "currently"? if we used "formerly"
1535 it would sound like a reference to the original alignment */
1536 ? (!final ? "currently " : "temporarily ")
1537 /* permanent conversion */
1538 : (u.ualign.type != u.ualignbase[A_ORIGINAL])
1539 /* and what's the past tense of "now"? certainly not "then"
1540 in a context like this...; "belatedly" == weren't that
1541 way sooner (in other words, didn't start that way) */
1542 ? (!final ? "now " : "belatedly ")
1543 /* atheist (ignored in very early game) */
1544 : (!u.uconduct.gnostic && moves > 1000L)
1545 ? "nominally "
1546 /* lastly, normal case */
1547 : "",
1548 u_gname());
1549 putstr(en_win, 0, buf);
1550 /* show the rest of this game's pantheon (finishes previous sentence)
1551 [appending "also Moloch" at the end would allow for straightforward
1552 trailing "and" on all three aligned entries but looks too verbose] */
1553 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1554 if (u.ualign.type != A_LAWFUL)
1555 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1556 align_str(A_LAWFUL));
1557 if (u.ualign.type != A_NEUTRAL)
1558 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1559 align_str(A_NEUTRAL),
1560 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1561 if (u.ualign.type != A_CHAOTIC)
1562 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1563 align_str(A_CHAOTIC));
1564 Strcat(buf, "."); /* terminate sentence */
1565 putstr(en_win, 0, buf);
1567 /* show original alignment,gender,race,role if any have been changed;
1568 giving separate message for temporary alignment change bypasses need
1569 for tricky phrasing otherwise necessitated by possibility of having
1570 helm of opposite alignment mask a permanent alignment conversion */
1571 difgend = (innategend != flags.initgend);
1572 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1573 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1574 ? 2 : 0));
1575 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1576 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1577 you_are(buf, "");
1578 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1580 if (difgend || difalgn) { /* sex change or perm align change or both */
1581 Sprintf(buf, " You started out %s%s%s.",
1582 difgend ? genders[flags.initgend].adj : "",
1583 (difgend && difalgn) ? " and " : "",
1584 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1585 putstr(en_win, 0, buf);
1589 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1590 STATIC_OVL void
1591 characteristics_enlightenment(mode, final)
1592 int mode;
1593 int final;
1595 char buf[BUFSZ];
1596 int hp = Upolyd ? u.mh : u.uhp;
1597 int hpmax = Upolyd ? u.mhmax : u.uhpmax;
1599 putstr(en_win, 0, ""); /* separator after background */
1600 putstr(en_win, 0,
1601 final ? "Final Characteristics:" : "Current Characteristics:");
1603 if (hp < 0)
1604 hp = 0;
1605 Sprintf(buf, "%d hit points (max:%d)", hp, hpmax);
1606 you_have(buf, "");
1608 Sprintf(buf, "%d magic power (max:%d)", u.uen, u.uenmax);
1609 you_have(buf, "");
1611 Sprintf(buf, "%d", u.uac);
1612 enl_msg("Your armor class ", "is ", "was ", buf, "");
1614 if (Upolyd) {
1615 Sprintf(buf, "%d hit dice", mons[u.umonnum].mlevel);
1616 } else {
1617 /* flags.showexp does not matter */
1618 /* experience level is already shown in the Background section */
1619 Sprintf(buf, "%-1ld experience point%s",
1620 u.uexp, u.uexp == 1 ? "" : "s");
1622 you_have(buf, "");
1624 Sprintf(buf, " You entered the dungeon %ld turn%s ago",
1625 moves, moves == 1 ? "" : "s");
1626 putstr(en_win, 0, buf);
1628 #ifdef SCORE_ON_BOTL
1629 Sprintf(buf, "%ld", botl_score());
1630 enl_msg("Your score ", "is ", "was ", buf, "");
1631 #endif
1633 /* bottom line order */
1634 one_characteristic(mode, final, A_STR); /* strength */
1635 one_characteristic(mode, final, A_DEX); /* dexterity */
1636 one_characteristic(mode, final, A_CON); /* constitution */
1637 one_characteristic(mode, final, A_INT); /* intelligence */
1638 one_characteristic(mode, final, A_WIS); /* wisdom */
1639 one_characteristic(mode, final, A_CHA); /* charisma */
1642 /* display one attribute value for characteristics_enlightenment() */
1643 STATIC_OVL void
1644 one_characteristic(mode, final, attrindx)
1645 int mode, final, attrindx;
1647 boolean hide_innate_value = FALSE, interesting_alimit;
1648 int acurrent, abase, apeak, alimit;
1649 const char *attrname, *paren_pfx;
1650 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
1652 /* being polymorphed or wearing certain cursed items prevents
1653 hero from reliably tracking changes to characteristics so
1654 we don't show base & peak values then; when the items aren't
1655 cursed, hero could take them off to check underlying values
1656 and we show those in such case so that player doesn't need
1657 to actually resort to doing that */
1658 if (Upolyd) {
1659 hide_innate_value = TRUE;
1660 } else if (Fixed_abil) {
1661 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
1662 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
1663 hide_innate_value = TRUE;
1665 switch (attrindx) {
1666 case A_STR:
1667 attrname = "strength";
1668 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
1669 hide_innate_value = TRUE;
1670 break;
1671 case A_DEX:
1672 attrname = "dexterity";
1673 break;
1674 case A_CON:
1675 attrname = "constitution";
1676 if (uwep && uwep->oartifact == ART_OGRESMASHER && uwep->cursed)
1677 hide_innate_value = TRUE;
1678 break;
1679 case A_INT:
1680 attrname = "intelligence";
1681 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1682 hide_innate_value = TRUE;
1683 break;
1684 case A_WIS:
1685 attrname = "wisdom";
1686 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1687 hide_innate_value = TRUE;
1688 break;
1689 case A_CHA:
1690 attrname = "charisma";
1691 break;
1692 default:
1693 return; /* impossible */
1695 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1696 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
1697 hide_innate_value = FALSE;
1699 acurrent = ACURR(attrindx);
1700 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
1701 Sprintf(subjbuf, "Your %s ", attrname);
1703 if (!hide_innate_value) {
1704 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1705 (a magic bonus or penalty is in effect) or abase doesn't match
1706 amax (some points have been lost to poison or exercise abuse
1707 and are restorable) or attrmax is different from normal human
1708 (while game is in progress; trying to reduce dependency on
1709 spoilers to keep track of such stuff) or attrmax was different
1710 from abase (at end of game; this attribute wasn't maxed out) */
1711 abase = ABASE(attrindx);
1712 apeak = AMAX(attrindx);
1713 alimit = ATTRMAX(attrindx);
1714 /* criterium for whether the limit is interesting varies */
1715 interesting_alimit =
1716 final ? TRUE /* was originally `(abase != alimit)' */
1717 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
1718 paren_pfx = final ? " (" : " (current; ";
1719 if (acurrent != abase) {
1720 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
1721 attrval(attrindx, abase, valstring));
1722 paren_pfx = ", ";
1724 if (abase != apeak) {
1725 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
1726 attrval(attrindx, apeak, valstring));
1727 paren_pfx = ", ";
1729 if (interesting_alimit) {
1730 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
1731 /* more verbose if exceeding 'limit' due to magic bonus */
1732 (acurrent > alimit) ? "innate " : "",
1733 attrval(attrindx, alimit, valstring));
1734 /* paren_pfx = ", "; */
1736 if (acurrent != abase || abase != apeak || interesting_alimit)
1737 Strcat(valubuf, ")");
1739 enl_msg(subjbuf, "is ", "was ", valubuf, "");
1742 /* status: selected obvious capabilities, assorted troubles */
1743 STATIC_OVL void
1744 status_enlightenment(mode, final)
1745 int mode;
1746 int final;
1748 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
1749 int cap;
1750 char buf[BUFSZ], youtoo[BUFSZ];
1751 boolean Riding = (u.usteed
1752 /* if hero dies while dismounting, u.usteed will still
1753 be set; we want to ignore steed in that situation */
1754 && !(final == ENL_GAMEOVERDEAD
1755 && !strcmp(killer.name, "riding accident")));
1756 const char *steedname = (!Riding ? (char *) 0
1757 : x_monnam(u.usteed,
1758 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
1759 (char *) 0,
1760 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
1761 FALSE));
1764 * Status (many are abbreviated on bottom line; others are or
1765 * should be discernible to the hero hence to the player)
1767 putstr(en_win, 0, ""); /* separator after title or characteristics */
1768 putstr(en_win, 0, final ? "Final Status:" : "Current Status:");
1770 Strcpy(youtoo, You_);
1771 /* not a traditional status but inherently obvious to player; more
1772 detail given below (attributes section) for magic enlightenment */
1773 if (Upolyd)
1774 you_are("transformed", "");
1775 /* not a trouble, but we want to display riding status before maybe
1776 reporting steed as trapped or hero stuck to cursed saddle */
1777 if (Riding) {
1778 Sprintf(buf, "riding %s", steedname);
1779 you_are(buf, "");
1780 Sprintf(eos(youtoo), "and %s ", steedname);
1782 /* other movement situations that hero should always know */
1783 if (Levitation) {
1784 if (Lev_at_will && magic)
1785 you_are("levitating, at will", "");
1786 else
1787 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
1788 } else if (Flying) { /* can only fly when not levitating */
1789 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
1791 if (Underwater) {
1792 you_are("underwater", "");
1793 } else if (u.uinwater) {
1794 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
1795 } else if (walking_on_water()) {
1796 /* show active Wwalking here, potential Wwalking elsewhere */
1797 Sprintf(buf, "walking on %s",
1798 is_pool(u.ux, u.uy) ? "water"
1799 : is_lava(u.ux, u.uy) ? "lava"
1800 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
1801 you_are(buf, from_what(WWALKING));
1803 if (Upolyd && (u.uundetected || youmonst.m_ap_type != M_AP_NOTHING))
1804 youhiding(TRUE, final);
1806 /* internal troubles, mostly in the order that prayer ranks them */
1807 if (Stoned)
1808 you_are("turning to stone", "");
1809 if (Slimed)
1810 you_are("turning into slime", "");
1811 if (Strangled) {
1812 if (u.uburied) {
1813 you_are("buried", "");
1814 } else {
1815 Strcpy(buf, "being strangled");
1816 if (wizard)
1817 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
1818 you_are(buf, from_what(STRANGLED));
1821 if (Sick) {
1822 /* prayer lumps these together; botl puts Ill before FoodPois */
1823 if (u.usick_type & SICK_NONVOMITABLE)
1824 you_are("terminally sick from illness", "");
1825 if (u.usick_type & SICK_VOMITABLE)
1826 you_are("terminally sick from food poisoning", "");
1828 if (Vomiting)
1829 you_are("nauseated", "");
1830 if (Stunned)
1831 you_are("stunned", "");
1832 if (Confusion)
1833 you_are("confused", "");
1834 if (Hallucination)
1835 you_are("hallucinating", "");
1836 if (Blind) {
1837 /* from_what() (currently wizard-mode only) checks !haseyes()
1838 before u.uroleplay.blind, so we should too */
1839 Sprintf(buf, "%s blind",
1840 !haseyes(youmonst.data) ? "innately"
1841 : u.uroleplay.blind ? "permanently"
1842 /* better phrasing desperately wanted... */
1843 : Blindfolded_only ? "deliberately"
1844 : "temporarily");
1845 if (wizard && (Blinded & TIMEOUT) != 0L
1846 && !u.uroleplay.blind && haseyes(youmonst.data))
1847 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
1848 /* !haseyes: avoid "you are innately blind innately" */
1849 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
1851 if (Deaf)
1852 you_are("deaf", from_what(DEAF));
1854 /* external troubles, more or less */
1855 if (Punished) {
1856 if (uball) {
1857 Sprintf(buf, "chained to %s", ansimpleoname(uball));
1858 } else {
1859 impossible("Punished without uball?");
1860 Strcpy(buf, "punished");
1862 you_are(buf, "");
1864 if (u.utrap) {
1865 char predicament[BUFSZ];
1866 struct trap *t;
1867 boolean anchored = (u.utraptype == TT_BURIEDBALL);
1869 if (anchored) {
1870 Strcpy(predicament, "tethered to something buried");
1871 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
1872 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
1873 } else {
1874 Strcpy(predicament, "trapped");
1875 if ((t = t_at(u.ux, u.uy)) != 0)
1876 Sprintf(eos(predicament), " in %s",
1877 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
1879 if (u.usteed) { /* not `Riding' here */
1880 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
1881 *buf = highc(*buf);
1882 enl_msg(buf, (anchored ? "are " : "is "),
1883 (anchored ? "were " : "was "), predicament, "");
1884 } else
1885 you_are(predicament, "");
1886 } /* (u.utrap) */
1887 if (u.uswallow) {
1888 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
1889 if (wizard)
1890 Sprintf(eos(buf), " (%u)", u.uswldtim);
1891 you_are(buf, "");
1892 } else if (u.ustuck) {
1893 Sprintf(buf, "%s %s",
1894 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
1895 a_monnam(u.ustuck));
1896 you_are(buf, "");
1898 if (Riding) {
1899 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
1901 if (saddle && saddle->cursed) {
1902 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
1903 simpleonames(saddle));
1904 you_are(buf, "");
1907 if (Wounded_legs) {
1908 /* when mounted, Wounded_legs applies to steed rather than to
1909 hero; we only report steed's wounded legs in wizard mode */
1910 if (u.usteed) { /* not `Riding' here */
1911 if (wizard && steedname) {
1912 Strcpy(buf, steedname);
1913 *buf = highc(*buf);
1914 enl_msg(buf, " has", " had", " wounded legs", "");
1916 } else {
1917 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
1918 you_have(buf, "");
1921 if (Glib) {
1922 Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
1923 you_have(buf, "");
1925 if (Fumbling) {
1926 if (magic || cause_known(FUMBLING))
1927 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
1929 if (Sleepy) {
1930 if (magic || cause_known(SLEEPY)) {
1931 Strcpy(buf, from_what(SLEEPY));
1932 if (wizard)
1933 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
1934 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
1937 /* hunger/nutrition */
1938 if (Hunger) {
1939 if (magic || cause_known(HUNGER))
1940 enl_msg(You_, "hunger", "hungered", " rapidly",
1941 from_what(HUNGER));
1943 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
1944 mungspaces(buf); /* strip trailing spaces */
1945 if (*buf) {
1946 *buf = lowc(*buf); /* override capitalization */
1947 if (!strcmp(buf, "weak"))
1948 Strcat(buf, " from severe hunger");
1949 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
1950 Strcat(buf, " due to starvation");
1951 you_are(buf, "");
1953 /* encumbrance */
1954 if ((cap = near_capacity()) > UNENCUMBERED) {
1955 const char *adj = "?_?"; /* (should always get overridden) */
1957 Strcpy(buf, enc_stat[cap]);
1958 *buf = lowc(*buf);
1959 switch (cap) {
1960 case SLT_ENCUMBER:
1961 adj = "slightly";
1962 break; /* burdened */
1963 case MOD_ENCUMBER:
1964 adj = "moderately";
1965 break; /* stressed */
1966 case HVY_ENCUMBER:
1967 adj = "very";
1968 break; /* strained */
1969 case EXT_ENCUMBER:
1970 adj = "extremely";
1971 break; /* overtaxed */
1972 case OVERLOADED:
1973 adj = "not possible";
1974 break;
1976 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
1977 (cap < OVERLOADED) ? " slowed" : "");
1978 you_are(buf, "");
1979 } else {
1980 /* last resort entry, guarantees Status section is non-empty
1981 (no longer needed for that purpose since weapon status added;
1982 still useful though) */
1983 you_are("unencumbered", "");
1985 /* report being weaponless; distinguish whether gloves are worn */
1986 if (!uwep) {
1987 you_are(uarmg ? "empty handed" /* gloves imply hands */
1988 : humanoid(youmonst.data)
1989 /* hands but no weapon and no gloves */
1990 ? "bare handed"
1991 /* alternate phrasing for paws or lack of hands */
1992 : "not wielding anything",
1993 "");
1994 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1995 } else if (u.twoweap) {
1996 you_are("wielding two weapons at once", "");
1997 /* report most weapons by their skill class (so a katana will be
1998 described as a long sword, for instance; mattock and hook are
1999 exceptions), or wielded non-weapon item by its object class */
2000 } else {
2001 const char *what = weapon_descr(uwep);
2003 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
2004 || !strcmpi(what, "venom"))
2005 Sprintf(buf, "wielding some %s", what);
2006 else
2007 Sprintf(buf, "wielding %s",
2008 (uwep->quan == 1L) ? an(what) : makeplural(what));
2009 you_are(buf, "");
2011 /* report 'nudity' */
2012 if (!uarm && !uarmu && !uarmc && !uarmg && !uarmf && !uarmh) {
2013 if (u.uroleplay.nudist)
2014 enl_msg(You_, "do", "did", " not wear any armor", "");
2015 else
2016 you_are("not wearing any armor", "");
2020 /* attributes: intrinsics and the like, other non-obvious capabilities */
2021 void
2022 attributes_enlightenment(unused_mode, final)
2023 int unused_mode UNUSED;
2024 int final;
2026 static NEARDATA const char if_surroundings_permitted[] =
2027 " if surroundings permitted";
2028 int ltmp, armpro;
2029 char buf[BUFSZ];
2032 * Attributes
2034 putstr(en_win, 0, "");
2035 putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
2037 if (u.uevent.uhand_of_elbereth) {
2038 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
2039 "the Envoy of Balance",
2040 "the Glory of Arioch" };
2041 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
2044 Sprintf(buf, "%s", piousness(TRUE, "aligned"));
2045 if (u.ualign.record >= 0)
2046 you_are(buf, "");
2047 else
2048 you_have(buf, "");
2050 if (wizard) {
2051 Sprintf(buf, " %d", u.ualign.record);
2052 enl_msg("Your alignment ", "is", "was", buf, "");
2055 /*** Resistances to troubles ***/
2056 if (Invulnerable)
2057 you_are("invulnerable", from_what(INVULNERABLE));
2058 if (Antimagic)
2059 you_are("magic-protected", from_what(ANTIMAGIC));
2060 if (Fire_resistance)
2061 you_are("fire resistant", from_what(FIRE_RES));
2062 if (Cold_resistance)
2063 you_are("cold resistant", from_what(COLD_RES));
2064 if (Sleep_resistance)
2065 you_are("sleep resistant", from_what(SLEEP_RES));
2066 if (Disint_resistance)
2067 you_are("disintegration-resistant", from_what(DISINT_RES));
2068 if (Shock_resistance)
2069 you_are("shock resistant", from_what(SHOCK_RES));
2070 if (Poison_resistance)
2071 you_are("poison resistant", from_what(POISON_RES));
2072 if (Acid_resistance)
2073 you_are("acid resistant", from_what(ACID_RES));
2074 if (Drain_resistance)
2075 you_are("level-drain resistant", from_what(DRAIN_RES));
2076 if (Sick_resistance)
2077 you_are("immune to sickness", from_what(SICK_RES));
2078 if (Stone_resistance)
2079 you_are("petrification resistant", from_what(STONE_RES));
2080 if (Halluc_resistance)
2081 enl_msg(You_, "resist", "resisted", " hallucinations",
2082 from_what(HALLUC_RES));
2083 if (u.uedibility)
2084 you_can("recognize detrimental food", "");
2086 /*** Vision and senses ***/
2087 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2088 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2089 if (See_invisible) {
2090 if (!Blind)
2091 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2092 else
2093 enl_msg(You_, "will see", "would have seen",
2094 " invisible when not blind", from_what(SEE_INVIS));
2096 if (Blind_telepat)
2097 you_are("telepathic", from_what(TELEPAT));
2098 if (Warning)
2099 you_are("warned", from_what(WARNING));
2100 if (Warn_of_mon && context.warntype.obj) {
2101 Sprintf(buf, "aware of the presence of %s",
2102 (context.warntype.obj & M2_ORC) ? "orcs"
2103 : (context.warntype.obj & M2_ELF) ? "elves"
2104 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2105 you_are(buf, from_what(WARN_OF_MON));
2107 if (Warn_of_mon && context.warntype.polyd) {
2108 Sprintf(buf, "aware of the presence of %s",
2109 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2110 == (M2_HUMAN | M2_ELF))
2111 ? "humans and elves"
2112 : (context.warntype.polyd & M2_HUMAN)
2113 ? "humans"
2114 : (context.warntype.polyd & M2_ELF)
2115 ? "elves"
2116 : (context.warntype.polyd & M2_ORC)
2117 ? "orcs"
2118 : (context.warntype.polyd & M2_DEMON)
2119 ? "demons"
2120 : "certain monsters");
2121 you_are(buf, "");
2123 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2124 Sprintf(buf, "aware of the presence of %s",
2125 makeplural(mons[context.warntype.speciesidx].mname));
2126 you_are(buf, from_what(WARN_OF_MON));
2128 if (Undead_warning)
2129 you_are("warned of undead", from_what(WARN_UNDEAD));
2130 if (Searching)
2131 you_have("automatic searching", from_what(SEARCHING));
2132 if (Clairvoyant)
2133 you_are("clairvoyant", from_what(CLAIRVOYANT));
2134 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2135 Strcpy(buf, from_what(-CLAIRVOYANT));
2136 if (!strncmp(buf, " because of ", 12))
2137 /* overwrite substring; strncpy doesn't add terminator */
2138 (void) strncpy(buf, " if not for ", 12);
2139 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2141 if (Infravision)
2142 you_have("infravision", from_what(INFRAVISION));
2143 if (Detect_monsters)
2144 you_are("sensing the presence of monsters", "");
2145 if (u.umconf)
2146 you_are("going to confuse monsters", "");
2148 /*** Appearance and behavior ***/
2149 if (Adornment) {
2150 int adorn = 0;
2152 if (uleft && uleft->otyp == RIN_ADORNMENT)
2153 adorn += uleft->spe;
2154 if (uright && uright->otyp == RIN_ADORNMENT)
2155 adorn += uright->spe;
2156 /* the sum might be 0 (+0 ring or two which negate each other);
2157 that yields "you are charismatic" (which isn't pointless
2158 because it potentially impacts seduction attacks) */
2159 Sprintf(buf, "%scharismatic",
2160 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2161 you_are(buf, from_what(ADORNED));
2163 if (Invisible)
2164 you_are("invisible", from_what(INVIS));
2165 else if (Invis)
2166 you_are("invisible to others", from_what(INVIS));
2167 /* ordinarily "visible" is redundant; this is a special case for
2168 the situation when invisibility would be an expected attribute */
2169 else if ((HInvis || EInvis) && BInvis)
2170 you_are("visible", from_what(-INVIS));
2171 if (Displaced)
2172 you_are("displaced", from_what(DISPLACED));
2173 if (Stealth)
2174 you_are("stealthy", from_what(STEALTH));
2175 if (Aggravate_monster)
2176 enl_msg("You aggravate", "", "d", " monsters",
2177 from_what(AGGRAVATE_MONSTER));
2178 if (Conflict)
2179 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2181 /*** Transportation ***/
2182 if (Jumping)
2183 you_can("jump", from_what(JUMPING));
2184 if (Teleportation)
2185 you_can("teleport", from_what(TELEPORT));
2186 if (Teleport_control)
2187 you_have("teleport control", from_what(TELEPORT_CONTROL));
2188 /* actively levitating handled earlier as a status condition */
2189 if (BLevitation) { /* levitation is blocked */
2190 long save_BLev = BLevitation;
2192 BLevitation = 0L;
2193 if (Levitation)
2194 enl_msg(You_, "would levitate", "would have levitated",
2195 if_surroundings_permitted, "");
2196 BLevitation = save_BLev;
2198 /* actively flying handled earlier as a status condition */
2199 if (BFlying) { /* flight is blocked */
2200 long save_BFly = BFlying;
2202 BFlying = 0L;
2203 if (Flying)
2204 enl_msg(You_, "would fly", "would have flown",
2205 Levitation
2206 ? "if you weren't levitating"
2207 : (save_BFly == FROMOUTSIDE)
2208 ? if_surroundings_permitted
2209 /* both surroundings and [latent] levitation */
2210 : " if circumstances permitted",
2211 "");
2212 BFlying = save_BFly;
2214 /* actively walking on water handled earlier as a status condition */
2215 if (Wwalking && !walking_on_water())
2216 you_can("walk on water", from_what(WWALKING));
2217 /* actively swimming (in water but not under it) handled earlier */
2218 if (Swimming && (Underwater || !u.uinwater))
2219 you_can("swim", from_what(SWIMMING));
2220 if (Breathless)
2221 you_can("survive without air", from_what(MAGICAL_BREATHING));
2222 else if (Amphibious)
2223 you_can("breathe water", from_what(MAGICAL_BREATHING));
2224 if (Passes_walls)
2225 you_can("walk through walls", from_what(PASSES_WALLS));
2227 /*** Physical attributes ***/
2228 if (Regeneration)
2229 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2230 if (Slow_digestion)
2231 you_have("slower digestion", from_what(SLOW_DIGESTION));
2232 if (u.uhitinc)
2233 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2234 if (u.udaminc)
2235 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2236 if (u.uspellprot || Protection) {
2237 int prot = 0;
2239 if (uleft && uleft->otyp == RIN_PROTECTION)
2240 prot += uleft->spe;
2241 if (uright && uright->otyp == RIN_PROTECTION)
2242 prot += uright->spe;
2243 if (HProtection & INTRINSIC)
2244 prot += u.ublessed;
2245 prot += u.uspellprot;
2246 if (prot)
2247 you_have(enlght_combatinc("defense", prot, final, buf), "");
2249 if ((armpro = magic_negation(&youmonst)) > 0) {
2250 /* magic cancellation factor, conferred by worn armor */
2251 static const char *const mc_types[] = {
2252 "" /*ordinary*/, "warded", "guarded", "protected",
2254 /* sanity check */
2255 if (armpro >= SIZE(mc_types))
2256 armpro = SIZE(mc_types) - 1;
2257 you_are(mc_types[armpro], "");
2259 if (Half_physical_damage)
2260 enlght_halfdmg(HALF_PHDAM, final);
2261 if (Half_spell_damage)
2262 enlght_halfdmg(HALF_SPDAM, final);
2263 /* polymorph and other shape change */
2264 if (Protection_from_shape_changers)
2265 you_are("protected from shape changers",
2266 from_what(PROT_FROM_SHAPE_CHANGERS));
2267 if (Unchanging) {
2268 const char *what = 0;
2270 if (!Upolyd) /* Upolyd handled below after current form */
2271 you_can("not change from your current form",
2272 from_what(UNCHANGING));
2273 /* blocked shape changes */
2274 if (Polymorph)
2275 what = !final ? "polymorph" : "have polymorphed";
2276 else if (u.ulycn >= LOW_PM)
2277 what = !final ? "change shape" : "have changed shape";
2278 if (what) {
2279 Sprintf(buf, "would %s periodically", what);
2280 /* omit from_what(UNCHANGING); too verbose */
2281 enl_msg(You_, buf, buf, " if not locked into your current form",
2282 "");
2284 } else if (Polymorph) {
2285 you_are("polymorphing periodically", from_what(POLYMORPH));
2287 if (Polymorph_control)
2288 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2289 if (Upolyd && u.umonnum != u.ulycn) {
2290 /* foreign shape (except were-form which is handled below) */
2291 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2292 if (wizard)
2293 Sprintf(eos(buf), " (%d)", u.mtimedone);
2294 you_are(buf, "");
2296 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2297 you_can("lay eggs", "");
2298 if (u.ulycn >= LOW_PM) {
2299 /* "you are a werecreature [in beast form]" */
2300 Strcpy(buf, an(mons[u.ulycn].mname));
2301 if (u.umonnum == u.ulycn) {
2302 Strcat(buf, " in beast form");
2303 if (wizard)
2304 Sprintf(eos(buf), " (%d)", u.mtimedone);
2306 you_are(buf, "");
2308 if (Unchanging && Upolyd) /* !Upolyd handled above */
2309 you_can("not change from your current form", from_what(UNCHANGING));
2310 if (Hate_silver)
2311 you_are("harmed by silver", "");
2312 /* movement and non-armor-based protection */
2313 if (Fast)
2314 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2315 if (Reflecting)
2316 you_have("reflection", from_what(REFLECTING));
2317 if (Free_action)
2318 you_have("free action", from_what(FREE_ACTION));
2319 if (Fixed_abil)
2320 you_have("fixed abilities", from_what(FIXED_ABIL));
2321 if (Lifesaved)
2322 enl_msg("Your life ", "will be", "would have been", " saved", "");
2324 /*** Miscellany ***/
2325 if (Luck) {
2326 ltmp = abs((int) Luck);
2327 Sprintf(buf, "%s%slucky",
2328 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2329 Luck < 0 ? "un" : "");
2330 if (wizard)
2331 Sprintf(eos(buf), " (%d)", Luck);
2332 you_are(buf, "");
2333 } else if (wizard)
2334 enl_msg("Your luck ", "is", "was", " zero", "");
2335 if (u.moreluck > 0)
2336 you_have("extra luck", "");
2337 else if (u.moreluck < 0)
2338 you_have("reduced luck", "");
2339 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2340 ltmp = stone_luck(0);
2341 if (ltmp <= 0)
2342 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2343 if (ltmp >= 0)
2344 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2347 if (u.ugangr) {
2348 Sprintf(buf, " %sangry with you",
2349 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2350 if (wizard)
2351 Sprintf(eos(buf), " (%d)", u.ugangr);
2352 enl_msg(u_gname(), " is", " was", buf, "");
2353 } else {
2355 * We need to suppress this when the game is over, because death
2356 * can change the value calculated by can_pray(), potentially
2357 * resulting in a false claim that you could have prayed safely.
2359 if (!final) {
2360 #if 0
2361 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2362 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2363 final ? "have " : "", final ? "ed" : "");
2364 #else
2365 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2366 #endif
2367 if (wizard)
2368 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2369 you_can(buf, "");
2373 #ifdef DEBUG
2374 /* named fruit debugging (doesn't really belong here...); to enable,
2375 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2376 if (wizard && explicitdebug("fruit")) {
2377 int fcount = 0;
2378 struct fruit *f;
2379 char buf2[BUFSZ];
2381 for (f = ffruit; f; f = f->nextf) {
2382 Sprintf(buf, "Fruit %d ", ++fcount);
2383 Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
2384 enl_msg(buf, "is ", "was ", buf2, "");
2386 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
2387 Sprintf(buf, "%d", flags.made_fruit);
2388 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
2390 #endif
2393 const char *p;
2395 buf[0] = '\0';
2396 if (final < 2) { /* still in progress, or quit/escaped/ascended */
2397 p = "survived after being killed ";
2398 switch (u.umortality) {
2399 case 0:
2400 p = !final ? (char *) 0 : "survived";
2401 break;
2402 case 1:
2403 Strcpy(buf, "once");
2404 break;
2405 case 2:
2406 Strcpy(buf, "twice");
2407 break;
2408 case 3:
2409 Strcpy(buf, "thrice");
2410 break;
2411 default:
2412 Sprintf(buf, "%d times", u.umortality);
2413 break;
2415 } else { /* game ended in character's death */
2416 p = "are dead";
2417 switch (u.umortality) {
2418 case 0:
2419 impossible("dead without dying?");
2420 case 1:
2421 break; /* just "are dead" */
2422 default:
2423 Sprintf(buf, " (%d%s time!)", u.umortality,
2424 ordin(u.umortality));
2425 break;
2428 if (p)
2429 enl_msg(You_, "have been killed ", p, buf, "");
2433 #if 0 /* no longer used */
2434 STATIC_DCL boolean NDECL(minimal_enlightenment);
2437 * Courtesy function for non-debug, non-explorer mode players
2438 * to help refresh them about who/what they are.
2439 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2441 STATIC_OVL boolean
2442 minimal_enlightenment()
2444 winid tmpwin;
2445 menu_item *selected;
2446 anything any;
2447 int genidx, n;
2448 char buf[BUFSZ], buf2[BUFSZ];
2449 static const char untabbed_fmtstr[] = "%-15s: %-12s";
2450 static const char untabbed_deity_fmtstr[] = "%-17s%s";
2451 static const char tabbed_fmtstr[] = "%s:\t%-12s";
2452 static const char tabbed_deity_fmtstr[] = "%s\t%s";
2453 static const char *fmtstr;
2454 static const char *deity_fmtstr;
2456 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
2457 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
2458 : untabbed_deity_fmtstr;
2459 any = zeroany;
2460 buf[0] = buf2[0] = '\0';
2461 tmpwin = create_nhwindow(NHW_MENU);
2462 start_menu(tmpwin);
2463 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2464 "Starting", FALSE);
2466 /* Starting name, race, role, gender */
2467 Sprintf(buf, fmtstr, "name", plname);
2468 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2469 Sprintf(buf, fmtstr, "race", urace.noun);
2470 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2471 Sprintf(buf, fmtstr, "role",
2472 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
2473 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2474 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
2475 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2477 /* Starting alignment */
2478 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
2479 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2481 /* Current name, race, role, gender */
2482 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2483 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2484 "Current", FALSE);
2485 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
2486 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2487 if (Upolyd) {
2488 Sprintf(buf, fmtstr, "role (base)",
2489 (u.mfemale && urole.name.f) ? urole.name.f
2490 : urole.name.m);
2491 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2492 } else {
2493 Sprintf(buf, fmtstr, "role",
2494 (flags.female && urole.name.f) ? urole.name.f
2495 : urole.name.m);
2496 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2498 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2499 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
2500 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
2501 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2502 if (Upolyd && (int) u.mfemale != genidx) {
2503 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
2504 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2507 /* Current alignment */
2508 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
2509 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2511 /* Deity list */
2512 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2513 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2514 "Deities", FALSE);
2515 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
2516 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2517 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
2518 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
2519 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
2520 Sprintf(buf, fmtstr, "Chaotic", buf2);
2521 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2523 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
2524 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2525 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
2526 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
2527 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
2528 Sprintf(buf, fmtstr, "Neutral", buf2);
2529 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2531 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
2532 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2533 && u.ualign.type == A_LAWFUL) ? " (s,c)"
2534 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
2535 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
2536 Sprintf(buf, fmtstr, "Lawful", buf2);
2537 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2539 end_menu(tmpwin, "Base Attributes");
2540 n = select_menu(tmpwin, PICK_NONE, &selected);
2541 destroy_nhwindow(tmpwin);
2542 return (boolean) (n != -1);
2544 #endif /*0*/
2546 /* ^X command */
2547 STATIC_PTR int
2548 doattributes(VOID_ARGS)
2550 int mode = BASICENLIGHTENMENT;
2552 /* show more--as if final disclosure--for wizard and explore modes */
2553 if (wizard || discover)
2554 mode |= MAGICENLIGHTENMENT;
2556 enlightenment(mode, ENL_GAMEINPROGRESS);
2557 return 0;
2560 void
2561 youhiding(via_enlghtmt, msgflag)
2562 boolean via_enlghtmt; /* englightment line vs topl message */
2563 int msgflag; /* for variant message phrasing */
2565 char *bp, buf[BUFSZ];
2567 Strcpy(buf, "hiding");
2568 if (youmonst.m_ap_type != M_AP_NOTHING) {
2569 /* mimic; hero is only able to mimic a strange object or gold
2570 or hallucinatory alternative to gold, so we skip the details
2571 for the hypothetical furniture and monster cases */
2572 bp = eos(strcpy(buf, "mimicking"));
2573 if (youmonst.m_ap_type == M_AP_OBJECT) {
2574 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
2575 } else if (youmonst.m_ap_type == M_AP_FURNITURE) {
2576 Strcpy(bp, " something");
2577 } else if (youmonst.m_ap_type == M_AP_MONSTER) {
2578 Strcpy(bp, " someone");
2579 } else {
2580 ; /* something unexpected; leave 'buf' as-is */
2582 } else if (u.uundetected) {
2583 bp = eos(buf); /* points past "hiding" */
2584 if (youmonst.data->mlet == S_EEL) {
2585 if (is_pool(u.ux, u.uy))
2586 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
2587 } else if (hides_under(youmonst.data)) {
2588 struct obj *o = level.objects[u.ux][u.uy];
2590 if (o)
2591 Sprintf(bp, " underneath %s", ansimpleoname(o));
2592 } else if (is_clinger(youmonst.data) || Flying) {
2593 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2594 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
2595 } else {
2596 /* on floor; is_hider() but otherwise not special: 'trapper' */
2597 if (u.utrap && u.utraptype == TT_PIT) {
2598 struct trap *t = t_at(u.ux, u.uy);
2600 Sprintf(bp, " in a %spit",
2601 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
2602 } else
2603 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
2605 } else {
2606 ; /* shouldn't happen; will result in generic "you are hiding" */
2609 if (via_enlghtmt) {
2610 int final = msgflag; /* 'final' is used by you_are() macro */
2612 you_are(buf, "");
2613 } else {
2614 /* for dohide(), when player uses '#monster' command */
2615 You("are %s %s.", msgflag ? "already" : "now", buf);
2619 /* KMH, #conduct
2620 * (shares enlightenment's tense handling)
2622 STATIC_PTR int
2623 doconduct(VOID_ARGS)
2625 show_conduct(0);
2626 return 0;
2629 void
2630 show_conduct(final)
2631 int final;
2633 char buf[BUFSZ];
2634 int ngenocided;
2636 /* Create the conduct window */
2637 en_win = create_nhwindow(NHW_MENU);
2638 putstr(en_win, 0, "Voluntary challenges:");
2640 if (u.uroleplay.blind)
2641 you_have_been("blind from birth");
2642 if (u.uroleplay.nudist)
2643 you_have_been("faithfully nudist");
2645 if (!u.uconduct.food)
2646 enl_msg(You_, "have gone", "went", " without food", "");
2647 /* But beverages are okay */
2648 else if (!u.uconduct.unvegan)
2649 you_have_X("followed a strict vegan diet");
2650 else if (!u.uconduct.unvegetarian)
2651 you_have_been("vegetarian");
2653 if (!u.uconduct.gnostic)
2654 you_have_been("an atheist");
2656 if (!u.uconduct.weaphit) {
2657 you_have_never("hit with a wielded weapon");
2658 } else if (wizard) {
2659 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
2660 plur(u.uconduct.weaphit));
2661 you_have_X(buf);
2663 if (!u.uconduct.killer)
2664 you_have_been("a pacifist");
2666 if (!u.uconduct.literate) {
2667 you_have_been("illiterate");
2668 } else if (wizard) {
2669 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
2670 plur(u.uconduct.literate));
2671 you_have_X(buf);
2674 ngenocided = num_genocides();
2675 if (ngenocided == 0) {
2676 you_have_never("genocided any monsters");
2677 } else {
2678 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
2679 plur(ngenocided), plur(ngenocided));
2680 you_have_X(buf);
2683 if (!u.uconduct.polypiles) {
2684 you_have_never("polymorphed an object");
2685 } else if (wizard) {
2686 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
2687 plur(u.uconduct.polypiles));
2688 you_have_X(buf);
2691 if (!u.uconduct.polyselfs) {
2692 you_have_never("changed form");
2693 } else if (wizard) {
2694 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
2695 plur(u.uconduct.polyselfs));
2696 you_have_X(buf);
2699 if (!u.uconduct.wishes) {
2700 you_have_X("used no wishes");
2701 } else {
2702 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
2703 (u.uconduct.wishes > 1L) ? "es" : "");
2704 if (u.uconduct.wisharti) {
2705 /* if wisharti == wishes
2706 * 1 wish (for an artifact)
2707 * 2 wishes (both for artifacts)
2708 * N wishes (all for artifacts)
2709 * else (N is at least 2 in order to get here; M < N)
2710 * N wishes (1 for an artifact)
2711 * N wishes (M for artifacts)
2713 if (u.uconduct.wisharti == u.uconduct.wishes)
2714 Sprintf(eos(buf), " (%s",
2715 (u.uconduct.wisharti > 2L) ? "all "
2716 : (u.uconduct.wisharti == 2L) ? "both " : "");
2717 else
2718 Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti);
2720 Sprintf(eos(buf), "for %s)",
2721 (u.uconduct.wisharti == 1L) ? "an artifact"
2722 : "artifacts");
2724 you_have_X(buf);
2726 if (!u.uconduct.wisharti)
2727 enl_msg(You_, "have not wished", "did not wish",
2728 " for any artifacts", "");
2731 /* Pop up the window and wait for a key */
2732 display_nhwindow(en_win, TRUE);
2733 destroy_nhwindow(en_win);
2734 en_win = WIN_ERR;
2737 /* Macros for meta and ctrl modifiers:
2738 * M and C return the meta/ctrl code for the given character;
2739 * e.g., (C('c') is ctrl-c
2740 * ISMETA and ISCTRL return TRUE iff the code is a meta/ctrl code
2741 * UNMETA and UNCTRL are the opposite of M/C and return the key for a given
2742 * meta/ctrl code. */
2743 #ifndef M
2744 #ifndef NHSTDC
2745 #define M(c) (0x80 | (c))
2746 #else
2747 #define M(c) ((c) -128)
2748 #endif /* NHSTDC */
2749 #endif
2750 #define ISMETA(c) (((c) & 0x80) != 0)
2751 #define UNMETA(c) ((c) & 0x7f)
2753 #ifndef C
2754 #define C(c) (0x1f & (c))
2755 #endif
2756 #define ISCTRL(c) ((uchar)(c) < 0x20)
2757 #define UNCTRL(c) (ISCTRL(c) ? (0x60 | (c)) : (c))
2759 /* ordered by command name */
2760 struct ext_func_tab extcmdlist[] = {
2761 { '#', "#", "perform an extended command",
2762 doextcmd, IFBURIED | GENERALCMD },
2763 { M('?'), "?", "get this list of extended commands",
2764 doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2765 { M('a'), "adjust", "adjust inventory letters",
2766 doorganize, IFBURIED | AUTOCOMPLETE },
2767 { M('A'), "annotate", "name current level",
2768 donamelevel, IFBURIED | AUTOCOMPLETE },
2769 { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)",
2770 doapply },
2771 { C('x'), "attributes", "show your attributes",
2772 doattributes, IFBURIED },
2773 { '@', "autopickup", "toggle the pickup option on/off",
2774 dotogglepickup, IFBURIED },
2775 { 'C', "call", "call (name) something", docallcmd, IFBURIED },
2776 { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED },
2777 { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE },
2778 { 'c', "close", "close a door", doclose },
2779 { M('C'), "conduct", "list voluntary challenges you have maintained",
2780 doconduct, IFBURIED | AUTOCOMPLETE },
2781 { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE },
2782 { '>', "down", "go down a staircase", dodown },
2783 { 'd', "drop", "drop an item", dodrop },
2784 { 'D', "droptype", "drop specific item types", doddrop },
2785 { 'e', "eat", "eat something", doeat },
2786 { 'E', "engrave", "engrave writing on the floor", doengrave },
2787 { M('e'), "enhance", "advance or check weapon and spell skills",
2788 enhance_weapon_skill, IFBURIED | AUTOCOMPLETE },
2789 { '\0', "exploremode", "enter explore (discovery) mode",
2790 enter_explore_mode, IFBURIED },
2791 { 'f', "fire", "fire ammunition from quiver", dofire },
2792 { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE },
2793 { ';', "glance", "show what type of thing a map symbol corresponds to",
2794 doquickwhatis, IFBURIED | GENERALCMD },
2795 { '?', "help", "give a help message", dohelp, IFBURIED | GENERALCMD },
2796 { 'V', "history", "show long version and game history",
2797 dohistory, IFBURIED | GENERALCMD },
2798 { 'i', "inventory", "show your inventory", ddoinv, IFBURIED },
2799 { 'I', "inventtype", "inventory specific item types",
2800 dotypeinv, IFBURIED },
2801 { M('i'), "invoke", "invoke an object's special powers",
2802 doinvoke, IFBURIED | AUTOCOMPLETE },
2803 { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE },
2804 { C('d'), "kick", "kick something", dokick },
2805 { '\\', "known", "show what object types have been discovered",
2806 dodiscovered, IFBURIED | GENERALCMD },
2807 { '`', "knownclass", "show discovered types for one class of objects",
2808 doclassdisco, IFBURIED|GENERALCMD },
2809 { '\0', "levelchange", "change experience level",
2810 wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2811 { '\0', "lightsources", "show mobile light sources",
2812 wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2813 { ':', "look", "look at what is here", dolook, IFBURIED },
2814 { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE },
2815 #ifdef DEBUG_MIGRATING_MONS
2816 { '\0', "migratemons", "migrate N random monsters",
2817 wiz_migrate_mons, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2818 #endif
2819 { '\0', "monpolycontrol", "control monster polymorphs",
2820 wiz_mon_polycontrol, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2821 { M('m'), "monster", "use monster's special ability",
2822 domonability, IFBURIED | AUTOCOMPLETE },
2823 { 'N', "name", "name a monster or an object",
2824 docallcmd, IFBURIED | AUTOCOMPLETE },
2825 { M('o'), "offer", "offer a sacrifice to the gods",
2826 dosacrifice, AUTOCOMPLETE },
2827 { 'o', "open", "open a door", doopen },
2828 { 'O', "options", "show option settings, possibly change them",
2829 doset, IFBURIED|GENERALCMD },
2830 { C('o'), "overview", "show a summary of the explored dungeon",
2831 dooverview, IFBURIED|AUTOCOMPLETE },
2832 { '\0', "panic", "test panic routine (fatal to game)",
2833 wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2834 { 'p', "pay", "pay your shopping bill", dopay },
2835 { ',', "pickup", "pick up things at the current location", dopickup },
2836 { '\0', "polyself", "polymorph self",
2837 wiz_polyself, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2838 #ifdef PORT_DEBUG
2839 { '\0', "portdebug", "wizard port debug command",
2840 wiz_port_debug, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2841 #endif
2842 { M('p'), "pray", "pray to the gods for help",
2843 dopray, IFBURIED | AUTOCOMPLETE },
2844 { C('p'), "prevmsg", "view recent game messages",
2845 doprev_message, IFBURIED|GENERALCMD },
2846 { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon },
2847 { 'q', "quaff", "quaff (drink) something", dodrink },
2848 { M('q'), "quit", "exit without saving current game",
2849 done2, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2850 { 'Q', "quiver", "select ammunition for quiver", dowieldquiver },
2851 { 'r', "read", "read a scroll or spellbook", doread },
2852 { C('r'), "redraw", "redraw screen", doredraw, IFBURIED | GENERALCMD },
2853 { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring },
2854 { M('R'), "ride", "mount or dismount a saddled steed",
2855 doride, AUTOCOMPLETE },
2856 { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE },
2857 { 'S', "save", "save the game and exit", dosave, IFBURIED | GENERALCMD },
2858 { 's', "search", "search for traps and secret doors",
2859 dosearch, IFBURIED, "searching" },
2860 { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED },
2861 { AMULET_SYM, "seeamulet", "show the amulet currently worn",
2862 dopramulet, IFBURIED },
2863 { ARMOR_SYM, "seearmor", "show the armor currently worn",
2864 doprarm, IFBURIED },
2865 { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED },
2866 { '\0', "seenv", "show seen vectors",
2867 wiz_show_seenv, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2868 { RING_SYM, "seerings", "show the ring(s) currently worn",
2869 doprring, IFBURIED },
2870 { SPBOOK_SYM, "seespells", "list and reorder known spells",
2871 dovspell, IFBURIED },
2872 { TOOL_SYM, "seetools", "show the tools currently in use",
2873 doprtool, IFBURIED },
2874 { '^', "seetrap", "show the type of adjacent trap", doidtrap, IFBURIED },
2875 { WEAPON_SYM, "seeweapon", "show the weapon currently wielded",
2876 doprwep, IFBURIED },
2877 #ifdef SHELL
2878 { '!', "shell", "do a shell escape", dosh, IFBURIED | GENERALCMD },
2879 #endif /* SHELL */
2880 { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE },
2881 { '\0', "stats", "show memory statistics",
2882 wiz_show_stats, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2883 #ifdef SUSPEND
2884 { C('z'), "suspend", "suspend the game",
2885 dosuspend_core, IFBURIED | GENERALCMD },
2886 #endif /* SUSPEND */
2887 { 'x', "swap", "swap wielded and secondary weapons", doswapweapon },
2888 { 'T', "takeoff", "take off one piece of armor", dotakeoff },
2889 { 'A', "takeoffall", "remove all armor", doddoremarm },
2890 { C('t'), "teleport", "teleport around the level", dotele, IFBURIED },
2891 { '\0', "terrain", "show map without obstructions",
2892 doterrain, IFBURIED | AUTOCOMPLETE },
2893 { 't', "throw", "throw something", dothrow },
2894 { '\0', "timeout", "look at timeout queue",
2895 wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2896 { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE },
2897 { '_', "travel", "travel to a specific location on the map", dotravel },
2898 { M('t'), "turn", "turn undead away", doturn, IFBURIED | AUTOCOMPLETE },
2899 { 'X', "twoweapon", "toggle two-weapon combat",
2900 dotwoweapon, AUTOCOMPLETE },
2901 { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE },
2902 { '<', "up", "go up a staircase", doup },
2903 { '\0', "vanquished", "list vanquished monsters",
2904 dovanquished, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2905 { M('v'), "version",
2906 "list compile time options for this version of NetHack",
2907 doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2908 { 'v', "versionshort", "show version", doversion, IFBURIED | GENERALCMD },
2909 { '\0', "vision", "show vision array",
2910 wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2911 { '.', "wait", "rest one move while doing nothing",
2912 donull, IFBURIED, "waiting" },
2913 { 'W', "wear", "wear a piece of armor", dowear },
2914 { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED },
2915 { '/', "whatis", "show what type of thing a symbol corresponds to",
2916 dowhatis, IFBURIED | GENERALCMD },
2917 { 'w', "wield", "wield (put in use) a weapon", dowield },
2918 { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE },
2919 #ifdef DEBUG
2920 { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you",
2921 wiz_debug_cmd_bury, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2922 { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2923 wiz_debug_cmd_traveldisplay, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2924 #endif
2925 { C('e'), "wizdetect", "reveal hidden things within a small radius",
2926 wiz_detect, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2927 { C('g'), "wizgenesis", "create a monster",
2928 wiz_genesis, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2929 { C('i'), "wizidentify", "identify all items in inventory",
2930 wiz_identify, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2931 { '\0', "wizintrinsic", "set an intrinsic",
2932 wiz_intrinsic, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2933 { C('v'), "wizlevelport", "teleport to another level",
2934 wiz_level_tele, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2935 { '\0', "wizmakemap", "recreate the current level",
2936 wiz_makemap, IFBURIED | WIZMODECMD },
2937 { C('f'), "wizmap", "map the level",
2938 wiz_map, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2939 { '\0', "wizrumorcheck", "verify rumor boundaries",
2940 wiz_rumor_check, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2941 { '\0', "wizsmell", "smell monster",
2942 wiz_smell, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2943 { '\0', "wizwhere", "show locations of special levels",
2944 wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2945 { C('w'), "wizwish", "wish for something",
2946 wiz_wish, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2947 { '\0', "wmode", "show wall modes",
2948 wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2949 { 'z', "zap", "zap a wand", dozap },
2950 { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */
2953 const char *
2954 key2extcmddesc(key)
2955 uchar key;
2957 if (Cmd.commands[key] && Cmd.commands[key]->ef_txt)
2958 return Cmd.commands[key]->ef_desc;
2959 return (char *) 0;
2962 void
2963 bind_key(key, command)
2964 uchar key;
2965 const char *command;
2967 struct ext_func_tab *extcmd;
2969 /* special case: "nothing" is reserved for unbinding */
2970 if (!strcmp(command, "nothing")) {
2971 Cmd.commands[key] = (struct ext_func_tab *) 0;
2972 return;
2975 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
2976 if (strcmp(command, extcmd->ef_txt))
2977 continue;
2978 Cmd.commands[key] = extcmd;
2979 return;
2982 pline(
2983 "Bad command %s matched with key %c (ASCII %i). Ignoring command.\n",
2984 command, key, key);
2987 /* initialize all keyboard commands */
2988 void
2989 commands_init()
2991 struct ext_func_tab *extcmd;
2993 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++)
2994 if (extcmd->key)
2995 Cmd.commands[extcmd->key] = extcmd;
2997 bind_key(C('l'), "redraw"); /* if number_pad is set */
2998 /* 'b', 'B' : go sw */
2999 /* 'F' : fight (one time) */
3000 /* 'g', 'G' : multiple go */
3001 /* 'h', 'H' : go west */
3002 bind_key('h', "help"); /* if number_pad is set */
3003 bind_key('j', "jump"); /* if number_pad is on */
3004 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' move commands */
3005 bind_key('k', "kick"); /* if number_pad is on */
3006 bind_key('l', "loot"); /* if number_pad is on */
3007 bind_key(C('n'), "annotate"); /* if number_pad is on */
3008 bind_key(M('n'), "name");
3009 bind_key(M('N'), "name");
3010 bind_key('u', "untrap"); /* if number_pad is on */
3012 /* alt keys: */
3013 bind_key(M('O'), "overview");
3014 bind_key(M('2'), "twoweapon");
3016 /* wait_on_space */
3017 bind_key(' ', "wait");
3021 dokeylist_putcmds(datawin, docount, cmdflags, exflags, keys_used)
3022 winid datawin;
3023 boolean docount;
3024 int cmdflags, exflags;
3025 boolean *keys_used; /* boolean keys_used[256] */
3027 int i;
3028 char buf[BUFSZ];
3029 char buf2[QBUFSZ];
3030 int count = 0;
3032 for (i = 0; i < 256; i++) {
3033 const struct ext_func_tab *extcmd;
3034 uchar key = (uchar) i;
3036 if (keys_used[i])
3037 continue;
3038 if (key == ' ' && !flags.rest_on_space)
3039 continue;
3040 if ((extcmd = Cmd.commands[i]) != (struct ext_func_tab *) 0) {
3041 if ((cmdflags && !(extcmd->flags & cmdflags))
3042 || (exflags && (extcmd->flags & exflags)))
3043 continue;
3044 if (docount) {
3045 count++;
3046 continue;
3048 Sprintf(buf, "%-8s %-12s %s", key2txt(key, buf2),
3049 extcmd->ef_txt,
3050 extcmd->ef_desc);
3051 putstr(datawin, 0, buf);
3052 keys_used[i] = TRUE;
3055 return count;
3058 /* list all keys and their bindings, like dat/hh but dynamic */
3059 void
3060 dokeylist(VOID_ARGS)
3062 char buf[BUFSZ], buf2[BUFSZ];
3063 uchar key;
3064 boolean keys_used[256] = {0};
3065 winid datawin;
3066 int i;
3067 const struct {
3068 int nhkf;
3069 const char *desc;
3070 boolean numpad;
3071 } misc_keys[] = {
3072 { NHKF_ESC, "escape from the current query/action", FALSE },
3073 { NHKF_RUSH,
3074 "Prefix: rush until something interesting is seen", FALSE },
3075 { NHKF_RUN,
3076 "Prefix: run until something extremely interesting is seen", FALSE },
3077 { NHKF_RUN2,
3078 "Prefix: run until something extremely interesting is seen", TRUE },
3079 { NHKF_FIGHT,
3080 "Prefix: force fight even if you don't see a monster", FALSE },
3081 { NHKF_FIGHT2,
3082 "Prefix: force fight even if you don't see a monster", TRUE },
3083 { NHKF_NOPICKUP,
3084 "Prefix: move without picking up objects/fighting", FALSE },
3085 { NHKF_RUN_NOPICKUP,
3086 "Prefix: run without picking up objects/fighting", FALSE },
3087 { NHKF_DOINV, "inventory (same as #inventory)", TRUE },
3088 { NHKF_REQMENU, "Prefix: request a menu", FALSE },
3089 #ifdef REDO
3090 { NHKF_DOAGAIN , "redo the previous command", FALSE },
3091 #endif
3092 { 0, (const char *) 0, FALSE }
3095 datawin = create_nhwindow(NHW_TEXT);
3096 putstr(datawin, 0, "");
3097 putstr(datawin, 0, " Full Current Key Bindings List");
3099 /* directional keys */
3100 putstr(datawin, 0, "");
3101 putstr(datawin, 0, "Directional keys:");
3102 show_direction_keys(datawin, FALSE);
3104 keys_used[(uchar) Cmd.move_NW] = keys_used[(uchar) Cmd.move_N]
3105 = keys_used[(uchar) Cmd.move_NE] = keys_used[(uchar) Cmd.move_W]
3106 = keys_used[(uchar) Cmd.move_E] = keys_used[(uchar) Cmd.move_SW]
3107 = keys_used[(uchar) Cmd.move_S] = keys_used[(uchar) Cmd.move_SE]
3108 = TRUE;
3110 if (!iflags.num_pad) {
3111 keys_used[(uchar) highc(Cmd.move_NW)]
3112 = keys_used[(uchar) highc(Cmd.move_N)]
3113 = keys_used[(uchar) highc(Cmd.move_NE)]
3114 = keys_used[(uchar) highc(Cmd.move_W)]
3115 = keys_used[(uchar) highc(Cmd.move_E)]
3116 = keys_used[(uchar) highc(Cmd.move_SW)]
3117 = keys_used[(uchar) highc(Cmd.move_S)]
3118 = keys_used[(uchar) highc(Cmd.move_SE)] = TRUE;
3119 keys_used[(uchar) C(Cmd.move_NW)]
3120 = keys_used[(uchar) C(Cmd.move_N)]
3121 = keys_used[(uchar) C(Cmd.move_NE)]
3122 = keys_used[(uchar) C(Cmd.move_W)]
3123 = keys_used[(uchar) C(Cmd.move_E)]
3124 = keys_used[(uchar) C(Cmd.move_SW)]
3125 = keys_used[(uchar) C(Cmd.move_S)]
3126 = keys_used[(uchar) C(Cmd.move_SE)] = TRUE;
3127 putstr(datawin, 0, "");
3128 putstr(datawin, 0,
3129 "Shift-<direction> will move in specified direction until you hit");
3130 putstr(datawin, 0, " a wall or run into something.");
3131 putstr(datawin, 0,
3132 "Ctrl-<direction> will run in specified direction until something");
3133 putstr(datawin, 0, " very interesting is seen.");
3136 putstr(datawin, 0, "");
3137 putstr(datawin, 0, "Miscellaneous keys:");
3138 for (i = 0; misc_keys[i].desc; i++) {
3139 key = Cmd.spkeys[misc_keys[i].nhkf];
3140 if (key && ((misc_keys[i].numpad && iflags.num_pad)
3141 || !misc_keys[i].numpad)) {
3142 keys_used[(uchar) key] = TRUE;
3143 Sprintf(buf, "%-8s %s", key2txt(key, buf2), misc_keys[i].desc);
3144 putstr(datawin, 0, buf);
3147 #ifndef NO_SIGNAL
3148 putstr(datawin, 0, "^c break out of NetHack (SIGINT)");
3149 keys_used[(uchar) C('c')] = TRUE;
3150 #endif
3152 putstr(datawin, 0, "");
3153 show_menu_controls(datawin, TRUE);
3155 if (dokeylist_putcmds(datawin, TRUE, GENERALCMD, WIZMODECMD, keys_used)) {
3156 putstr(datawin, 0, "");
3157 putstr(datawin, 0, "General commands:");
3158 (void) dokeylist_putcmds(datawin, FALSE, GENERALCMD, WIZMODECMD,
3159 keys_used);
3162 if (dokeylist_putcmds(datawin, TRUE, 0, WIZMODECMD, keys_used)) {
3163 putstr(datawin, 0, "");
3164 putstr(datawin, 0, "Game commands:");
3165 (void) dokeylist_putcmds(datawin, FALSE, 0, WIZMODECMD, keys_used);
3168 if (wizard
3169 && dokeylist_putcmds(datawin, TRUE, WIZMODECMD, 0, keys_used)) {
3170 putstr(datawin, 0, "");
3171 putstr(datawin, 0, "Wizard-mode commands:");
3172 (void) dokeylist_putcmds(datawin, FALSE, WIZMODECMD, 0, keys_used);
3175 display_nhwindow(datawin, FALSE);
3176 destroy_nhwindow(datawin);
3179 STATIC_OVL char
3180 cmd_from_func(fn)
3181 int NDECL((*fn));
3183 int i;
3185 for (i = 0; i < 256; ++i)
3186 if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn)
3187 return (char) i;
3188 return '\0';
3192 * wizard mode sanity_check code
3195 static const char template[] = "%-27s %4ld %6ld";
3196 static const char stats_hdr[] = " count bytes";
3197 static const char stats_sep[] = "--------------------------- ----- -------";
3199 STATIC_OVL int
3200 size_obj(otmp)
3201 struct obj *otmp;
3203 int sz = (int) sizeof(struct obj);
3205 if (otmp->oextra) {
3206 sz += (int) sizeof(struct oextra);
3207 if (ONAME(otmp))
3208 sz += (int) strlen(ONAME(otmp)) + 1;
3209 if (OMONST(otmp))
3210 sz += (int) sizeof(struct monst);
3211 if (OMID(otmp))
3212 sz += (int) sizeof(unsigned);
3213 if (OLONG(otmp))
3214 sz += (int) sizeof(long);
3215 if (OMAILCMD(otmp))
3216 sz += (int) strlen(OMAILCMD(otmp)) + 1;
3218 return sz;
3221 STATIC_OVL void
3222 count_obj(chain, total_count, total_size, top, recurse)
3223 struct obj *chain;
3224 long *total_count;
3225 long *total_size;
3226 boolean top;
3227 boolean recurse;
3229 long count, size;
3230 struct obj *obj;
3232 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
3233 if (top) {
3234 count++;
3235 size += size_obj(obj);
3237 if (recurse && obj->cobj)
3238 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
3240 *total_count += count;
3241 *total_size += size;
3244 STATIC_OVL void
3245 obj_chain(win, src, chain, force, total_count, total_size)
3246 winid win;
3247 const char *src;
3248 struct obj *chain;
3249 boolean force;
3250 long *total_count;
3251 long *total_size;
3253 char buf[BUFSZ];
3254 long count = 0L, size = 0L;
3256 count_obj(chain, &count, &size, TRUE, FALSE);
3258 if (count || size || force) {
3259 *total_count += count;
3260 *total_size += size;
3261 Sprintf(buf, template, src, count, size);
3262 putstr(win, 0, buf);
3266 STATIC_OVL void
3267 mon_invent_chain(win, src, chain, total_count, total_size)
3268 winid win;
3269 const char *src;
3270 struct monst *chain;
3271 long *total_count;
3272 long *total_size;
3274 char buf[BUFSZ];
3275 long count = 0, size = 0;
3276 struct monst *mon;
3278 for (mon = chain; mon; mon = mon->nmon)
3279 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
3281 if (count || size) {
3282 *total_count += count;
3283 *total_size += size;
3284 Sprintf(buf, template, src, count, size);
3285 putstr(win, 0, buf);
3289 STATIC_OVL void
3290 contained_stats(win, src, total_count, total_size)
3291 winid win;
3292 const char *src;
3293 long *total_count;
3294 long *total_size;
3296 char buf[BUFSZ];
3297 long count = 0, size = 0;
3298 struct monst *mon;
3300 count_obj(invent, &count, &size, FALSE, TRUE);
3301 count_obj(fobj, &count, &size, FALSE, TRUE);
3302 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3303 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3304 /* DEADMONSTER check not required in this loop since they have no
3305 * inventory */
3306 for (mon = fmon; mon; mon = mon->nmon)
3307 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3308 for (mon = migrating_mons; mon; mon = mon->nmon)
3309 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3311 if (count || size) {
3312 *total_count += count;
3313 *total_size += size;
3314 Sprintf(buf, template, src, count, size);
3315 putstr(win, 0, buf);
3319 STATIC_OVL int
3320 size_monst(mtmp, incl_wsegs)
3321 struct monst *mtmp;
3322 boolean incl_wsegs;
3324 int sz = (int) sizeof (struct monst);
3326 if (mtmp->wormno && incl_wsegs)
3327 sz += size_wseg(mtmp);
3329 if (mtmp->mextra) {
3330 sz += (int) sizeof (struct mextra);
3331 if (MNAME(mtmp))
3332 sz += (int) strlen(MNAME(mtmp)) + 1;
3333 if (EGD(mtmp))
3334 sz += (int) sizeof (struct egd);
3335 if (EPRI(mtmp))
3336 sz += (int) sizeof (struct epri);
3337 if (ESHK(mtmp))
3338 sz += (int) sizeof (struct eshk);
3339 if (EMIN(mtmp))
3340 sz += (int) sizeof (struct emin);
3341 if (EDOG(mtmp))
3342 sz += (int) sizeof (struct edog);
3343 /* mextra->mcorpsenm doesn't point to more memory */
3345 return sz;
3348 STATIC_OVL void
3349 mon_chain(win, src, chain, force, total_count, total_size)
3350 winid win;
3351 const char *src;
3352 struct monst *chain;
3353 boolean force;
3354 long *total_count;
3355 long *total_size;
3357 char buf[BUFSZ];
3358 long count, size;
3359 struct monst *mon;
3360 /* mon->wormno means something different for migrating_mons and mydogs */
3361 boolean incl_wsegs = !strcmpi(src, "fmon");
3363 count = size = 0L;
3364 for (mon = chain; mon; mon = mon->nmon) {
3365 count++;
3366 size += size_monst(mon, incl_wsegs);
3368 if (count || size || force) {
3369 *total_count += count;
3370 *total_size += size;
3371 Sprintf(buf, template, src, count, size);
3372 putstr(win, 0, buf);
3376 STATIC_OVL void
3377 misc_stats(win, total_count, total_size)
3378 winid win;
3379 long *total_count;
3380 long *total_size;
3382 char buf[BUFSZ], hdrbuf[QBUFSZ];
3383 long count, size;
3384 int idx;
3385 struct trap *tt;
3386 struct damage *sd; /* shop damage */
3387 struct cemetery *bi; /* bones info */
3389 /* traps and engravings are output unconditionally;
3390 * others only if nonzero
3392 count = size = 0L;
3393 for (tt = ftrap; tt; tt = tt->ntrap) {
3394 ++count;
3395 size += (long) sizeof *tt;
3397 *total_count += count;
3398 *total_size += size;
3399 Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap));
3400 Sprintf(buf, template, hdrbuf, count, size);
3401 putstr(win, 0, buf);
3403 count = size = 0L;
3404 engr_stats("engravings, size %ld+text", hdrbuf, &count, &size);
3405 *total_count += count;
3406 *total_size += size;
3407 Sprintf(buf, template, hdrbuf, count, size);
3408 putstr(win, 0, buf);
3410 count = size = 0L;
3411 light_stats("light sources, size %ld", hdrbuf, &count, &size);
3412 if (count || size) {
3413 *total_count += count;
3414 *total_size += size;
3415 Sprintf(buf, template, hdrbuf, count, size);
3416 putstr(win, 0, buf);
3419 count = size = 0L;
3420 timer_stats("timers, size %ld", hdrbuf, &count, &size);
3421 if (count || size) {
3422 *total_count += count;
3423 *total_size += size;
3424 Sprintf(buf, template, hdrbuf, count, size);
3425 putstr(win, 0, buf);
3428 count = size = 0L;
3429 for (sd = level.damagelist; sd; sd = sd->next) {
3430 ++count;
3431 size += (long) sizeof *sd;
3433 if (count || size) {
3434 *total_count += count;
3435 *total_size += size;
3436 Sprintf(hdrbuf, "shop damage, size %ld",
3437 (long) sizeof (struct damage));
3438 Sprintf(buf, template, hdrbuf, count, size);
3439 putstr(win, 0, buf);
3442 count = size = 0L;
3443 region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size);
3444 if (count || size) {
3445 *total_count += count;
3446 *total_size += size;
3447 Sprintf(buf, template, hdrbuf, count, size);
3448 putstr(win, 0, buf);
3451 count = size = 0L;
3452 for (bi = level.bonesinfo; bi; bi = bi->next) {
3453 ++count;
3454 size += (long) sizeof *bi;
3456 if (count || size) {
3457 *total_count += count;
3458 *total_size += size;
3459 Sprintf(hdrbuf, "bones history, size %ld",
3460 (long) sizeof (struct cemetery));
3461 Sprintf(buf, template, hdrbuf, count, size);
3462 putstr(win, 0, buf);
3465 count = size = 0L;
3466 for (idx = 0; idx < NUM_OBJECTS; ++idx)
3467 if (objects[idx].oc_uname) {
3468 ++count;
3469 size += (long) (strlen(objects[idx].oc_uname) + 1);
3471 if (count || size) {
3472 *total_count += count;
3473 *total_size += size;
3474 Strcpy(hdrbuf, "object type names, text");
3475 Sprintf(buf, template, hdrbuf, count, size);
3476 putstr(win, 0, buf);
3481 * Display memory usage of all monsters and objects on the level.
3483 static int
3484 wiz_show_stats()
3486 char buf[BUFSZ];
3487 winid win;
3488 long total_obj_size, total_obj_count,
3489 total_mon_size, total_mon_count,
3490 total_ovr_size, total_ovr_count,
3491 total_misc_size, total_misc_count;
3493 win = create_nhwindow(NHW_TEXT);
3494 putstr(win, 0, "Current memory statistics:");
3496 total_obj_count = total_obj_size = 0L;
3497 putstr(win, 0, stats_hdr);
3498 Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj));
3499 putstr(win, 0, buf);
3500 obj_chain(win, "invent", invent, TRUE, &total_obj_count, &total_obj_size);
3501 obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size);
3502 obj_chain(win, "buried", level.buriedobjlist, FALSE,
3503 &total_obj_count, &total_obj_size);
3504 obj_chain(win, "migrating obj", migrating_objs, FALSE,
3505 &total_obj_count, &total_obj_size);
3506 obj_chain(win, "billobjs", billobjs, FALSE,
3507 &total_obj_count, &total_obj_size);
3508 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3509 mon_invent_chain(win, "migrating minvent", migrating_mons,
3510 &total_obj_count, &total_obj_size);
3511 contained_stats(win, "contained", &total_obj_count, &total_obj_size);
3512 putstr(win, 0, stats_sep);
3513 Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size);
3514 putstr(win, 0, buf);
3516 total_mon_count = total_mon_size = 0L;
3517 putstr(win, 0, "");
3518 Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst));
3519 putstr(win, 0, buf);
3520 mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size);
3521 mon_chain(win, "migrating", migrating_mons, FALSE,
3522 &total_mon_count, &total_mon_size);
3523 /* 'mydogs' is only valid during level change or end of game disclosure,
3524 but conceivably we've been called from within debugger at such time */
3525 if (mydogs) /* monsters accompanying hero */
3526 mon_chain(win, "mydogs", mydogs, FALSE,
3527 &total_mon_count, &total_mon_size);
3528 putstr(win, 0, stats_sep);
3529 Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size);
3530 putstr(win, 0, buf);
3532 total_ovr_count = total_ovr_size = 0L;
3533 putstr(win, 0, "");
3534 putstr(win, 0, " Overview");
3535 overview_stats(win, template, &total_ovr_count, &total_ovr_size);
3536 putstr(win, 0, stats_sep);
3537 Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size);
3538 putstr(win, 0, buf);
3540 total_misc_count = total_misc_size = 0L;
3541 putstr(win, 0, "");
3542 putstr(win, 0, " Miscellaneous");
3543 misc_stats(win, &total_misc_count, &total_misc_size);
3544 putstr(win, 0, stats_sep);
3545 Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size);
3546 putstr(win, 0, buf);
3548 putstr(win, 0, "");
3549 putstr(win, 0, stats_sep);
3550 Sprintf(buf, template, " Grand total",
3551 (total_obj_count + total_mon_count
3552 + total_ovr_count + total_misc_count),
3553 (total_obj_size + total_mon_size
3554 + total_ovr_size + total_misc_size));
3555 putstr(win, 0, buf);
3557 #if defined(__BORLANDC__) && !defined(_WIN32)
3558 show_borlandc_stats(win);
3559 #endif
3561 display_nhwindow(win, FALSE);
3562 destroy_nhwindow(win);
3563 return 0;
3566 void
3567 sanity_check()
3569 obj_sanity_check();
3570 timer_sanity_check();
3571 mon_sanity_check();
3572 light_sources_sanity_check();
3575 #ifdef DEBUG_MIGRATING_MONS
3576 static int
3577 wiz_migrate_mons()
3579 int mcount = 0;
3580 char inbuf[BUFSZ];
3581 struct permonst *ptr;
3582 struct monst *mtmp;
3583 d_level tolevel;
3585 getlin("How many random monsters to migrate? [0]", inbuf);
3586 if (*inbuf == '\033')
3587 return 0;
3588 mcount = atoi(inbuf);
3589 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3590 return 0;
3591 while (mcount > 0) {
3592 if (Is_stronghold(&u.uz))
3593 assign_level(&tolevel, &valley_level);
3594 else
3595 get_level(&tolevel, depth(&u.uz) + 1);
3596 ptr = rndmonst();
3597 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3598 if (mtmp)
3599 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3600 (coord *) 0);
3601 mcount--;
3603 return 0;
3605 #endif
3607 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3608 #define unmeta(c) (0x7f & (c))
3610 struct {
3611 int nhkf;
3612 char key;
3613 const char *name;
3614 } const spkeys_binds[] = {
3615 { NHKF_ESC, '\033', (char *) 0 }, /* no binding */
3616 { NHKF_DOAGAIN, DOAGAIN, "repeat" },
3617 { NHKF_REQMENU, 'm', "reqmenu" },
3618 { NHKF_RUN, 'G', "run" },
3619 { NHKF_RUN2, '5', "run.numpad" },
3620 { NHKF_RUSH, 'g', "rush" },
3621 { NHKF_FIGHT, 'F', "fight" },
3622 { NHKF_FIGHT2, '-', "fight.numpad" },
3623 { NHKF_NOPICKUP, 'm', "nopickup" },
3624 { NHKF_RUN_NOPICKUP, 'M', "run.nopickup" },
3625 { NHKF_DOINV, '0', "doinv" },
3626 { NHKF_TRAVEL, CMD_TRAVEL, (char *) 0 }, /* no binding */
3627 { NHKF_CLICKLOOK, CMD_CLICKLOOK, (char *) 0 }, /* no binding */
3628 { NHKF_REDRAW, C('r'), "redraw" },
3629 { NHKF_REDRAW2, C('l'), "redraw.numpad" },
3630 { NHKF_GETDIR_SELF, '.', "getdir.self" },
3631 { NHKF_GETDIR_SELF2, 's', "getdir.self2" },
3632 { NHKF_GETDIR_HELP, '?', "getdir.help" },
3633 { NHKF_COUNT, 'n', "count" },
3634 { NHKF_GETPOS_SELF, '@', "getpos.self" },
3635 { NHKF_GETPOS_PICK, '.', "getpos.pick" },
3636 { NHKF_GETPOS_PICK_Q, ',', "getpos.pick.quick" },
3637 { NHKF_GETPOS_PICK_O, ';', "getpos.pick.once" },
3638 { NHKF_GETPOS_PICK_V, ':', "getpos.pick.verbose" },
3639 { NHKF_GETPOS_SHOWVALID, '$', "getpos.valid" },
3640 { NHKF_GETPOS_AUTODESC, '#', "getpos.autodescribe" },
3641 { NHKF_GETPOS_MON_NEXT, 'm', "getpos.mon.next" },
3642 { NHKF_GETPOS_MON_PREV, 'M', "getpos.mon.prev" },
3643 { NHKF_GETPOS_OBJ_NEXT, 'o', "getpos.obj.next" },
3644 { NHKF_GETPOS_OBJ_PREV, 'O', "getpos.obj.prev" },
3645 { NHKF_GETPOS_DOOR_NEXT, 'd', "getpos.door.next" },
3646 { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" },
3647 { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" },
3648 { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" },
3649 { NHKF_GETPOS_INTERESTING_NEXT, 'a', "getpos.all.next" },
3650 { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" },
3651 { NHKF_GETPOS_HELP, '?', "getpos.help" },
3652 { NHKF_GETPOS_LIMITVIEW, '"', "getpos.inview" },
3653 { NHKF_GETPOS_MENU, '!', "getpos.menu" }
3656 boolean
3657 bind_specialkey(key, command)
3658 uchar key;
3659 const char *command;
3661 int i;
3662 for (i = 0; i < SIZE(spkeys_binds); i++) {
3663 if (!spkeys_binds[i].name || strcmp(command, spkeys_binds[i].name))
3664 continue;
3665 Cmd.spkeys[spkeys_binds[i].nhkf] = key;
3666 return TRUE;
3668 return FALSE;
3671 /* returns a one-byte character from the text (it may massacre the txt
3672 * buffer) */
3673 char
3674 txt2key(txt)
3675 char *txt;
3677 txt = trimspaces(txt);
3678 if (!*txt)
3679 return '\0';
3681 /* simple character */
3682 if (!txt[1])
3683 return txt[0];
3685 /* a few special entries */
3686 if (!strcmp(txt, "<enter>"))
3687 return '\n';
3688 if (!strcmp(txt, "<space>"))
3689 return ' ';
3690 if (!strcmp(txt, "<esc>"))
3691 return '\033';
3693 /* control and meta keys */
3694 switch (*txt) {
3695 case 'm': /* can be mx, Mx, m-x, M-x */
3696 case 'M':
3697 txt++;
3698 if (*txt == '-' && txt[1])
3699 txt++;
3700 if (txt[1])
3701 return '\0';
3702 return M(*txt);
3703 case 'c': /* can be cx, Cx, ^x, c-x, C-x, ^-x */
3704 case 'C':
3705 case '^':
3706 txt++;
3707 if (*txt == '-' && txt[1])
3708 txt++;
3709 if (txt[1])
3710 return '\0';
3711 return C(*txt);
3714 /* ascii codes: must be three-digit decimal */
3715 if (*txt >= '0' && *txt <= '9') {
3716 uchar key = 0;
3717 int i;
3719 for (i = 0; i < 3; i++) {
3720 if (txt[i] < '0' || txt[i] > '9')
3721 return '\0';
3722 key = 10 * key + txt[i] - '0';
3724 return key;
3727 return '\0';
3730 /* returns the text for a one-byte encoding
3731 * must be shorter than a tab for proper formatting */
3732 char *
3733 key2txt(c, txt)
3734 uchar c;
3735 char *txt; /* sufficiently long buffer */
3737 if (c == ' ')
3738 Sprintf(txt, "<space>");
3739 else if (c == '\033')
3740 Sprintf(txt, "<esc>");
3741 else if (c == '\n')
3742 Sprintf(txt, "<enter>");
3743 else if (ISCTRL(c))
3744 Sprintf(txt, "^%c", highc(UNCTRL(c)));
3745 else if (ISMETA(c))
3746 Sprintf(txt, "M-%c", UNMETA(c));
3747 else if (c >= 33 && c <= 126)
3748 Sprintf(txt, "%c", c); /* regular keys: ! through ~ */
3749 else
3750 Sprintf(txt, "A-%i", c); /* arbitrary ascii combinations */
3751 return txt;
3755 void
3756 parseautocomplete(autocomplete, condition)
3757 char *autocomplete;
3758 boolean condition;
3760 struct ext_func_tab *efp;
3761 register char *autoc;
3763 /* break off first autocomplete from the rest; parse the rest */
3764 if ((autoc = index(autocomplete, ',')) != 0
3765 || (autoc = index(autocomplete, ':')) != 0) {
3766 *autoc++ = '\0';
3767 parseautocomplete(autoc, condition);
3770 /* strip leading and trailing white space */
3771 autocomplete = trimspaces(autocomplete);
3773 if (!*autocomplete)
3774 return;
3776 /* take off negation */
3777 if (*autocomplete == '!') {
3778 /* unlike most options, a leading "no" might actually be a part of
3779 * the extended command. Thus you have to use ! */
3780 autocomplete++;
3781 autocomplete = trimspaces(autocomplete);
3782 condition = !condition;
3785 /* find and modify the extended command */
3786 for (efp = extcmdlist; efp->ef_txt; efp++) {
3787 if (!strcmp(autocomplete, efp->ef_txt)) {
3788 if (condition)
3789 efp->flags |= AUTOCOMPLETE;
3790 else
3791 efp->flags &= ~AUTOCOMPLETE;
3792 return;
3796 /* not a real extended command */
3797 raw_printf("Bad autocomplete: invalid extended command '%s'.",
3798 autocomplete);
3799 wait_synch();
3802 /* called at startup and after number_pad is twiddled */
3803 void
3804 reset_commands(initial)
3805 boolean initial;
3807 static const char sdir[] = "hykulnjb><",
3808 sdir_swap_yz[] = "hzkulnjb><",
3809 ndir[] = "47896321><",
3810 ndir_phone_layout[] = "41236987><";
3811 static const int ylist[] = {
3812 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3814 static struct ext_func_tab *back_dir_cmd[8];
3815 const struct ext_func_tab *cmdtmp;
3816 boolean flagtemp;
3817 int c, i, updated = 0;
3818 static boolean backed_dir_cmd = FALSE;
3820 if (initial) {
3821 updated = 1;
3822 Cmd.num_pad = FALSE;
3823 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3824 for (i = 0; i < SIZE(spkeys_binds); i++)
3825 Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key;
3826 commands_init();
3827 } else {
3829 if (backed_dir_cmd) {
3830 for (i = 0; i < 8; i++) {
3831 Cmd.commands[(uchar) Cmd.dirchars[i]] = back_dir_cmd[i];
3835 /* basic num_pad */
3836 flagtemp = iflags.num_pad;
3837 if (flagtemp != Cmd.num_pad) {
3838 Cmd.num_pad = flagtemp;
3839 ++updated;
3841 /* swap_yz mode (only applicable for !num_pad); intended for
3842 QWERTZ keyboard used in Central Europe, particularly Germany */
3843 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3844 if (flagtemp != Cmd.swap_yz) {
3845 Cmd.swap_yz = flagtemp;
3846 ++updated;
3847 /* Cmd.swap_yz has been toggled;
3848 perform the swap (or reverse previous one) */
3849 for (i = 0; i < SIZE(ylist); i++) {
3850 c = ylist[i] & 0xff;
3851 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3852 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3853 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3856 /* MSDOS compatibility mode (only applicable for num_pad) */
3857 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3858 if (flagtemp != Cmd.pcHack_compat) {
3859 Cmd.pcHack_compat = flagtemp;
3860 ++updated;
3861 /* pcHack_compat has been toggled */
3862 c = M('5') & 0xff;
3863 cmdtmp = Cmd.commands['5'];
3864 Cmd.commands['5'] = Cmd.commands[c];
3865 Cmd.commands[c] = cmdtmp;
3866 c = M('0') & 0xff;
3867 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3869 /* phone keypad layout (only applicable for num_pad) */
3870 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3871 if (flagtemp != Cmd.phone_layout) {
3872 Cmd.phone_layout = flagtemp;
3873 ++updated;
3874 /* phone_layout has been toggled */
3875 for (i = 0; i < 3; i++) {
3876 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3877 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3878 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3879 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3880 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3881 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3882 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3883 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3886 } /*?initial*/
3888 if (updated)
3889 Cmd.serialno++;
3890 Cmd.dirchars = !Cmd.num_pad
3891 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3892 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3893 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3895 Cmd.move_W = Cmd.dirchars[0];
3896 Cmd.move_NW = Cmd.dirchars[1];
3897 Cmd.move_N = Cmd.dirchars[2];
3898 Cmd.move_NE = Cmd.dirchars[3];
3899 Cmd.move_E = Cmd.dirchars[4];
3900 Cmd.move_SE = Cmd.dirchars[5];
3901 Cmd.move_S = Cmd.dirchars[6];
3902 Cmd.move_SW = Cmd.dirchars[7];
3904 if (!initial) {
3905 for (i = 0; i < 8; i++) {
3906 back_dir_cmd[i] =
3907 (struct ext_func_tab *) Cmd.commands[(uchar) Cmd.dirchars[i]];
3908 Cmd.commands[(uchar) Cmd.dirchars[i]] = (struct ext_func_tab *) 0;
3910 backed_dir_cmd = TRUE;
3911 for (i = 0; i < 8; i++)
3912 bind_key(Cmd.dirchars[i], "nothing");
3916 /* non-movement commands which accept 'm' prefix to request menu operation */
3917 STATIC_OVL boolean
3918 accept_menu_prefix(cmd_func)
3919 int NDECL((*cmd_func));
3921 if (cmd_func == dopickup || cmd_func == dotip
3922 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
3923 an item on floor or in invent; 'm' skips picking from floor
3924 (ie, inventory only) rather than request use of menu operation */
3925 || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
3926 /* 'm' for removing saddle from adjacent monster without checking
3927 for containers at <u.ux,u.uy> */
3928 || cmd_func == doloot
3929 /* travel: pop up a menu of interesting targets in view */
3930 || cmd_func == dotravel
3931 /* 'm' prefix allowed for some extended commands */
3932 || cmd_func == doextcmd || cmd_func == doextlist)
3933 return TRUE;
3934 return FALSE;
3938 ch2spkeys(c, start,end)
3939 char c;
3940 int start,end;
3942 int i;
3943 for (i = start; i <= end; i++)
3944 if (Cmd.spkeys[i] == c)
3945 return i;
3946 return NHKF_ESC;
3949 void
3950 rhack(cmd)
3951 register char *cmd;
3953 boolean do_walk, do_rush, prefix_seen, bad_command,
3954 firsttime = (cmd == 0);
3956 iflags.menu_requested = FALSE;
3957 #ifdef SAFERHANGUP
3958 if (program_state.done_hup)
3959 end_of_input();
3960 #endif
3961 if (firsttime) {
3962 context.nopick = 0;
3963 cmd = parse();
3965 if (*cmd == Cmd.spkeys[NHKF_ESC]) {
3966 context.move = FALSE;
3967 return;
3969 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3970 in_doagain = TRUE;
3971 stail = 0;
3972 rhack((char *) 0); /* read and execute command */
3973 in_doagain = FALSE;
3974 return;
3976 /* Special case of *cmd == ' ' handled better below */
3977 if (!*cmd || *cmd == (char) 0377) {
3978 nhbell();
3979 context.move = FALSE;
3980 return; /* probably we just had an interrupt */
3983 /* handle most movement commands */
3984 do_walk = do_rush = prefix_seen = FALSE;
3985 context.travel = context.travel1 = 0;
3986 switch (ch2spkeys(*cmd, NHKF_RUN,NHKF_CLICKLOOK)) {
3987 case NHKF_RUSH:
3988 if (movecmd(cmd[1])) {
3989 context.run = 2;
3990 do_rush = TRUE;
3991 } else
3992 prefix_seen = TRUE;
3993 break;
3994 case NHKF_RUN2:
3995 if (!Cmd.num_pad)
3996 break; /* else FALLTHRU */
3997 case NHKF_RUN:
3998 if (movecmd(lowc(cmd[1]))) {
3999 context.run = 3;
4000 do_rush = TRUE;
4001 } else
4002 prefix_seen = TRUE;
4003 break;
4004 case NHKF_FIGHT2:
4005 if (!Cmd.num_pad)
4006 break; /* else FALLTHRU */
4007 /* Effects of movement commands and invisible monsters:
4008 * m: always move onto space (even if 'I' remembered)
4009 * F: always attack space (even if 'I' not remembered)
4010 * normal movement: attack if 'I', move otherwise.
4012 case NHKF_FIGHT:
4013 if (movecmd(cmd[1])) {
4014 context.forcefight = 1;
4015 do_walk = TRUE;
4016 } else
4017 prefix_seen = TRUE;
4018 break;
4019 case NHKF_NOPICKUP:
4020 if (movecmd(cmd[1]) || u.dz) {
4021 context.run = 0;
4022 context.nopick = 1;
4023 if (!u.dz)
4024 do_walk = TRUE;
4025 else
4026 cmd[0] = cmd[1]; /* "m<" or "m>" */
4027 } else
4028 prefix_seen = TRUE;
4029 break;
4030 case NHKF_RUN_NOPICKUP:
4031 if (movecmd(lowc(cmd[1]))) {
4032 context.run = 1;
4033 context.nopick = 1;
4034 do_rush = TRUE;
4035 } else
4036 prefix_seen = TRUE;
4037 break;
4038 case NHKF_DOINV:
4039 if (!Cmd.num_pad)
4040 break;
4041 (void) ddoinv(); /* a convenience borrowed from the PC */
4042 context.move = FALSE;
4043 multi = 0;
4044 return;
4045 case NHKF_CLICKLOOK:
4046 if (iflags.clicklook) {
4047 context.move = FALSE;
4048 do_look(2, &clicklook_cc);
4050 return;
4051 case NHKF_TRAVEL:
4052 if (flags.travelcmd) {
4053 context.travel = 1;
4054 context.travel1 = 1;
4055 context.run = 8;
4056 context.nopick = 1;
4057 do_rush = TRUE;
4058 break;
4060 /*FALLTHRU*/
4061 default:
4062 if (movecmd(*cmd)) { /* ordinary movement */
4063 context.run = 0; /* only matters here if it was 8 */
4064 do_walk = TRUE;
4065 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
4066 context.run = 1;
4067 do_rush = TRUE;
4068 } else if (movecmd(unctrl(*cmd))) {
4069 context.run = 3;
4070 do_rush = TRUE;
4072 break;
4075 /* some special prefix handling */
4076 /* overload 'm' prefix to mean "request a menu" */
4077 if (prefix_seen && cmd[0] == Cmd.spkeys[NHKF_REQMENU]) {
4078 /* (for func_tab cast, see below) */
4079 const struct ext_func_tab *ft = Cmd.commands[cmd[1] & 0xff];
4080 int NDECL((*func)) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0;
4082 if (func && accept_menu_prefix(func)) {
4083 iflags.menu_requested = TRUE;
4084 ++cmd;
4088 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
4089 /* trying to move diagonally as a grid bug;
4090 this used to be treated by movecmd() as not being
4091 a movement attempt, but that didn't provide for any
4092 feedback and led to strangeness if the key pressed
4093 ('u' in particular) was overloaded for num_pad use */
4094 You_cant("get there from here...");
4095 context.run = 0;
4096 context.nopick = context.forcefight = FALSE;
4097 context.move = context.mv = FALSE;
4098 multi = 0;
4099 return;
4102 if (do_walk) {
4103 if (multi)
4104 context.mv = TRUE;
4105 domove();
4106 context.forcefight = 0;
4107 return;
4108 } else if (do_rush) {
4109 if (firsttime) {
4110 if (!multi)
4111 multi = max(COLNO, ROWNO);
4112 u.last_str_turn = 0;
4114 context.mv = TRUE;
4115 domove();
4116 return;
4117 } else if (prefix_seen && cmd[1] == Cmd.spkeys[NHKF_ESC]) {
4118 /* <prefix><escape> */
4119 /* don't report "unknown command" for change of heart... */
4120 bad_command = FALSE;
4121 } else if (*cmd == ' ' && !flags.rest_on_space) {
4122 bad_command = TRUE; /* skip cmdlist[] loop */
4124 /* handle all other commands */
4125 } else {
4126 register const struct ext_func_tab *tlist;
4127 int res, NDECL((*func));
4129 /* current - use *cmd to directly index cmdlist array */
4130 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
4131 if (!wizard && (tlist->flags & WIZMODECMD)) {
4132 You_cant("do that!");
4133 res = 0;
4134 } else if (u.uburied && !(tlist->flags & IFBURIED)) {
4135 You_cant("do that while you are buried!");
4136 res = 0;
4137 } else {
4138 /* we discard 'const' because some compilers seem to have
4139 trouble with the pointer passed to set_occupation() */
4140 func = ((struct ext_func_tab *) tlist)->ef_funct;
4141 if (tlist->f_text && !occupation && multi)
4142 set_occupation(func, tlist->f_text, multi);
4143 res = (*func)(); /* perform the command */
4145 if (!res) {
4146 context.move = FALSE;
4147 multi = 0;
4149 return;
4151 /* if we reach here, cmd wasn't found in cmdlist[] */
4152 bad_command = TRUE;
4155 if (bad_command) {
4156 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
4157 register char c;
4159 expcmd[0] = '\0';
4160 while ((c = *cmd++) != '\0')
4161 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
4163 if (!prefix_seen || !iflags.cmdassist
4164 || !help_dir(0, "Invalid direction key!"))
4165 Norep("Unknown command '%s'.", expcmd);
4167 /* didn't move */
4168 context.move = FALSE;
4169 multi = 0;
4170 return;
4173 /* convert an x,y pair into a direction code */
4175 xytod(x, y)
4176 schar x, y;
4178 register int dd;
4180 for (dd = 0; dd < 8; dd++)
4181 if (x == xdir[dd] && y == ydir[dd])
4182 return dd;
4183 return -1;
4186 /* convert a direction code into an x,y pair */
4187 void
4188 dtoxy(cc, dd)
4189 coord *cc;
4190 register int dd;
4192 cc->x = xdir[dd];
4193 cc->y = ydir[dd];
4194 return;
4197 /* also sets u.dz, but returns false for <> */
4199 movecmd(sym)
4200 char sym;
4202 register const char *dp = index(Cmd.dirchars, sym);
4204 u.dz = 0;
4205 if (!dp || !*dp)
4206 return 0;
4207 u.dx = xdir[dp - Cmd.dirchars];
4208 u.dy = ydir[dp - Cmd.dirchars];
4209 u.dz = zdir[dp - Cmd.dirchars];
4210 #if 0 /* now handled elsewhere */
4211 if (u.dx && u.dy && NODIAG(u.umonnum)) {
4212 u.dx = u.dy = 0;
4213 return 0;
4215 #endif
4216 return !u.dz;
4219 /* grid bug handling which used to be in movecmd() */
4221 dxdy_moveok()
4223 if (u.dx && u.dy && NODIAG(u.umonnum))
4224 u.dx = u.dy = 0;
4225 return u.dx || u.dy;
4228 /* decide whether a character (user input keystroke) requests screen repaint */
4229 boolean
4230 redraw_cmd(c)
4231 char c;
4233 return (boolean) (c == Cmd.spkeys[NHKF_REDRAW]
4234 || (Cmd.num_pad && c == Cmd.spkeys[NHKF_REDRAW2]));
4237 boolean
4238 prefix_cmd(c)
4239 char c;
4241 return (c == Cmd.spkeys[NHKF_RUSH]
4242 || c == Cmd.spkeys[NHKF_RUN]
4243 || c == Cmd.spkeys[NHKF_NOPICKUP]
4244 || c == Cmd.spkeys[NHKF_RUN_NOPICKUP]
4245 || c == Cmd.spkeys[NHKF_FIGHT]
4246 || (Cmd.num_pad && (c == Cmd.spkeys[NHKF_RUN2]
4247 || c == Cmd.spkeys[NHKF_FIGHT2])));
4251 * uses getdir() but unlike getdir() it specifically
4252 * produces coordinates using the direction from getdir()
4253 * and verifies that those coordinates are ok.
4255 * If the call to getdir() returns 0, Never_mind is displayed.
4256 * If the resulting coordinates are not okay, emsg is displayed.
4258 * Returns non-zero if coordinates in cc are valid.
4261 get_adjacent_loc(prompt, emsg, x, y, cc)
4262 const char *prompt, *emsg;
4263 xchar x, y;
4264 coord *cc;
4266 xchar new_x, new_y;
4267 if (!getdir(prompt)) {
4268 pline1(Never_mind);
4269 return 0;
4271 new_x = x + u.dx;
4272 new_y = y + u.dy;
4273 if (cc && isok(new_x, new_y)) {
4274 cc->x = new_x;
4275 cc->y = new_y;
4276 } else {
4277 if (emsg)
4278 pline1(emsg);
4279 return 0;
4281 return 1;
4285 getdir(s)
4286 const char *s;
4288 char dirsym;
4289 int is_mov;
4291 retry:
4292 if (in_doagain || *readchar_queue)
4293 dirsym = readchar();
4294 else
4295 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
4296 (char *) 0, '\0');
4297 /* remove the prompt string so caller won't have to */
4298 clear_nhwindow(WIN_MESSAGE);
4300 if (redraw_cmd(dirsym)) { /* ^R */
4301 docrt(); /* redraw */
4302 goto retry;
4304 savech(dirsym);
4306 if (dirsym == Cmd.spkeys[NHKF_GETDIR_SELF]
4307 || dirsym == Cmd.spkeys[NHKF_GETDIR_SELF2]) {
4308 u.dx = u.dy = u.dz = 0;
4309 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
4310 boolean did_help = FALSE, help_requested;
4312 if (!index(quitchars, dirsym)) {
4313 help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]);
4314 if (help_requested || iflags.cmdassist) {
4315 did_help = help_dir((s && *s == '^') ? dirsym : 0,
4316 help_requested ? (const char *) 0
4317 : "Invalid direction key!");
4318 if (help_requested)
4319 goto retry;
4321 if (!did_help)
4322 pline("What a strange direction!");
4324 return 0;
4325 } else if (is_mov && !dxdy_moveok()) {
4326 You_cant("orient yourself that direction.");
4327 return 0;
4329 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
4330 confdir();
4331 return 1;
4334 STATIC_OVL void
4335 show_direction_keys(win, nodiag)
4336 winid win;
4337 boolean nodiag;
4339 char buf[BUFSZ];
4341 if (nodiag) {
4342 Sprintf(buf, " %c ", Cmd.move_N);
4343 putstr(win, 0, buf);
4344 putstr(win, 0, " | ");
4345 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4346 putstr(win, 0, buf);
4347 putstr(win, 0, " | ");
4348 Sprintf(buf, " %c ", Cmd.move_S);
4349 putstr(win, 0, buf);
4350 } else {
4351 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
4352 Cmd.move_NE);
4353 putstr(win, 0, buf);
4354 putstr(win, 0, " \\ | / ");
4355 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4356 putstr(win, 0, buf);
4357 putstr(win, 0, " / | \\ ");
4358 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
4359 Cmd.move_SE);
4360 putstr(win, 0, buf);
4364 STATIC_OVL boolean
4365 help_dir(sym, msg)
4366 char sym;
4367 const char *msg;
4369 static const char wiz_only_list[] = "EFGIVW";
4370 char ctrl;
4371 winid win;
4372 char buf[BUFSZ], buf2[BUFSZ], *explain;
4374 win = create_nhwindow(NHW_TEXT);
4375 if (!win)
4376 return FALSE;
4377 if (msg) {
4378 Sprintf(buf, "cmdassist: %s", msg);
4379 putstr(win, 0, buf);
4380 putstr(win, 0, "");
4382 if (letter(sym) || sym == '[') { /* 'dat/cmdhelp' shows ESC as ^[ */
4383 sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */
4384 ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
4385 if ((explain = dowhatdoes_core(ctrl, buf2)) != 0
4386 && (!index(wiz_only_list, sym) || wizard)) {
4387 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
4388 index(wiz_only_list, sym)
4389 ? ""
4390 : " as specified in the Guidebook");
4391 putstr(win, 0, buf);
4392 putstr(win, 0, "");
4393 putstr(win, 0, explain);
4394 putstr(win, 0, "");
4395 putstr(win, 0,
4396 "To use that command, hold down the <Ctrl> key as a shift");
4397 Sprintf(buf, "and press the <%c> key.", sym);
4398 putstr(win, 0, buf);
4399 putstr(win, 0, "");
4403 Sprintf(buf, "Valid direction keys %sare:",
4404 NODIAG(u.umonnum) ? "in your current form " : "");
4405 putstr(win, 0, buf);
4406 show_direction_keys(win, NODIAG(u.umonnum));
4408 putstr(win, 0, "");
4409 putstr(win, 0, " < up");
4410 putstr(win, 0, " > down");
4411 Sprintf(buf, " %4s direct at yourself",
4412 visctrl(Cmd.spkeys[NHKF_GETDIR_SELF]));
4413 putstr(win, 0, buf);
4414 if (msg) {
4415 /* non-null msg means that this wasn't an explicit user request */
4416 putstr(win, 0, "");
4417 putstr(win, 0,
4418 "(Suppress this message with !cmdassist in config file.)");
4420 display_nhwindow(win, FALSE);
4421 destroy_nhwindow(win);
4422 return TRUE;
4425 void
4426 confdir()
4428 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
4430 u.dx = xdir[x];
4431 u.dy = ydir[x];
4432 return;
4435 const char *
4436 directionname(dir)
4437 int dir;
4439 static NEARDATA const char *const dirnames[] = {
4440 "west", "northwest", "north", "northeast", "east",
4441 "southeast", "south", "southwest", "down", "up",
4444 if (dir < 0 || dir >= SIZE(dirnames))
4445 return "invalid";
4446 return dirnames[dir];
4450 isok(x, y)
4451 register int x, y;
4453 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
4454 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
4457 static NEARDATA int last_multi;
4460 * convert a MAP window position into a movecmd
4462 const char *
4463 click_to_cmd(x, y, mod)
4464 int x, y, mod;
4466 int dir;
4467 static char cmd[4];
4468 cmd[1] = 0;
4470 if (iflags.clicklook && mod == CLICK_2) {
4471 clicklook_cc.x = x;
4472 clicklook_cc.y = y;
4473 cmd[0] = Cmd.spkeys[NHKF_CLICKLOOK];
4474 return cmd;
4477 x -= u.ux;
4478 y -= u.uy;
4480 if (flags.travelcmd) {
4481 if (abs(x) <= 1 && abs(y) <= 1) {
4482 x = sgn(x), y = sgn(y);
4483 } else {
4484 u.tx = u.ux + x;
4485 u.ty = u.uy + y;
4486 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4487 return cmd;
4490 if (x == 0 && y == 0) {
4491 /* here */
4492 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
4493 || IS_SINK(levl[u.ux][u.uy].typ)) {
4494 cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip);
4495 return cmd;
4496 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
4497 cmd[0] = cmd_from_func(dosit);
4498 return cmd;
4499 } else if ((u.ux == xupstair && u.uy == yupstair)
4500 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4501 && sstairs.up)
4502 || (u.ux == xupladder && u.uy == yupladder)) {
4503 cmd[0] = cmd_from_func(doup);
4504 return cmd;
4505 } else if ((u.ux == xdnstair && u.uy == ydnstair)
4506 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4507 && !sstairs.up)
4508 || (u.ux == xdnladder && u.uy == ydnladder)) {
4509 cmd[0] = cmd_from_func(dodown);
4510 return cmd;
4511 } else if (OBJ_AT(u.ux, u.uy)) {
4512 cmd[0] = cmd_from_func(Is_container(level.objects[u.ux][u.uy])
4513 ? doloot : dopickup);
4514 return cmd;
4515 } else {
4516 cmd[0] = cmd_from_func(donull); /* just rest */
4517 return cmd;
4521 /* directional commands */
4523 dir = xytod(x, y);
4525 if (!m_at(u.ux + x, u.uy + y)
4526 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
4527 cmd[1] = Cmd.dirchars[dir];
4528 cmd[2] = '\0';
4529 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
4530 /* slight assistance to the player: choose kick/open for them
4532 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
4533 cmd[0] = cmd_from_func(dokick);
4534 return cmd;
4536 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
4537 cmd[0] = cmd_from_func(doopen);
4538 return cmd;
4541 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
4542 cmd[0] = cmd_from_func(dosearch);
4543 cmd[1] = 0;
4544 return cmd;
4547 } else {
4548 /* convert without using floating point, allowing sloppy clicking */
4549 if (x > 2 * abs(y))
4550 x = 1, y = 0;
4551 else if (y > 2 * abs(x))
4552 x = 0, y = 1;
4553 else if (x < -2 * abs(y))
4554 x = -1, y = 0;
4555 else if (y < -2 * abs(x))
4556 x = 0, y = -1;
4557 else
4558 x = sgn(x), y = sgn(y);
4560 if (x == 0 && y == 0) {
4561 /* map click on player to "rest" command */
4562 cmd[0] = cmd_from_func(donull);
4563 return cmd;
4565 dir = xytod(x, y);
4568 /* move, attack, etc. */
4569 cmd[1] = 0;
4570 if (mod == CLICK_1) {
4571 cmd[0] = Cmd.dirchars[dir];
4572 } else {
4573 cmd[0] = (Cmd.num_pad
4574 ? M(Cmd.dirchars[dir])
4575 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
4578 return cmd;
4581 char
4582 get_count(allowchars, inkey, maxcount, count)
4583 char *allowchars;
4584 char inkey;
4585 long maxcount;
4586 long *count;
4588 char qbuf[QBUFSZ];
4589 int key;
4590 long cnt = 0L;
4591 boolean backspaced = FALSE;
4592 /* this should be done in port code so that we have erase_char
4593 and kill_char available; we can at least fake erase_char */
4594 #define STANDBY_erase_char '\177'
4596 for (;;) {
4597 if (inkey) {
4598 key = inkey;
4599 inkey = '\0';
4600 } else
4601 key = readchar();
4603 if (digit(key)) {
4604 cnt = 10L * cnt + (long) (key - '0');
4605 if (cnt < 0)
4606 cnt = 0;
4607 else if (maxcount > 0 && cnt > maxcount)
4608 cnt = maxcount;
4609 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
4610 cnt = cnt / 10;
4611 backspaced = TRUE;
4612 } else if (key == Cmd.spkeys[NHKF_ESC]) {
4613 break;
4614 } else if (!allowchars || index(allowchars, key)) {
4615 *count = cnt;
4616 break;
4619 if (cnt > 9 || backspaced) {
4620 clear_nhwindow(WIN_MESSAGE);
4621 if (backspaced && !cnt) {
4622 Sprintf(qbuf, "Count: ");
4623 } else {
4624 Sprintf(qbuf, "Count: %ld", cnt);
4625 backspaced = FALSE;
4627 pline1(qbuf);
4628 mark_synch();
4631 return key;
4635 STATIC_OVL char *
4636 parse()
4638 #ifdef LINT /* static char in_line[COLNO]; */
4639 char in_line[COLNO];
4640 #else
4641 static char in_line[COLNO];
4642 #endif
4643 register int foo;
4644 boolean prezero = FALSE;
4646 multi = 0;
4647 context.move = 1;
4648 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
4650 #ifdef ALTMETA
4651 alt_esc = iflags.altmeta; /* readchar() hack */
4652 #endif
4653 if (!Cmd.num_pad || (foo = readchar()) == Cmd.spkeys[NHKF_COUNT]) {
4654 long tmpmulti = multi;
4656 foo = get_count((char *) 0, '\0', LARGEST_INT, &tmpmulti);
4657 last_multi = multi = tmpmulti;
4659 #ifdef ALTMETA
4660 alt_esc = FALSE; /* readchar() reset */
4661 #endif
4663 if (foo == Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */
4664 clear_nhwindow(WIN_MESSAGE);
4665 multi = last_multi = 0;
4666 } else if (foo == Cmd.spkeys[NHKF_DOAGAIN] || in_doagain) {
4667 multi = last_multi;
4668 } else {
4669 last_multi = multi;
4670 savech(0); /* reset input queue */
4671 savech((char) foo);
4674 if (multi) {
4675 multi--;
4676 save_cm = in_line;
4677 } else {
4678 save_cm = (char *) 0;
4680 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
4681 if (Cmd.pcHack_compat) {
4682 /* This handles very old inconsistent DOS/Windows behaviour
4683 in a different way: earlier, the keyboard handler mapped
4684 these, which caused counts to be strange when entered
4685 from the number pad. Now do not map them until here. */
4686 switch (foo) {
4687 case '5':
4688 foo = Cmd.spkeys[NHKF_RUSH];
4689 break;
4690 case M('5'):
4691 foo = Cmd.spkeys[NHKF_RUN];
4692 break;
4693 case M('0'):
4694 foo = Cmd.spkeys[NHKF_DOINV];
4695 break;
4696 default:
4697 break; /* as is */
4701 in_line[0] = foo;
4702 in_line[1] = '\0';
4703 if (prefix_cmd(foo)) {
4704 foo = readchar();
4705 savech((char) foo);
4706 in_line[1] = foo;
4707 in_line[2] = 0;
4709 clear_nhwindow(WIN_MESSAGE);
4710 if (prezero)
4711 in_line[0] = Cmd.spkeys[NHKF_ESC];
4712 return in_line;
4715 #ifdef HANGUPHANDLING
4716 /* some very old systems, or descendents of such systems, expect signal
4717 handlers to have return type `int', but they don't actually inspect
4718 the return value so we should be safe using `void' unconditionally */
4719 /*ARGUSED*/
4720 void
4721 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4722 int sig_unused UNUSED;
4724 if (program_state.exiting)
4725 program_state.in_moveloop = 0;
4726 nhwindows_hangup();
4727 #ifdef SAFERHANGUP
4728 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4729 and a couple of other places; actual hangup handling occurs then.
4730 This is 'safer' because it disallows certain cheats and also
4731 protects against losing objects in the process of being thrown,
4732 but also potentially riskier because the disconnected program
4733 must continue running longer before attempting a hangup save. */
4734 program_state.done_hup++;
4735 /* defer hangup iff game appears to be in progress */
4736 if (program_state.in_moveloop && program_state.something_worth_saving)
4737 return;
4738 #endif /* SAFERHANGUP */
4739 end_of_input();
4742 void
4743 end_of_input()
4745 #ifdef NOSAVEONHANGUP
4746 #ifdef INSURANCE
4747 if (flags.ins_chkpt && program_state.something_worth_saving)
4748 program_statue.preserve_locks = 1; /* keep files for recovery */
4749 #endif
4750 program_state.something_worth_saving = 0; /* don't save */
4751 #endif
4753 #ifndef SAFERHANGUP
4754 if (!program_state.done_hup++)
4755 #endif
4756 if (program_state.something_worth_saving)
4757 (void) dosave0();
4758 if (iflags.window_inited)
4759 exit_nhwindows((char *) 0);
4760 clearlocks();
4761 terminate(EXIT_SUCCESS);
4762 /*NOTREACHED*/ /* not necessarily true for vms... */
4763 return;
4765 #endif /* HANGUPHANDLING */
4767 char
4768 readchar()
4770 register int sym;
4771 int x = u.ux, y = u.uy, mod = 0;
4773 if (*readchar_queue)
4774 sym = *readchar_queue++;
4775 else
4776 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4778 #ifdef NR_OF_EOFS
4779 if (sym == EOF) {
4780 register int cnt = NR_OF_EOFS;
4782 * Some SYSV systems seem to return EOFs for various reasons
4783 * (?like when one hits break or for interrupted systemcalls?),
4784 * and we must see several before we quit.
4786 do {
4787 clearerr(stdin); /* omit if clearerr is undefined */
4788 sym = pgetchar();
4789 } while (--cnt && sym == EOF);
4791 #endif /* NR_OF_EOFS */
4793 if (sym == EOF) {
4794 #ifdef HANGUPHANDLING
4795 hangup(0); /* call end_of_input() or set program_state.done_hup */
4796 #endif
4797 sym = '\033';
4798 #ifdef ALTMETA
4799 } else if (sym == '\033' && alt_esc) {
4800 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4801 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4802 if (sym == EOF || sym == 0)
4803 sym = '\033';
4804 else if (sym != '\033')
4805 sym |= 0200; /* force 8th bit on */
4806 #endif /*ALTMETA*/
4807 } else if (sym == 0) {
4808 /* click event */
4809 readchar_queue = click_to_cmd(x, y, mod);
4810 sym = *readchar_queue++;
4812 return (char) sym;
4815 STATIC_PTR int
4816 dotravel(VOID_ARGS)
4818 /* Keyboard travel command */
4819 static char cmd[2];
4820 coord cc;
4822 if (!flags.travelcmd)
4823 return 0;
4824 cmd[1] = 0;
4825 cc.x = iflags.travelcc.x;
4826 cc.y = iflags.travelcc.y;
4827 if (cc.x == -1 && cc.y == -1) {
4828 /* No cached destination, start attempt from current position */
4829 cc.x = u.ux;
4830 cc.y = u.uy;
4832 iflags.getloc_travelmode = TRUE;
4833 if (iflags.menu_requested) {
4834 if (!getpos_menu(&cc, TRUE, GLOC_INTERESTING)) {
4835 iflags.getloc_travelmode = FALSE;
4836 return 0;
4838 } else {
4839 pline("Where do you want to travel to?");
4840 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4841 /* user pressed ESC */
4842 iflags.getloc_travelmode = FALSE;
4843 return 0;
4846 iflags.getloc_travelmode = FALSE;
4847 iflags.travelcc.x = u.tx = cc.x;
4848 iflags.travelcc.y = u.ty = cc.y;
4849 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4850 readchar_queue = cmd;
4851 return 0;
4854 #ifdef PORT_DEBUG
4855 extern void NDECL(win32con_debug_keystrokes);
4856 extern void NDECL(win32con_handler_info);
4859 wiz_port_debug()
4861 int n, k;
4862 winid win;
4863 anything any;
4864 int item = 'a';
4865 int num_menu_selections;
4866 struct menu_selection_struct {
4867 char *menutext;
4868 void NDECL((*fn));
4869 } menu_selections[] = {
4870 #ifdef WIN32
4871 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4872 { "show keystroke handler information (tty only)",
4873 win32con_handler_info },
4874 #endif
4875 { (char *) 0, (void NDECL((*))) 0 } /* array terminator */
4878 num_menu_selections = SIZE(menu_selections) - 1;
4879 if (num_menu_selections > 0) {
4880 menu_item *pick_list;
4881 win = create_nhwindow(NHW_MENU);
4882 start_menu(win);
4883 for (k = 0; k < num_menu_selections; ++k) {
4884 any.a_int = k + 1;
4885 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4886 menu_selections[k].menutext, MENU_UNSELECTED);
4888 end_menu(win, "Which port debugging feature?");
4889 n = select_menu(win, PICK_ONE, &pick_list);
4890 destroy_nhwindow(win);
4891 if (n > 0) {
4892 n = pick_list[0].item.a_int - 1;
4893 free((genericptr_t) pick_list);
4894 /* execute the function */
4895 (*menu_selections[n].fn)();
4897 } else
4898 pline("No port-specific debug capability defined.");
4899 return 0;
4901 #endif /*PORT_DEBUG*/
4904 * Parameter validator for generic yes/no function to prevent
4905 * the core from sending too long a prompt string to the
4906 * window port causing a buffer overflow there.
4908 char
4909 yn_function(query, resp, def)
4910 const char *query, *resp;
4911 char def;
4913 char qbuf[QBUFSZ];
4915 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4917 /* maximum acceptable length is QBUFSZ-1 */
4918 if (strlen(query) >= QBUFSZ) {
4919 /* caller shouldn't have passed anything this long */
4920 paniclog("Query truncated: ", query);
4921 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4922 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4923 query = qbuf;
4925 return (*windowprocs.win_yn_function)(query, resp, def);
4928 /* for paranoid_confirm:quit,die,attack prompting */
4929 boolean
4930 paranoid_query(be_paranoid, prompt)
4931 boolean be_paranoid;
4932 const char *prompt;
4934 boolean confirmed_ok;
4936 /* when paranoid, player must respond with "yes" rather than just 'y'
4937 to give the go-ahead for this query; default is "no" unless the
4938 ParanoidConfirm flag is set in which case there's no default */
4939 if (be_paranoid) {
4940 char qbuf[QBUFSZ], ans[BUFSZ];
4941 const char *promptprefix = "", *responsetype = ParanoidConfirm
4942 ? "(yes|no)"
4943 : "(yes) [no]";
4944 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4946 /* in addition to being paranoid about this particular
4947 query, we might be even more paranoid about all paranoia
4948 responses (ie, ParanoidConfirm is set) in which case we
4949 require "no" to reject in addition to "yes" to confirm
4950 (except we won't loop if response is ESC; it means no) */
4951 do {
4952 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4953 getlin(qbuf, ans);
4954 (void) mungspaces(ans);
4955 confirmed_ok = !strcmpi(ans, "yes");
4956 if (confirmed_ok || *ans == '\033')
4957 break;
4958 promptprefix = "\"Yes\" or \"No\": ";
4959 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4960 } else
4961 confirmed_ok = (yn(prompt) == 'y');
4963 return confirmed_ok;
4967 dosuspend_core()
4969 #ifdef SUSPEND
4970 /* Does current window system support suspend? */
4971 if ((*windowprocs.win_can_suspend)()) {
4972 /* NB: SYSCF SHELLERS handled in port code. */
4973 dosuspend();
4974 } else
4975 #endif
4976 Norep("Suspend command not available.");
4977 return 0;
4980 /*cmd.c*/