NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / cmd.c
blob25c358367b7416c0e9df9599533a906c0901d3c7
1 /* aNetHack 0.0.1 cmd.c $ANH-Date: 1457207033 2016/03/05 19:43:53 $ $ANH-Branch: chasonr $:$ANH-Revision: 1.220 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack 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
2741 #ifndef M
2742 #ifndef NHSTDC
2743 #define M(c) (0x80 | (c))
2744 #else
2745 #define M(c) ((c) - 128)
2746 #endif /* NHSTDC */
2747 #endif
2749 #ifndef C
2750 #define C(c) (0x1f & (c))
2751 #endif
2753 /* ordered by command name */
2754 struct ext_func_tab extcmdlist[] = {
2755 { '#', "#", "perform an extended command",
2756 doextcmd, IFBURIED | GENERALCMD },
2757 { M('?'), "?", "get this list of extended commands",
2758 doextlist, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2759 { M('a'), "adjust", "adjust inventory letters",
2760 doorganize, IFBURIED | AUTOCOMPLETE },
2761 { M('A'), "annotate", "name current level",
2762 donamelevel, IFBURIED | AUTOCOMPLETE },
2763 { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)",
2764 doapply },
2765 { C('x'), "attributes", "show your attributes",
2766 doattributes, IFBURIED },
2767 { '@', "autopickup", "toggle the pickup option on/off",
2768 dotogglepickup, IFBURIED },
2769 { 'C', "call", "call (name) something", docallcmd, IFBURIED },
2770 { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED },
2771 { M('c'), "chat", "talk to someone", dotalk, IFBURIED | AUTOCOMPLETE },
2772 { 'c', "close", "close a door", doclose },
2773 { M('C'), "conduct", "list voluntary challenges you have maintained",
2774 doconduct, IFBURIED | AUTOCOMPLETE },
2775 { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE },
2776 { '>', "down", "go down a staircase", dodown },
2777 { 'd', "drop", "drop an item", dodrop },
2778 { 'D', "droptype", "drop specific item types", doddrop },
2779 { 'e', "eat", "eat something", doeat },
2780 { 'E', "engrave", "engrave writing on the floor", doengrave },
2781 { M('e'), "enhance", "advance or check weapon and spell skills",
2782 enhance_weapon_skill, IFBURIED | AUTOCOMPLETE },
2783 { '\0', "exploremode", "enter explore (discovery) mode",
2784 enter_explore_mode, IFBURIED },
2785 { 'f', "fire", "fire ammunition from quiver", dofire },
2786 { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE },
2787 { ';', "glance", "show what type of thing a map symbol corresponds to",
2788 doquickwhatis, IFBURIED | GENERALCMD },
2789 { '?', "help", "give a help message", dohelp, IFBURIED | GENERALCMD },
2790 { 'V', "history", "show long version and game history",
2791 dohistory, IFBURIED | GENERALCMD },
2792 { 'i', "inventory", "show your inventory", ddoinv, IFBURIED },
2793 { 'I', "inventtype", "inventory specific item types",
2794 dotypeinv, IFBURIED },
2795 { M('i'), "invoke", "invoke an object's special powers",
2796 doinvoke, IFBURIED | AUTOCOMPLETE },
2797 { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE },
2798 { C('d'), "kick", "kick something", dokick },
2799 { '\\', "known", "show what object types have been discovered",
2800 dodiscovered, IFBURIED | GENERALCMD },
2801 { '`', "knownclass", "show discovered types for one class of objects",
2802 doclassdisco, IFBURIED|GENERALCMD },
2803 { '\0', "levelchange", "change experience level",
2804 wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2805 { '\0', "lightsources", "show mobile light sources",
2806 wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2807 { ':', "look", "look at what is here", dolook, IFBURIED },
2808 { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE },
2809 #ifdef DEBUG_MIGRATING_MONS
2810 { '\0', "migratemons", "migrate N random monsters",
2811 wiz_migrate_mons, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2812 #endif
2813 { '\0', "monpolycontrol", "control monster polymorphs",
2814 wiz_mon_polycontrol, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2815 { M('m'), "monster", "use monster's special ability",
2816 domonability, IFBURIED | AUTOCOMPLETE },
2817 { 'N', "name", "name a monster or an object",
2818 docallcmd, IFBURIED | AUTOCOMPLETE },
2819 { M('o'), "offer", "offer a sacrifice to the gods",
2820 dosacrifice, AUTOCOMPLETE },
2821 { 'o', "open", "open a door", doopen },
2822 { 'O', "options", "show option settings, possibly change them",
2823 doset, IFBURIED|GENERALCMD },
2824 { C('o'), "overview", "show a summary of the explored dungeon",
2825 dooverview, IFBURIED|AUTOCOMPLETE },
2826 { '\0', "panic", "test panic routine (fatal to game)",
2827 wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD },
2828 { 'p', "pay", "pay your shopping bill", dopay },
2829 { ',', "pickup", "pick up things at the current location", dopickup },
2830 { '\0', "polyself", "polymorph self",
2831 wiz_polyself, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2832 #ifdef PORT_DEBUG
2833 { '\0', "portdebug", "wizard port debug command",
2834 wiz_port_debug, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2835 #endif
2836 { M('p'), "pray", "pray to the gods for help",
2837 dopray, IFBURIED | AUTOCOMPLETE },
2838 { C('p'), "prevmsg", "view recent game messages",
2839 doprev_message, IFBURIED|GENERALCMD },
2840 { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon },
2841 { 'q', "quaff", "quaff (drink) something", dodrink },
2842 { M('q'), "quit", "exit without saving current game",
2843 done2, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2844 { 'Q', "quiver", "select ammunition for quiver", dowieldquiver },
2845 { 'r', "read", "read a scroll or spellbook", doread },
2846 { C('r'), "redraw", "redraw screen", doredraw, IFBURIED | GENERALCMD },
2847 { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring },
2848 { M('R'), "ride", "mount or dismount a saddled steed",
2849 doride, AUTOCOMPLETE },
2850 { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE },
2851 { 'S', "save", "save the game and exit", dosave, IFBURIED | GENERALCMD },
2852 { 's', "search", "search for traps and secret doors",
2853 dosearch, IFBURIED, "searching" },
2854 { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED },
2855 { AMULET_SYM, "seeamulet", "show the amulet currently worn",
2856 dopramulet, IFBURIED },
2857 { ARMOR_SYM, "seearmor", "show the armor currently worn",
2858 doprarm, IFBURIED },
2859 { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED },
2860 { '\0', "seenv", "show seen vectors",
2861 wiz_show_seenv, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2862 { RING_SYM, "seerings", "show the ring(s) currently worn",
2863 doprring, IFBURIED },
2864 { SPBOOK_SYM, "seespells", "list and reorder known spells",
2865 dovspell, IFBURIED },
2866 { TOOL_SYM, "seetools", "show the tools currently in use",
2867 doprtool, IFBURIED },
2868 { '^', "seetrap", "show the type of adjacent trap", doidtrap, IFBURIED },
2869 { WEAPON_SYM, "seeweapon", "show the weapon currently wielded",
2870 doprwep, IFBURIED },
2871 #ifdef SHELL
2872 { '!', "shell", "do a shell escape", dosh, IFBURIED | GENERALCMD },
2873 #endif /* SHELL */
2874 { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE },
2875 { '\0', "stats", "show memory statistics",
2876 wiz_show_stats, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2877 #ifdef SUSPEND
2878 { C('z'), "suspend", "suspend the game",
2879 dosuspend_core, IFBURIED | GENERALCMD },
2880 #endif /* SUSPEND */
2881 { 'x', "swap", "swap wielded and secondary weapons", doswapweapon },
2882 { 'T', "takeoff", "take off one piece of armor", dotakeoff },
2883 { 'A', "takeoffall", "remove all armor", doddoremarm },
2884 { C('t'), "teleport", "teleport around the level", dotele, IFBURIED },
2885 { '\0', "terrain", "show map without obstructions",
2886 doterrain, IFBURIED | AUTOCOMPLETE },
2887 { 't', "throw", "throw something", dothrow },
2888 { '\0', "timeout", "look at timeout queue",
2889 wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2890 { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE },
2891 { '_', "travel", "travel to a specific location on the map", dotravel },
2892 { M('t'), "turn", "turn undead away", doturn, IFBURIED | AUTOCOMPLETE },
2893 { 'X', "twoweapon", "toggle two-weapon combat",
2894 dotwoweapon, AUTOCOMPLETE },
2895 { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE },
2896 { '<', "up", "go up a staircase", doup },
2897 { '\0', "vanquished", "list vanquished monsters",
2898 dovanquished, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2899 { M('v'), "version",
2900 "list compile time options for this version of aNetHack",
2901 doextversion, IFBURIED | AUTOCOMPLETE | GENERALCMD },
2902 { 'v', "versionshort", "show version", doversion, IFBURIED | GENERALCMD },
2903 { '\0', "vision", "show vision array",
2904 wiz_show_vision, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2905 { '.', "wait", "rest one move while doing nothing",
2906 donull, IFBURIED, "waiting" },
2907 { 'W', "wear", "wear a piece of armor", dowear },
2908 { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED },
2909 { '/', "whatis", "show what type of thing a symbol corresponds to",
2910 dowhatis, IFBURIED | GENERALCMD },
2911 { 'w', "wield", "wield (put in use) a weapon", dowield },
2912 { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE },
2913 #ifdef DEBUG
2914 { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you",
2915 wiz_debug_cmd_bury, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2916 { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2917 wiz_debug_cmd_traveldisplay, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2918 #endif
2919 { C('e'), "wizdetect", "reveal hidden things within a small radius",
2920 wiz_detect, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2921 { C('g'), "wizgenesis", "create a monster",
2922 wiz_genesis, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2923 { C('i'), "wizidentify", "identify all items in inventory",
2924 wiz_identify, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2925 { '\0', "wizintrinsic", "set an intrinsic",
2926 wiz_intrinsic, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2927 { C('v'), "wizlevelport", "teleport to another level",
2928 wiz_level_tele, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2929 { '\0', "wizmakemap", "recreate the current level",
2930 wiz_makemap, IFBURIED | WIZMODECMD },
2931 { C('f'), "wizmap", "map the level",
2932 wiz_map, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2933 { '\0', "wizrumorcheck", "verify rumor boundaries",
2934 wiz_rumor_check, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2935 { '\0', "wizsmell", "smell monster",
2936 wiz_smell, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2937 { '\0', "wizwhere", "show locations of special levels",
2938 wiz_where, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2939 { C('w'), "wizwish", "wish for something",
2940 wiz_wish, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2941 { '\0', "wmode", "show wall modes",
2942 wiz_show_wmodes, IFBURIED | AUTOCOMPLETE | WIZMODECMD },
2943 { 'z', "zap", "zap a wand", dozap },
2944 { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */
2947 const char *
2948 key2extcmddesc(key)
2949 uchar key;
2951 if (Cmd.commands[key] && Cmd.commands[key]->ef_txt)
2952 return Cmd.commands[key]->ef_desc;
2953 return (char *) 0;
2956 void
2957 bind_key(key, command)
2958 uchar key;
2959 const char *command;
2961 struct ext_func_tab *extcmd;
2963 /* special case: "nothing" is reserved for unbinding */
2964 if (!strcmp(command, "nothing")) {
2965 Cmd.commands[key] = (struct ext_func_tab *) 0;
2966 return;
2969 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) {
2970 if (strcmp(command, extcmd->ef_txt))
2971 continue;
2972 Cmd.commands[key] = extcmd;
2973 return;
2976 pline(
2977 "Bad command %s matched with key %c (ASCII %i). Ignoring command.\n",
2978 command, key, key);
2981 /* initialize all keyboard commands */
2982 void
2983 commands_init()
2985 struct ext_func_tab *extcmd;
2987 for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++)
2988 if (extcmd->key)
2989 Cmd.commands[extcmd->key] = extcmd;
2991 bind_key(C('l'), "redraw"); /* if number_pad is set */
2992 /* 'b', 'B' : go sw */
2993 /* 'F' : fight (one time) */
2994 /* 'g', 'G' : multiple go */
2995 /* 'h', 'H' : go west */
2996 bind_key('h', "help"); /* if number_pad is set */
2997 bind_key('j', "jump"); /* if number_pad is on */
2998 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' move commands */
2999 bind_key('k', "kick"); /* if number_pad is on */
3000 bind_key('l', "loot"); /* if number_pad is on */
3001 bind_key(C('n'), "annotate"); /* if number_pad is on */
3002 bind_key(M('n'), "name");
3003 bind_key(M('N'), "name");
3004 bind_key('u', "untrap"); /* if number_pad is on */
3006 /* alt keys: */
3007 bind_key(M('O'), "overview");
3008 bind_key(M('2'), "twoweapon");
3010 /* wait_on_space */
3011 bind_key(' ', "wait");
3015 dokeylist_putcmds(datawin, docount, cmdflags, exflags, keys_used)
3016 winid datawin;
3017 boolean docount;
3018 int cmdflags, exflags;
3019 boolean *keys_used; /* boolean keys_used[256] */
3021 int i;
3022 char buf[BUFSZ];
3023 char buf2[QBUFSZ];
3024 int count = 0;
3026 for (i = 0; i < 256; i++) {
3027 const struct ext_func_tab *extcmd;
3028 uchar key = (uchar) i;
3030 if (keys_used[i])
3031 continue;
3032 if (key == ' ' && !flags.rest_on_space)
3033 continue;
3034 if ((extcmd = Cmd.commands[i]) != (struct ext_func_tab *) 0) {
3035 if ((cmdflags && !(extcmd->flags & cmdflags))
3036 || (exflags && (extcmd->flags & exflags)))
3037 continue;
3038 if (docount) {
3039 count++;
3040 continue;
3042 Sprintf(buf, "%-8s %-12s %s", key2txt(key, buf2),
3043 extcmd->ef_txt,
3044 extcmd->ef_desc);
3045 putstr(datawin, 0, buf);
3046 keys_used[i] = TRUE;
3049 return count;
3052 /* list all keys and their bindings, like dat/hh but dynamic */
3053 void
3054 dokeylist(VOID_ARGS)
3056 char buf[BUFSZ], buf2[BUFSZ];
3057 uchar key;
3058 boolean keys_used[256] = {0};
3059 winid datawin;
3060 int i;
3061 const struct {
3062 int nhkf;
3063 const char *desc;
3064 boolean numpad;
3065 } misc_keys[] = {
3066 { NHKF_ESC, "escape from the current query/action", FALSE },
3067 { NHKF_RUSH,
3068 "Prefix: rush until something interesting is seen", FALSE },
3069 { NHKF_RUN,
3070 "Prefix: run until something extremely interesting is seen", FALSE },
3071 { NHKF_RUN2,
3072 "Prefix: run until something extremely interesting is seen", TRUE },
3073 { NHKF_FIGHT,
3074 "Prefix: force fight even if you don't see a monster", FALSE },
3075 { NHKF_FIGHT2,
3076 "Prefix: force fight even if you don't see a monster", TRUE },
3077 { NHKF_NOPICKUP,
3078 "Prefix: move without picking up objects/fighting", FALSE },
3079 { NHKF_RUN_NOPICKUP,
3080 "Prefix: run without picking up objects/fighting", FALSE },
3081 { NHKF_DOINV, "inventory (same as #inventory)", TRUE },
3082 { NHKF_REQMENU, "Prefix: request a menu", FALSE },
3083 #ifdef REDO
3084 { NHKF_DOAGAIN , "redo the previous command", FALSE },
3085 #endif
3086 { 0, (const char *) 0, FALSE }
3089 datawin = create_nhwindow(NHW_TEXT);
3090 putstr(datawin, 0, "");
3091 putstr(datawin, 0, " Full Current Key Bindings List");
3093 /* directional keys */
3094 putstr(datawin, 0, "");
3095 putstr(datawin, 0, "Directional keys:");
3096 show_direction_keys(datawin, FALSE);
3098 keys_used[(uchar) Cmd.move_NW] = keys_used[(uchar) Cmd.move_N]
3099 = keys_used[(uchar) Cmd.move_NE] = keys_used[(uchar) Cmd.move_W]
3100 = keys_used[(uchar) Cmd.move_E] = keys_used[(uchar) Cmd.move_SW]
3101 = keys_used[(uchar) Cmd.move_S] = keys_used[(uchar) Cmd.move_SE]
3102 = TRUE;
3104 if (!iflags.num_pad) {
3105 keys_used[(uchar) highc(Cmd.move_NW)]
3106 = keys_used[(uchar) highc(Cmd.move_N)]
3107 = keys_used[(uchar) highc(Cmd.move_NE)]
3108 = keys_used[(uchar) highc(Cmd.move_W)]
3109 = keys_used[(uchar) highc(Cmd.move_E)]
3110 = keys_used[(uchar) highc(Cmd.move_SW)]
3111 = keys_used[(uchar) highc(Cmd.move_S)]
3112 = keys_used[(uchar) highc(Cmd.move_SE)] = TRUE;
3113 keys_used[(uchar) C(Cmd.move_NW)]
3114 = keys_used[(uchar) C(Cmd.move_N)]
3115 = keys_used[(uchar) C(Cmd.move_NE)]
3116 = keys_used[(uchar) C(Cmd.move_W)]
3117 = keys_used[(uchar) C(Cmd.move_E)]
3118 = keys_used[(uchar) C(Cmd.move_SW)]
3119 = keys_used[(uchar) C(Cmd.move_S)]
3120 = keys_used[(uchar) C(Cmd.move_SE)] = TRUE;
3121 putstr(datawin, 0, "");
3122 putstr(datawin, 0,
3123 "Shift-<direction> will move in specified direction until you hit");
3124 putstr(datawin, 0, " a wall or run into something.");
3125 putstr(datawin, 0,
3126 "Ctrl-<direction> will run in specified direction until something");
3127 putstr(datawin, 0, " very interesting is seen.");
3130 putstr(datawin, 0, "");
3131 putstr(datawin, 0, "Miscellaneous keys:");
3132 for (i = 0; misc_keys[i].desc; i++) {
3133 key = Cmd.spkeys[misc_keys[i].nhkf];
3134 if (key && ((misc_keys[i].numpad && iflags.num_pad)
3135 || !misc_keys[i].numpad)) {
3136 keys_used[(uchar) key] = TRUE;
3137 Sprintf(buf, "%-8s %s", key2txt(key, buf2), misc_keys[i].desc);
3138 putstr(datawin, 0, buf);
3141 #ifndef NO_SIGNAL
3142 putstr(datawin, 0, "^c break out of aNetHack (SIGINT)");
3143 keys_used[(uchar) C('c')] = TRUE;
3144 #endif
3146 putstr(datawin, 0, "");
3147 show_menu_controls(datawin, TRUE);
3149 if (dokeylist_putcmds(datawin, TRUE, GENERALCMD, WIZMODECMD, keys_used)) {
3150 putstr(datawin, 0, "");
3151 putstr(datawin, 0, "General commands:");
3152 (void) dokeylist_putcmds(datawin, FALSE, GENERALCMD, WIZMODECMD,
3153 keys_used);
3156 if (dokeylist_putcmds(datawin, TRUE, 0, WIZMODECMD, keys_used)) {
3157 putstr(datawin, 0, "");
3158 putstr(datawin, 0, "Game commands:");
3159 (void) dokeylist_putcmds(datawin, FALSE, 0, WIZMODECMD, keys_used);
3162 if (wizard
3163 && dokeylist_putcmds(datawin, TRUE, WIZMODECMD, 0, keys_used)) {
3164 putstr(datawin, 0, "");
3165 putstr(datawin, 0, "Wizard-mode commands:");
3166 (void) dokeylist_putcmds(datawin, FALSE, WIZMODECMD, 0, keys_used);
3169 display_nhwindow(datawin, FALSE);
3170 destroy_nhwindow(datawin);
3173 STATIC_OVL char
3174 cmd_from_func(fn)
3175 int NDECL((*fn));
3177 int i;
3179 for (i = 0; i < 256; ++i)
3180 if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn)
3181 return (char) i;
3182 return '\0';
3186 * wizard mode sanity_check code
3189 static const char template[] = "%-27s %4ld %6ld";
3190 static const char stats_hdr[] = " count bytes";
3191 static const char stats_sep[] = "--------------------------- ----- -------";
3193 STATIC_OVL int
3194 size_obj(otmp)
3195 struct obj *otmp;
3197 int sz = (int) sizeof(struct obj);
3199 if (otmp->oextra) {
3200 sz += (int) sizeof(struct oextra);
3201 if (ONAME(otmp))
3202 sz += (int) strlen(ONAME(otmp)) + 1;
3203 if (OMONST(otmp))
3204 sz += (int) sizeof(struct monst);
3205 if (OMID(otmp))
3206 sz += (int) sizeof(unsigned);
3207 if (OLONG(otmp))
3208 sz += (int) sizeof(long);
3209 if (OMAILCMD(otmp))
3210 sz += (int) strlen(OMAILCMD(otmp)) + 1;
3212 return sz;
3215 STATIC_OVL void
3216 count_obj(chain, total_count, total_size, top, recurse)
3217 struct obj *chain;
3218 long *total_count;
3219 long *total_size;
3220 boolean top;
3221 boolean recurse;
3223 long count, size;
3224 struct obj *obj;
3226 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
3227 if (top) {
3228 count++;
3229 size += size_obj(obj);
3231 if (recurse && obj->cobj)
3232 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
3234 *total_count += count;
3235 *total_size += size;
3238 STATIC_OVL void
3239 obj_chain(win, src, chain, force, total_count, total_size)
3240 winid win;
3241 const char *src;
3242 struct obj *chain;
3243 boolean force;
3244 long *total_count;
3245 long *total_size;
3247 char buf[BUFSZ];
3248 long count = 0L, size = 0L;
3250 count_obj(chain, &count, &size, TRUE, FALSE);
3252 if (count || size || force) {
3253 *total_count += count;
3254 *total_size += size;
3255 Sprintf(buf, template, src, count, size);
3256 putstr(win, 0, buf);
3260 STATIC_OVL void
3261 mon_invent_chain(win, src, chain, total_count, total_size)
3262 winid win;
3263 const char *src;
3264 struct monst *chain;
3265 long *total_count;
3266 long *total_size;
3268 char buf[BUFSZ];
3269 long count = 0, size = 0;
3270 struct monst *mon;
3272 for (mon = chain; mon; mon = mon->nmon)
3273 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
3275 if (count || size) {
3276 *total_count += count;
3277 *total_size += size;
3278 Sprintf(buf, template, src, count, size);
3279 putstr(win, 0, buf);
3283 STATIC_OVL void
3284 contained_stats(win, src, total_count, total_size)
3285 winid win;
3286 const char *src;
3287 long *total_count;
3288 long *total_size;
3290 char buf[BUFSZ];
3291 long count = 0, size = 0;
3292 struct monst *mon;
3294 count_obj(invent, &count, &size, FALSE, TRUE);
3295 count_obj(fobj, &count, &size, FALSE, TRUE);
3296 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3297 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3298 /* DEADMONSTER check not required in this loop since they have no
3299 * inventory */
3300 for (mon = fmon; mon; mon = mon->nmon)
3301 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3302 for (mon = migrating_mons; mon; mon = mon->nmon)
3303 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3305 if (count || size) {
3306 *total_count += count;
3307 *total_size += size;
3308 Sprintf(buf, template, src, count, size);
3309 putstr(win, 0, buf);
3313 STATIC_OVL int
3314 size_monst(mtmp, incl_wsegs)
3315 struct monst *mtmp;
3316 boolean incl_wsegs;
3318 int sz = (int) sizeof (struct monst);
3320 if (mtmp->wormno && incl_wsegs)
3321 sz += size_wseg(mtmp);
3323 if (mtmp->mextra) {
3324 sz += (int) sizeof (struct mextra);
3325 if (MNAME(mtmp))
3326 sz += (int) strlen(MNAME(mtmp)) + 1;
3327 if (EGD(mtmp))
3328 sz += (int) sizeof (struct egd);
3329 if (EPRI(mtmp))
3330 sz += (int) sizeof (struct epri);
3331 if (ESHK(mtmp))
3332 sz += (int) sizeof (struct eshk);
3333 if (EMIN(mtmp))
3334 sz += (int) sizeof (struct emin);
3335 if (EDOG(mtmp))
3336 sz += (int) sizeof (struct edog);
3337 /* mextra->mcorpsenm doesn't point to more memory */
3339 return sz;
3342 STATIC_OVL void
3343 mon_chain(win, src, chain, force, total_count, total_size)
3344 winid win;
3345 const char *src;
3346 struct monst *chain;
3347 boolean force;
3348 long *total_count;
3349 long *total_size;
3351 char buf[BUFSZ];
3352 long count, size;
3353 struct monst *mon;
3354 /* mon->wormno means something different for migrating_mons and mydogs */
3355 boolean incl_wsegs = !strcmpi(src, "fmon");
3357 count = size = 0L;
3358 for (mon = chain; mon; mon = mon->nmon) {
3359 count++;
3360 size += size_monst(mon, incl_wsegs);
3362 if (count || size || force) {
3363 *total_count += count;
3364 *total_size += size;
3365 Sprintf(buf, template, src, count, size);
3366 putstr(win, 0, buf);
3370 STATIC_OVL void
3371 misc_stats(win, total_count, total_size)
3372 winid win;
3373 long *total_count;
3374 long *total_size;
3376 char buf[BUFSZ], hdrbuf[QBUFSZ];
3377 long count, size;
3378 int idx;
3379 struct trap *tt;
3380 struct damage *sd; /* shop damage */
3381 struct cemetery *bi; /* bones info */
3383 /* traps and engravings are output unconditionally;
3384 * others only if nonzero
3386 count = size = 0L;
3387 for (tt = ftrap; tt; tt = tt->ntrap) {
3388 ++count;
3389 size += (long) sizeof *tt;
3391 *total_count += count;
3392 *total_size += size;
3393 Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap));
3394 Sprintf(buf, template, hdrbuf, count, size);
3395 putstr(win, 0, buf);
3397 count = size = 0L;
3398 engr_stats("engravings, size %ld+text", hdrbuf, &count, &size);
3399 *total_count += count;
3400 *total_size += size;
3401 Sprintf(buf, template, hdrbuf, count, size);
3402 putstr(win, 0, buf);
3404 count = size = 0L;
3405 light_stats("light sources, size %ld", hdrbuf, &count, &size);
3406 if (count || size) {
3407 *total_count += count;
3408 *total_size += size;
3409 Sprintf(buf, template, hdrbuf, count, size);
3410 putstr(win, 0, buf);
3413 count = size = 0L;
3414 timer_stats("timers, size %ld", hdrbuf, &count, &size);
3415 if (count || size) {
3416 *total_count += count;
3417 *total_size += size;
3418 Sprintf(buf, template, hdrbuf, count, size);
3419 putstr(win, 0, buf);
3422 count = size = 0L;
3423 for (sd = level.damagelist; sd; sd = sd->next) {
3424 ++count;
3425 size += (long) sizeof *sd;
3427 if (count || size) {
3428 *total_count += count;
3429 *total_size += size;
3430 Sprintf(hdrbuf, "shop damage, size %ld",
3431 (long) sizeof (struct damage));
3432 Sprintf(buf, template, hdrbuf, count, size);
3433 putstr(win, 0, buf);
3436 count = size = 0L;
3437 region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size);
3438 if (count || size) {
3439 *total_count += count;
3440 *total_size += size;
3441 Sprintf(buf, template, hdrbuf, count, size);
3442 putstr(win, 0, buf);
3445 count = size = 0L;
3446 for (bi = level.bonesinfo; bi; bi = bi->next) {
3447 ++count;
3448 size += (long) sizeof *bi;
3450 if (count || size) {
3451 *total_count += count;
3452 *total_size += size;
3453 Sprintf(hdrbuf, "bones history, size %ld",
3454 (long) sizeof (struct cemetery));
3455 Sprintf(buf, template, hdrbuf, count, size);
3456 putstr(win, 0, buf);
3459 count = size = 0L;
3460 for (idx = 0; idx < NUM_OBJECTS; ++idx)
3461 if (objects[idx].oc_uname) {
3462 ++count;
3463 size += (long) (strlen(objects[idx].oc_uname) + 1);
3465 if (count || size) {
3466 *total_count += count;
3467 *total_size += size;
3468 Strcpy(hdrbuf, "object type names, text");
3469 Sprintf(buf, template, hdrbuf, count, size);
3470 putstr(win, 0, buf);
3475 * Display memory usage of all monsters and objects on the level.
3477 static int
3478 wiz_show_stats()
3480 char buf[BUFSZ];
3481 winid win;
3482 long total_obj_size, total_obj_count,
3483 total_mon_size, total_mon_count,
3484 total_ovr_size, total_ovr_count,
3485 total_misc_size, total_misc_count;
3487 win = create_nhwindow(NHW_TEXT);
3488 putstr(win, 0, "Current memory statistics:");
3490 total_obj_count = total_obj_size = 0L;
3491 putstr(win, 0, stats_hdr);
3492 Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj));
3493 putstr(win, 0, buf);
3494 obj_chain(win, "invent", invent, TRUE, &total_obj_count, &total_obj_size);
3495 obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size);
3496 obj_chain(win, "buried", level.buriedobjlist, FALSE,
3497 &total_obj_count, &total_obj_size);
3498 obj_chain(win, "migrating obj", migrating_objs, FALSE,
3499 &total_obj_count, &total_obj_size);
3500 obj_chain(win, "billobjs", billobjs, FALSE,
3501 &total_obj_count, &total_obj_size);
3502 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3503 mon_invent_chain(win, "migrating minvent", migrating_mons,
3504 &total_obj_count, &total_obj_size);
3505 contained_stats(win, "contained", &total_obj_count, &total_obj_size);
3506 putstr(win, 0, stats_sep);
3507 Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size);
3508 putstr(win, 0, buf);
3510 total_mon_count = total_mon_size = 0L;
3511 putstr(win, 0, "");
3512 Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst));
3513 putstr(win, 0, buf);
3514 mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size);
3515 mon_chain(win, "migrating", migrating_mons, FALSE,
3516 &total_mon_count, &total_mon_size);
3517 /* 'mydogs' is only valid during level change or end of game disclosure,
3518 but conceivably we've been called from within debugger at such time */
3519 if (mydogs) /* monsters accompanying hero */
3520 mon_chain(win, "mydogs", mydogs, FALSE,
3521 &total_mon_count, &total_mon_size);
3522 putstr(win, 0, stats_sep);
3523 Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size);
3524 putstr(win, 0, buf);
3526 total_ovr_count = total_ovr_size = 0L;
3527 putstr(win, 0, "");
3528 putstr(win, 0, " Overview");
3529 overview_stats(win, template, &total_ovr_count, &total_ovr_size);
3530 putstr(win, 0, stats_sep);
3531 Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size);
3532 putstr(win, 0, buf);
3534 total_misc_count = total_misc_size = 0L;
3535 putstr(win, 0, "");
3536 putstr(win, 0, " Miscellaneous");
3537 misc_stats(win, &total_misc_count, &total_misc_size);
3538 putstr(win, 0, stats_sep);
3539 Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size);
3540 putstr(win, 0, buf);
3542 putstr(win, 0, "");
3543 putstr(win, 0, stats_sep);
3544 Sprintf(buf, template, " Grand total",
3545 (total_obj_count + total_mon_count
3546 + total_ovr_count + total_misc_count),
3547 (total_obj_size + total_mon_size
3548 + total_ovr_size + total_misc_size));
3549 putstr(win, 0, buf);
3551 #if defined(__BORLANDC__) && !defined(_WIN32)
3552 show_borlandc_stats(win);
3553 #endif
3555 display_nhwindow(win, FALSE);
3556 destroy_nhwindow(win);
3557 return 0;
3560 void
3561 sanity_check()
3563 obj_sanity_check();
3564 timer_sanity_check();
3565 mon_sanity_check();
3566 light_sources_sanity_check();
3569 #ifdef DEBUG_MIGRATING_MONS
3570 static int
3571 wiz_migrate_mons()
3573 int mcount = 0;
3574 char inbuf[BUFSZ];
3575 struct permonst *ptr;
3576 struct monst *mtmp;
3577 d_level tolevel;
3579 getlin("How many random monsters to migrate? [0]", inbuf);
3580 if (*inbuf == '\033')
3581 return 0;
3582 mcount = atoi(inbuf);
3583 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3584 return 0;
3585 while (mcount > 0) {
3586 if (Is_stronghold(&u.uz))
3587 assign_level(&tolevel, &valley_level);
3588 else
3589 get_level(&tolevel, depth(&u.uz) + 1);
3590 ptr = rndmonst();
3591 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3592 if (mtmp)
3593 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3594 (coord *) 0);
3595 mcount--;
3597 return 0;
3599 #endif
3601 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3602 #define unmeta(c) (0x7f & (c))
3604 struct {
3605 int nhkf;
3606 char key;
3607 const char *name;
3608 } const spkeys_binds[] = {
3609 { NHKF_ESC, '\033', (char *) 0 }, /* no binding */
3610 { NHKF_DOAGAIN, DOAGAIN, "repeat" },
3611 { NHKF_REQMENU, 'm', "reqmenu" },
3612 { NHKF_RUN, 'G', "run" },
3613 { NHKF_RUN2, '5', "run.numpad" },
3614 { NHKF_RUSH, 'g', "rush" },
3615 { NHKF_FIGHT, 'F', "fight" },
3616 { NHKF_FIGHT2, '-', "fight.numpad" },
3617 { NHKF_NOPICKUP, 'm', "nopickup" },
3618 { NHKF_RUN_NOPICKUP, 'M', "run.nopickup" },
3619 { NHKF_DOINV, '0', "doinv" },
3620 { NHKF_TRAVEL, CMD_TRAVEL, (char *) 0 }, /* no binding */
3621 { NHKF_CLICKLOOK, CMD_CLICKLOOK, (char *) 0 }, /* no binding */
3622 { NHKF_REDRAW, C('r'), "redraw" },
3623 { NHKF_REDRAW2, C('l'), "redraw.numpad" },
3624 { NHKF_GETDIR_SELF, '.', "getdir.self" },
3625 { NHKF_GETDIR_SELF2, 's', "getdir.self2" },
3626 { NHKF_GETDIR_HELP, '?', "getdir.help" },
3627 { NHKF_COUNT, 'n', "count" },
3628 { NHKF_GETPOS_SELF, '@', "getpos.self" },
3629 { NHKF_GETPOS_PICK, '.', "getpos.pick" },
3630 { NHKF_GETPOS_PICK_Q, ',', "getpos.pick.quick" },
3631 { NHKF_GETPOS_PICK_O, ';', "getpos.pick.once" },
3632 { NHKF_GETPOS_PICK_V, ':', "getpos.pick.verbose" },
3633 { NHKF_GETPOS_SHOWVALID, '$', "getpos.valid" },
3634 { NHKF_GETPOS_AUTODESC, '#', "getpos.autodescribe" },
3635 { NHKF_GETPOS_MON_NEXT, 'm', "getpos.mon.next" },
3636 { NHKF_GETPOS_MON_PREV, 'M', "getpos.mon.prev" },
3637 { NHKF_GETPOS_OBJ_NEXT, 'o', "getpos.obj.next" },
3638 { NHKF_GETPOS_OBJ_PREV, 'O', "getpos.obj.prev" },
3639 { NHKF_GETPOS_DOOR_NEXT, 'd', "getpos.door.next" },
3640 { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" },
3641 { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" },
3642 { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" },
3643 { NHKF_GETPOS_INTERESTING_NEXT, 'a', "getpos.all.next" },
3644 { NHKF_GETPOS_INTERESTING_PREV, 'A', "getpos.all.prev" },
3645 { NHKF_GETPOS_HELP, '?', "getpos.help" },
3646 { NHKF_GETPOS_LIMITVIEW, '"', "getpos.inview" },
3647 { NHKF_GETPOS_MENU, '!', "getpos.menu" }
3650 boolean
3651 bind_specialkey(key, command)
3652 uchar key;
3653 const char *command;
3655 int i;
3656 for (i = 0; i < SIZE(spkeys_binds); i++) {
3657 if (!spkeys_binds[i].name || strcmp(command, spkeys_binds[i].name))
3658 continue;
3659 Cmd.spkeys[spkeys_binds[i].nhkf] = key;
3660 return TRUE;
3662 return FALSE;
3665 /* returns a one-byte character from the text (it may massacre the txt
3666 * buffer) */
3667 char
3668 txt2key(txt)
3669 char *txt;
3671 txt = trimspaces(txt);
3672 if (!*txt)
3673 return '\0';
3675 /* simple character */
3676 if (!txt[1])
3677 return txt[0];
3679 /* a few special entries */
3680 if (!strcmp(txt, "<enter>"))
3681 return '\n';
3682 if (!strcmp(txt, "<space>"))
3683 return ' ';
3684 if (!strcmp(txt, "<esc>"))
3685 return '\033';
3687 /* control and meta keys */
3688 switch (*txt) {
3689 case 'm': /* can be mx, Mx, m-x, M-x */
3690 case 'M':
3691 txt++;
3692 if (*txt == '-' && txt[1])
3693 txt++;
3694 if (txt[1])
3695 return '\0';
3696 return M(*txt);
3697 case 'c': /* can be cx, Cx, ^x, c-x, C-x, ^-x */
3698 case 'C':
3699 case '^':
3700 txt++;
3701 if (*txt == '-' && txt[1])
3702 txt++;
3703 if (txt[1])
3704 return '\0';
3705 return C(*txt);
3708 /* ascii codes: must be three-digit decimal */
3709 if (*txt >= '0' && *txt <= '9') {
3710 uchar key = 0;
3711 int i;
3713 for (i = 0; i < 3; i++) {
3714 if (txt[i] < '0' || txt[i] > '9')
3715 return '\0';
3716 key = 10 * key + txt[i] - '0';
3718 return key;
3721 return '\0';
3724 /* returns the text for a one-byte encoding;
3725 * must be shorter than a tab for proper formatting */
3726 char *
3727 key2txt(c, txt)
3728 uchar c;
3729 char *txt; /* sufficiently long buffer */
3731 /* should probably switch to "SPC", "ESC", "RET"
3732 since anethack's documentation uses ESC for <escape> */
3733 if (c == ' ')
3734 Sprintf(txt, "<space>");
3735 else if (c == '\033')
3736 Sprintf(txt, "<esc>");
3737 else if (c == '\n')
3738 Sprintf(txt, "<enter>");
3739 else if (c == '\177')
3740 Sprintf(txt, "<del>"); /* "<delete>" won't fit */
3741 else
3742 Strcpy(txt, visctrl((char) c));
3743 return txt;
3747 void
3748 parseautocomplete(autocomplete, condition)
3749 char *autocomplete;
3750 boolean condition;
3752 struct ext_func_tab *efp;
3753 register char *autoc;
3755 /* break off first autocomplete from the rest; parse the rest */
3756 if ((autoc = index(autocomplete, ',')) != 0
3757 || (autoc = index(autocomplete, ':')) != 0) {
3758 *autoc++ = '\0';
3759 parseautocomplete(autoc, condition);
3762 /* strip leading and trailing white space */
3763 autocomplete = trimspaces(autocomplete);
3765 if (!*autocomplete)
3766 return;
3768 /* take off negation */
3769 if (*autocomplete == '!') {
3770 /* unlike most options, a leading "no" might actually be a part of
3771 * the extended command. Thus you have to use ! */
3772 autocomplete++;
3773 autocomplete = trimspaces(autocomplete);
3774 condition = !condition;
3777 /* find and modify the extended command */
3778 for (efp = extcmdlist; efp->ef_txt; efp++) {
3779 if (!strcmp(autocomplete, efp->ef_txt)) {
3780 if (condition)
3781 efp->flags |= AUTOCOMPLETE;
3782 else
3783 efp->flags &= ~AUTOCOMPLETE;
3784 return;
3788 /* not a real extended command */
3789 raw_printf("Bad autocomplete: invalid extended command '%s'.",
3790 autocomplete);
3791 wait_synch();
3794 /* called at startup and after number_pad is twiddled */
3795 void
3796 reset_commands(initial)
3797 boolean initial;
3799 static const char sdir[] = "hykulnjb><",
3800 sdir_swap_yz[] = "hzkulnjb><",
3801 ndir[] = "47896321><",
3802 ndir_phone_layout[] = "41236987><";
3803 static const int ylist[] = {
3804 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3806 static struct ext_func_tab *back_dir_cmd[8];
3807 const struct ext_func_tab *cmdtmp;
3808 boolean flagtemp;
3809 int c, i, updated = 0;
3810 static boolean backed_dir_cmd = FALSE;
3812 if (initial) {
3813 updated = 1;
3814 Cmd.num_pad = FALSE;
3815 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3816 for (i = 0; i < SIZE(spkeys_binds); i++)
3817 Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key;
3818 commands_init();
3819 } else {
3821 if (backed_dir_cmd) {
3822 for (i = 0; i < 8; i++) {
3823 Cmd.commands[(uchar) Cmd.dirchars[i]] = back_dir_cmd[i];
3827 /* basic num_pad */
3828 flagtemp = iflags.num_pad;
3829 if (flagtemp != Cmd.num_pad) {
3830 Cmd.num_pad = flagtemp;
3831 ++updated;
3833 /* swap_yz mode (only applicable for !num_pad); intended for
3834 QWERTZ keyboard used in Central Europe, particularly Germany */
3835 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3836 if (flagtemp != Cmd.swap_yz) {
3837 Cmd.swap_yz = flagtemp;
3838 ++updated;
3839 /* Cmd.swap_yz has been toggled;
3840 perform the swap (or reverse previous one) */
3841 for (i = 0; i < SIZE(ylist); i++) {
3842 c = ylist[i] & 0xff;
3843 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3844 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3845 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3848 /* MSDOS compatibility mode (only applicable for num_pad) */
3849 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3850 if (flagtemp != Cmd.pcHack_compat) {
3851 Cmd.pcHack_compat = flagtemp;
3852 ++updated;
3853 /* pcHack_compat has been toggled */
3854 c = M('5') & 0xff;
3855 cmdtmp = Cmd.commands['5'];
3856 Cmd.commands['5'] = Cmd.commands[c];
3857 Cmd.commands[c] = cmdtmp;
3858 c = M('0') & 0xff;
3859 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3861 /* phone keypad layout (only applicable for num_pad) */
3862 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3863 if (flagtemp != Cmd.phone_layout) {
3864 Cmd.phone_layout = flagtemp;
3865 ++updated;
3866 /* phone_layout has been toggled */
3867 for (i = 0; i < 3; i++) {
3868 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3869 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3870 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3871 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3872 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3873 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3874 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3875 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3878 } /*?initial*/
3880 if (updated)
3881 Cmd.serialno++;
3882 Cmd.dirchars = !Cmd.num_pad
3883 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3884 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3885 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3887 Cmd.move_W = Cmd.dirchars[0];
3888 Cmd.move_NW = Cmd.dirchars[1];
3889 Cmd.move_N = Cmd.dirchars[2];
3890 Cmd.move_NE = Cmd.dirchars[3];
3891 Cmd.move_E = Cmd.dirchars[4];
3892 Cmd.move_SE = Cmd.dirchars[5];
3893 Cmd.move_S = Cmd.dirchars[6];
3894 Cmd.move_SW = Cmd.dirchars[7];
3896 if (!initial) {
3897 for (i = 0; i < 8; i++) {
3898 back_dir_cmd[i] =
3899 (struct ext_func_tab *) Cmd.commands[(uchar) Cmd.dirchars[i]];
3900 Cmd.commands[(uchar) Cmd.dirchars[i]] = (struct ext_func_tab *) 0;
3902 backed_dir_cmd = TRUE;
3903 for (i = 0; i < 8; i++)
3904 bind_key(Cmd.dirchars[i], "nothing");
3908 /* non-movement commands which accept 'm' prefix to request menu operation */
3909 STATIC_OVL boolean
3910 accept_menu_prefix(cmd_func)
3911 int NDECL((*cmd_func));
3913 if (cmd_func == dopickup || cmd_func == dotip
3914 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
3915 an item on floor or in invent; 'm' skips picking from floor
3916 (ie, inventory only) rather than request use of menu operation */
3917 || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
3918 /* 'm' for removing saddle from adjacent monster without checking
3919 for containers at <u.ux,u.uy> */
3920 || cmd_func == doloot
3921 /* travel: pop up a menu of interesting targets in view */
3922 || cmd_func == dotravel
3923 /* 'm' prefix allowed for some extended commands */
3924 || cmd_func == doextcmd || cmd_func == doextlist)
3925 return TRUE;
3926 return FALSE;
3930 ch2spkeys(c, start,end)
3931 char c;
3932 int start,end;
3934 int i;
3935 for (i = start; i <= end; i++)
3936 if (Cmd.spkeys[i] == c)
3937 return i;
3938 return NHKF_ESC;
3941 void
3942 rhack(cmd)
3943 register char *cmd;
3945 boolean do_walk, do_rush, prefix_seen, bad_command,
3946 firsttime = (cmd == 0);
3948 iflags.menu_requested = FALSE;
3949 #ifdef SAFERHANGUP
3950 if (program_state.done_hup)
3951 end_of_input();
3952 #endif
3953 if (firsttime) {
3954 context.nopick = 0;
3955 cmd = parse();
3957 if (*cmd == Cmd.spkeys[NHKF_ESC]) {
3958 context.move = FALSE;
3959 return;
3961 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3962 in_doagain = TRUE;
3963 stail = 0;
3964 rhack((char *) 0); /* read and execute command */
3965 in_doagain = FALSE;
3966 return;
3968 /* Special case of *cmd == ' ' handled better below */
3969 if (!*cmd || *cmd == (char) 0377) {
3970 nhbell();
3971 context.move = FALSE;
3972 return; /* probably we just had an interrupt */
3975 /* handle most movement commands */
3976 do_walk = do_rush = prefix_seen = FALSE;
3977 context.travel = context.travel1 = 0;
3978 switch (ch2spkeys(*cmd, NHKF_RUN,NHKF_CLICKLOOK)) {
3979 case NHKF_RUSH:
3980 if (movecmd(cmd[1])) {
3981 context.run = 2;
3982 do_rush = TRUE;
3983 } else
3984 prefix_seen = TRUE;
3985 break;
3986 case NHKF_RUN2:
3987 if (!Cmd.num_pad)
3988 break; /* else FALLTHRU */
3989 case NHKF_RUN:
3990 if (movecmd(lowc(cmd[1]))) {
3991 context.run = 3;
3992 do_rush = TRUE;
3993 } else
3994 prefix_seen = TRUE;
3995 break;
3996 case NHKF_FIGHT2:
3997 if (!Cmd.num_pad)
3998 break; /* else FALLTHRU */
3999 /* Effects of movement commands and invisible monsters:
4000 * m: always move onto space (even if 'I' remembered)
4001 * F: always attack space (even if 'I' not remembered)
4002 * normal movement: attack if 'I', move otherwise.
4004 case NHKF_FIGHT:
4005 if (movecmd(cmd[1])) {
4006 context.forcefight = 1;
4007 do_walk = TRUE;
4008 } else
4009 prefix_seen = TRUE;
4010 break;
4011 case NHKF_NOPICKUP:
4012 if (movecmd(cmd[1]) || u.dz) {
4013 context.run = 0;
4014 context.nopick = 1;
4015 if (!u.dz)
4016 do_walk = TRUE;
4017 else
4018 cmd[0] = cmd[1]; /* "m<" or "m>" */
4019 } else
4020 prefix_seen = TRUE;
4021 break;
4022 case NHKF_RUN_NOPICKUP:
4023 if (movecmd(lowc(cmd[1]))) {
4024 context.run = 1;
4025 context.nopick = 1;
4026 do_rush = TRUE;
4027 } else
4028 prefix_seen = TRUE;
4029 break;
4030 case NHKF_DOINV:
4031 if (!Cmd.num_pad)
4032 break;
4033 (void) ddoinv(); /* a convenience borrowed from the PC */
4034 context.move = FALSE;
4035 multi = 0;
4036 return;
4037 case NHKF_CLICKLOOK:
4038 if (iflags.clicklook) {
4039 context.move = FALSE;
4040 do_look(2, &clicklook_cc);
4042 return;
4043 case NHKF_TRAVEL:
4044 if (flags.travelcmd) {
4045 context.travel = 1;
4046 context.travel1 = 1;
4047 context.run = 8;
4048 context.nopick = 1;
4049 do_rush = TRUE;
4050 break;
4052 /*FALLTHRU*/
4053 default:
4054 if (movecmd(*cmd)) { /* ordinary movement */
4055 context.run = 0; /* only matters here if it was 8 */
4056 do_walk = TRUE;
4057 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
4058 context.run = 1;
4059 do_rush = TRUE;
4060 } else if (movecmd(unctrl(*cmd))) {
4061 context.run = 3;
4062 do_rush = TRUE;
4064 break;
4067 /* some special prefix handling */
4068 /* overload 'm' prefix to mean "request a menu" */
4069 if (prefix_seen && cmd[0] == Cmd.spkeys[NHKF_REQMENU]) {
4070 /* (for func_tab cast, see below) */
4071 const struct ext_func_tab *ft = Cmd.commands[cmd[1] & 0xff];
4072 int NDECL((*func)) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0;
4074 if (func && accept_menu_prefix(func)) {
4075 iflags.menu_requested = TRUE;
4076 ++cmd;
4080 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
4081 /* trying to move diagonally as a grid bug;
4082 this used to be treated by movecmd() as not being
4083 a movement attempt, but that didn't provide for any
4084 feedback and led to strangeness if the key pressed
4085 ('u' in particular) was overloaded for num_pad use */
4086 You_cant("get there from here...");
4087 context.run = 0;
4088 context.nopick = context.forcefight = FALSE;
4089 context.move = context.mv = FALSE;
4090 multi = 0;
4091 return;
4094 if (do_walk) {
4095 if (multi)
4096 context.mv = TRUE;
4097 domove();
4098 context.forcefight = 0;
4099 return;
4100 } else if (do_rush) {
4101 if (firsttime) {
4102 if (!multi)
4103 multi = max(COLNO, ROWNO);
4104 u.last_str_turn = 0;
4106 context.mv = TRUE;
4107 domove();
4108 return;
4109 } else if (prefix_seen && cmd[1] == Cmd.spkeys[NHKF_ESC]) {
4110 /* <prefix><escape> */
4111 /* don't report "unknown command" for change of heart... */
4112 bad_command = FALSE;
4113 } else if (*cmd == ' ' && !flags.rest_on_space) {
4114 bad_command = TRUE; /* skip cmdlist[] loop */
4116 /* handle all other commands */
4117 } else {
4118 register const struct ext_func_tab *tlist;
4119 int res, NDECL((*func));
4121 /* current - use *cmd to directly index cmdlist array */
4122 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
4123 if (!wizard && (tlist->flags & WIZMODECMD)) {
4124 You_cant("do that!");
4125 res = 0;
4126 } else if (u.uburied && !(tlist->flags & IFBURIED)) {
4127 You_cant("do that while you are buried!");
4128 res = 0;
4129 } else {
4130 /* we discard 'const' because some compilers seem to have
4131 trouble with the pointer passed to set_occupation() */
4132 func = ((struct ext_func_tab *) tlist)->ef_funct;
4133 if (tlist->f_text && !occupation && multi)
4134 set_occupation(func, tlist->f_text, multi);
4135 res = (*func)(); /* perform the command */
4137 if (!res) {
4138 context.move = FALSE;
4139 multi = 0;
4141 return;
4143 /* if we reach here, cmd wasn't found in cmdlist[] */
4144 bad_command = TRUE;
4147 if (bad_command) {
4148 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
4149 register char c;
4151 expcmd[0] = '\0';
4152 while ((c = *cmd++) != '\0')
4153 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
4155 if (!prefix_seen || !iflags.cmdassist
4156 || !help_dir(0, "Invalid direction key!"))
4157 Norep("Unknown command '%s'.", expcmd);
4159 /* didn't move */
4160 context.move = FALSE;
4161 multi = 0;
4162 return;
4165 /* convert an x,y pair into a direction code */
4167 xytod(x, y)
4168 schar x, y;
4170 register int dd;
4172 for (dd = 0; dd < 8; dd++)
4173 if (x == xdir[dd] && y == ydir[dd])
4174 return dd;
4175 return -1;
4178 /* convert a direction code into an x,y pair */
4179 void
4180 dtoxy(cc, dd)
4181 coord *cc;
4182 register int dd;
4184 cc->x = xdir[dd];
4185 cc->y = ydir[dd];
4186 return;
4189 /* also sets u.dz, but returns false for <> */
4191 movecmd(sym)
4192 char sym;
4194 register const char *dp = index(Cmd.dirchars, sym);
4196 u.dz = 0;
4197 if (!dp || !*dp)
4198 return 0;
4199 u.dx = xdir[dp - Cmd.dirchars];
4200 u.dy = ydir[dp - Cmd.dirchars];
4201 u.dz = zdir[dp - Cmd.dirchars];
4202 #if 0 /* now handled elsewhere */
4203 if (u.dx && u.dy && NODIAG(u.umonnum)) {
4204 u.dx = u.dy = 0;
4205 return 0;
4207 #endif
4208 return !u.dz;
4211 /* grid bug handling which used to be in movecmd() */
4213 dxdy_moveok()
4215 if (u.dx && u.dy && NODIAG(u.umonnum))
4216 u.dx = u.dy = 0;
4217 return u.dx || u.dy;
4220 /* decide whether a character (user input keystroke) requests screen repaint */
4221 boolean
4222 redraw_cmd(c)
4223 char c;
4225 return (boolean) (c == Cmd.spkeys[NHKF_REDRAW]
4226 || (Cmd.num_pad && c == Cmd.spkeys[NHKF_REDRAW2]));
4229 boolean
4230 prefix_cmd(c)
4231 char c;
4233 return (c == Cmd.spkeys[NHKF_RUSH]
4234 || c == Cmd.spkeys[NHKF_RUN]
4235 || c == Cmd.spkeys[NHKF_NOPICKUP]
4236 || c == Cmd.spkeys[NHKF_RUN_NOPICKUP]
4237 || c == Cmd.spkeys[NHKF_FIGHT]
4238 || (Cmd.num_pad && (c == Cmd.spkeys[NHKF_RUN2]
4239 || c == Cmd.spkeys[NHKF_FIGHT2])));
4243 * uses getdir() but unlike getdir() it specifically
4244 * produces coordinates using the direction from getdir()
4245 * and verifies that those coordinates are ok.
4247 * If the call to getdir() returns 0, Never_mind is displayed.
4248 * If the resulting coordinates are not okay, emsg is displayed.
4250 * Returns non-zero if coordinates in cc are valid.
4253 get_adjacent_loc(prompt, emsg, x, y, cc)
4254 const char *prompt, *emsg;
4255 xchar x, y;
4256 coord *cc;
4258 xchar new_x, new_y;
4259 if (!getdir(prompt)) {
4260 pline1(Never_mind);
4261 return 0;
4263 new_x = x + u.dx;
4264 new_y = y + u.dy;
4265 if (cc && isok(new_x, new_y)) {
4266 cc->x = new_x;
4267 cc->y = new_y;
4268 } else {
4269 if (emsg)
4270 pline1(emsg);
4271 return 0;
4273 return 1;
4277 getdir(s)
4278 const char *s;
4280 char dirsym;
4281 int is_mov;
4283 retry:
4284 if (in_doagain || *readchar_queue)
4285 dirsym = readchar();
4286 else
4287 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
4288 (char *) 0, '\0');
4289 /* remove the prompt string so caller won't have to */
4290 clear_nhwindow(WIN_MESSAGE);
4292 if (redraw_cmd(dirsym)) { /* ^R */
4293 docrt(); /* redraw */
4294 goto retry;
4296 savech(dirsym);
4298 if (dirsym == Cmd.spkeys[NHKF_GETDIR_SELF]
4299 || dirsym == Cmd.spkeys[NHKF_GETDIR_SELF2]) {
4300 u.dx = u.dy = u.dz = 0;
4301 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
4302 boolean did_help = FALSE, help_requested;
4304 if (!index(quitchars, dirsym)) {
4305 help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]);
4306 if (help_requested || iflags.cmdassist) {
4307 did_help = help_dir((s && *s == '^') ? dirsym : 0,
4308 help_requested ? (const char *) 0
4309 : "Invalid direction key!");
4310 if (help_requested)
4311 goto retry;
4313 if (!did_help)
4314 pline("What a strange direction!");
4316 return 0;
4317 } else if (is_mov && !dxdy_moveok()) {
4318 You_cant("orient yourself that direction.");
4319 return 0;
4321 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
4322 confdir();
4323 return 1;
4326 STATIC_OVL void
4327 show_direction_keys(win, nodiag)
4328 winid win;
4329 boolean nodiag;
4331 char buf[BUFSZ];
4333 if (nodiag) {
4334 Sprintf(buf, " %c ", Cmd.move_N);
4335 putstr(win, 0, buf);
4336 putstr(win, 0, " | ");
4337 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4338 putstr(win, 0, buf);
4339 putstr(win, 0, " | ");
4340 Sprintf(buf, " %c ", Cmd.move_S);
4341 putstr(win, 0, buf);
4342 } else {
4343 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
4344 Cmd.move_NE);
4345 putstr(win, 0, buf);
4346 putstr(win, 0, " \\ | / ");
4347 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
4348 putstr(win, 0, buf);
4349 putstr(win, 0, " / | \\ ");
4350 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
4351 Cmd.move_SE);
4352 putstr(win, 0, buf);
4356 STATIC_OVL boolean
4357 help_dir(sym, msg)
4358 char sym;
4359 const char *msg;
4361 static const char wiz_only_list[] = "EFGIVW";
4362 char ctrl;
4363 winid win;
4364 char buf[BUFSZ], buf2[BUFSZ], *explain;
4366 win = create_nhwindow(NHW_TEXT);
4367 if (!win)
4368 return FALSE;
4369 if (msg) {
4370 Sprintf(buf, "cmdassist: %s", msg);
4371 putstr(win, 0, buf);
4372 putstr(win, 0, "");
4374 if (letter(sym) || sym == '[') { /* 'dat/cmdhelp' shows ESC as ^[ */
4375 sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */
4376 ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
4377 if ((explain = dowhatdoes_core(ctrl, buf2)) != 0
4378 && (!index(wiz_only_list, sym) || wizard)) {
4379 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
4380 index(wiz_only_list, sym)
4381 ? ""
4382 : " as specified in the Guidebook");
4383 putstr(win, 0, buf);
4384 putstr(win, 0, "");
4385 putstr(win, 0, explain);
4386 putstr(win, 0, "");
4387 putstr(win, 0,
4388 "To use that command, hold down the <Ctrl> key as a shift");
4389 Sprintf(buf, "and press the <%c> key.", sym);
4390 putstr(win, 0, buf);
4391 putstr(win, 0, "");
4395 Sprintf(buf, "Valid direction keys %sare:",
4396 NODIAG(u.umonnum) ? "in your current form " : "");
4397 putstr(win, 0, buf);
4398 show_direction_keys(win, NODIAG(u.umonnum));
4400 putstr(win, 0, "");
4401 putstr(win, 0, " < up");
4402 putstr(win, 0, " > down");
4403 Sprintf(buf, " %4s direct at yourself",
4404 visctrl(Cmd.spkeys[NHKF_GETDIR_SELF]));
4405 putstr(win, 0, buf);
4406 if (msg) {
4407 /* non-null msg means that this wasn't an explicit user request */
4408 putstr(win, 0, "");
4409 putstr(win, 0,
4410 "(Suppress this message with !cmdassist in config file.)");
4412 display_nhwindow(win, FALSE);
4413 destroy_nhwindow(win);
4414 return TRUE;
4417 void
4418 confdir()
4420 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
4422 u.dx = xdir[x];
4423 u.dy = ydir[x];
4424 return;
4427 const char *
4428 directionname(dir)
4429 int dir;
4431 static NEARDATA const char *const dirnames[] = {
4432 "west", "northwest", "north", "northeast", "east",
4433 "southeast", "south", "southwest", "down", "up",
4436 if (dir < 0 || dir >= SIZE(dirnames))
4437 return "invalid";
4438 return dirnames[dir];
4442 isok(x, y)
4443 register int x, y;
4445 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
4446 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
4449 static NEARDATA int last_multi;
4452 * convert a MAP window position into a movecmd
4454 const char *
4455 click_to_cmd(x, y, mod)
4456 int x, y, mod;
4458 int dir;
4459 static char cmd[4];
4460 cmd[1] = 0;
4462 if (iflags.clicklook && mod == CLICK_2) {
4463 clicklook_cc.x = x;
4464 clicklook_cc.y = y;
4465 cmd[0] = Cmd.spkeys[NHKF_CLICKLOOK];
4466 return cmd;
4469 x -= u.ux;
4470 y -= u.uy;
4472 if (flags.travelcmd) {
4473 if (abs(x) <= 1 && abs(y) <= 1) {
4474 x = sgn(x), y = sgn(y);
4475 } else {
4476 u.tx = u.ux + x;
4477 u.ty = u.uy + y;
4478 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4479 return cmd;
4482 if (x == 0 && y == 0) {
4483 /* here */
4484 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
4485 || IS_SINK(levl[u.ux][u.uy].typ)) {
4486 cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip);
4487 return cmd;
4488 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
4489 cmd[0] = cmd_from_func(dosit);
4490 return cmd;
4491 } else if ((u.ux == xupstair && u.uy == yupstair)
4492 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4493 && sstairs.up)
4494 || (u.ux == xupladder && u.uy == yupladder)) {
4495 cmd[0] = cmd_from_func(doup);
4496 return cmd;
4497 } else if ((u.ux == xdnstair && u.uy == ydnstair)
4498 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4499 && !sstairs.up)
4500 || (u.ux == xdnladder && u.uy == ydnladder)) {
4501 cmd[0] = cmd_from_func(dodown);
4502 return cmd;
4503 } else if (OBJ_AT(u.ux, u.uy)) {
4504 cmd[0] = cmd_from_func(Is_container(level.objects[u.ux][u.uy])
4505 ? doloot : dopickup);
4506 return cmd;
4507 } else {
4508 cmd[0] = cmd_from_func(donull); /* just rest */
4509 return cmd;
4513 /* directional commands */
4515 dir = xytod(x, y);
4517 if (!m_at(u.ux + x, u.uy + y)
4518 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
4519 cmd[1] = Cmd.dirchars[dir];
4520 cmd[2] = '\0';
4521 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
4522 /* slight assistance to the player: choose kick/open for them
4524 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
4525 cmd[0] = cmd_from_func(dokick);
4526 return cmd;
4528 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
4529 cmd[0] = cmd_from_func(doopen);
4530 return cmd;
4533 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
4534 cmd[0] = cmd_from_func(dosearch);
4535 cmd[1] = 0;
4536 return cmd;
4539 } else {
4540 /* convert without using floating point, allowing sloppy clicking */
4541 if (x > 2 * abs(y))
4542 x = 1, y = 0;
4543 else if (y > 2 * abs(x))
4544 x = 0, y = 1;
4545 else if (x < -2 * abs(y))
4546 x = -1, y = 0;
4547 else if (y < -2 * abs(x))
4548 x = 0, y = -1;
4549 else
4550 x = sgn(x), y = sgn(y);
4552 if (x == 0 && y == 0) {
4553 /* map click on player to "rest" command */
4554 cmd[0] = cmd_from_func(donull);
4555 return cmd;
4557 dir = xytod(x, y);
4560 /* move, attack, etc. */
4561 cmd[1] = 0;
4562 if (mod == CLICK_1) {
4563 cmd[0] = Cmd.dirchars[dir];
4564 } else {
4565 cmd[0] = (Cmd.num_pad
4566 ? M(Cmd.dirchars[dir])
4567 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
4570 return cmd;
4573 char
4574 get_count(allowchars, inkey, maxcount, count)
4575 char *allowchars;
4576 char inkey;
4577 long maxcount;
4578 long *count;
4580 char qbuf[QBUFSZ];
4581 int key;
4582 long cnt = 0L;
4583 boolean backspaced = FALSE;
4584 /* this should be done in port code so that we have erase_char
4585 and kill_char available; we can at least fake erase_char */
4586 #define STANDBY_erase_char '\177'
4588 for (;;) {
4589 if (inkey) {
4590 key = inkey;
4591 inkey = '\0';
4592 } else
4593 key = readchar();
4595 if (digit(key)) {
4596 cnt = 10L * cnt + (long) (key - '0');
4597 if (cnt < 0)
4598 cnt = 0;
4599 else if (maxcount > 0 && cnt > maxcount)
4600 cnt = maxcount;
4601 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
4602 cnt = cnt / 10;
4603 backspaced = TRUE;
4604 } else if (key == Cmd.spkeys[NHKF_ESC]) {
4605 break;
4606 } else if (!allowchars || index(allowchars, key)) {
4607 *count = cnt;
4608 break;
4611 if (cnt > 9 || backspaced) {
4612 clear_nhwindow(WIN_MESSAGE);
4613 if (backspaced && !cnt) {
4614 Sprintf(qbuf, "Count: ");
4615 } else {
4616 Sprintf(qbuf, "Count: %ld", cnt);
4617 backspaced = FALSE;
4619 pline1(qbuf);
4620 mark_synch();
4623 return key;
4627 STATIC_OVL char *
4628 parse()
4630 #ifdef LINT /* static char in_line[COLNO]; */
4631 char in_line[COLNO];
4632 #else
4633 static char in_line[COLNO];
4634 #endif
4635 register int foo;
4636 boolean prezero = FALSE;
4638 multi = 0;
4639 context.move = 1;
4640 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
4642 #ifdef ALTMETA
4643 alt_esc = iflags.altmeta; /* readchar() hack */
4644 #endif
4645 if (!Cmd.num_pad || (foo = readchar()) == Cmd.spkeys[NHKF_COUNT]) {
4646 long tmpmulti = multi;
4648 foo = get_count((char *) 0, '\0', LARGEST_INT, &tmpmulti);
4649 last_multi = multi = tmpmulti;
4651 #ifdef ALTMETA
4652 alt_esc = FALSE; /* readchar() reset */
4653 #endif
4655 if (foo == Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */
4656 clear_nhwindow(WIN_MESSAGE);
4657 multi = last_multi = 0;
4658 } else if (foo == Cmd.spkeys[NHKF_DOAGAIN] || in_doagain) {
4659 multi = last_multi;
4660 } else {
4661 last_multi = multi;
4662 savech(0); /* reset input queue */
4663 savech((char) foo);
4666 if (multi) {
4667 multi--;
4668 save_cm = in_line;
4669 } else {
4670 save_cm = (char *) 0;
4672 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
4673 if (Cmd.pcHack_compat) {
4674 /* This handles very old inconsistent DOS/Windows behaviour
4675 in a different way: earlier, the keyboard handler mapped
4676 these, which caused counts to be strange when entered
4677 from the number pad. Now do not map them until here. */
4678 switch (foo) {
4679 case '5':
4680 foo = Cmd.spkeys[NHKF_RUSH];
4681 break;
4682 case M('5'):
4683 foo = Cmd.spkeys[NHKF_RUN];
4684 break;
4685 case M('0'):
4686 foo = Cmd.spkeys[NHKF_DOINV];
4687 break;
4688 default:
4689 break; /* as is */
4693 in_line[0] = foo;
4694 in_line[1] = '\0';
4695 if (prefix_cmd(foo)) {
4696 foo = readchar();
4697 savech((char) foo);
4698 in_line[1] = foo;
4699 in_line[2] = 0;
4701 clear_nhwindow(WIN_MESSAGE);
4702 if (prezero)
4703 in_line[0] = Cmd.spkeys[NHKF_ESC];
4704 return in_line;
4707 #ifdef HANGUPHANDLING
4708 /* some very old systems, or descendents of such systems, expect signal
4709 handlers to have return type `int', but they don't actually inspect
4710 the return value so we should be safe using `void' unconditionally */
4711 /*ARGUSED*/
4712 void
4713 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4714 int sig_unused UNUSED;
4716 if (program_state.exiting)
4717 program_state.in_moveloop = 0;
4718 nhwindows_hangup();
4719 #ifdef SAFERHANGUP
4720 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4721 and a couple of other places; actual hangup handling occurs then.
4722 This is 'safer' because it disallows certain cheats and also
4723 protects against losing objects in the process of being thrown,
4724 but also potentially riskier because the disconnected program
4725 must continue running longer before attempting a hangup save. */
4726 program_state.done_hup++;
4727 /* defer hangup iff game appears to be in progress */
4728 if (program_state.in_moveloop && program_state.something_worth_saving)
4729 return;
4730 #endif /* SAFERHANGUP */
4731 end_of_input();
4734 void
4735 end_of_input()
4737 #ifdef NOSAVEONHANGUP
4738 #ifdef INSURANCE
4739 if (flags.ins_chkpt && program_state.something_worth_saving)
4740 program_statue.preserve_locks = 1; /* keep files for recovery */
4741 #endif
4742 program_state.something_worth_saving = 0; /* don't save */
4743 #endif
4745 #ifndef SAFERHANGUP
4746 if (!program_state.done_hup++)
4747 #endif
4748 if (program_state.something_worth_saving)
4749 (void) dosave0();
4750 if (iflags.window_inited)
4751 exit_nhwindows((char *) 0);
4752 clearlocks();
4753 terminate(EXIT_SUCCESS);
4754 /*NOTREACHED*/ /* not necessarily true for vms... */
4755 return;
4757 #endif /* HANGUPHANDLING */
4759 char
4760 readchar()
4762 register int sym;
4763 int x = u.ux, y = u.uy, mod = 0;
4765 if (*readchar_queue)
4766 sym = *readchar_queue++;
4767 else
4768 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4770 #ifdef NR_OF_EOFS
4771 if (sym == EOF) {
4772 register int cnt = NR_OF_EOFS;
4774 * Some SYSV systems seem to return EOFs for various reasons
4775 * (?like when one hits break or for interrupted systemcalls?),
4776 * and we must see several before we quit.
4778 do {
4779 clearerr(stdin); /* omit if clearerr is undefined */
4780 sym = pgetchar();
4781 } while (--cnt && sym == EOF);
4783 #endif /* NR_OF_EOFS */
4785 if (sym == EOF) {
4786 #ifdef HANGUPHANDLING
4787 hangup(0); /* call end_of_input() or set program_state.done_hup */
4788 #endif
4789 sym = '\033';
4790 #ifdef ALTMETA
4791 } else if (sym == '\033' && alt_esc) {
4792 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4793 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4794 if (sym == EOF || sym == 0)
4795 sym = '\033';
4796 else if (sym != '\033')
4797 sym |= 0200; /* force 8th bit on */
4798 #endif /*ALTMETA*/
4799 } else if (sym == 0) {
4800 /* click event */
4801 readchar_queue = click_to_cmd(x, y, mod);
4802 sym = *readchar_queue++;
4804 return (char) sym;
4807 STATIC_PTR int
4808 dotravel(VOID_ARGS)
4810 /* Keyboard travel command */
4811 static char cmd[2];
4812 coord cc;
4814 if (!flags.travelcmd)
4815 return 0;
4816 cmd[1] = 0;
4817 cc.x = iflags.travelcc.x;
4818 cc.y = iflags.travelcc.y;
4819 if (cc.x == -1 && cc.y == -1) {
4820 /* No cached destination, start attempt from current position */
4821 cc.x = u.ux;
4822 cc.y = u.uy;
4824 iflags.getloc_travelmode = TRUE;
4825 if (iflags.menu_requested) {
4826 if (!getpos_menu(&cc, TRUE, GLOC_INTERESTING)) {
4827 iflags.getloc_travelmode = FALSE;
4828 return 0;
4830 } else {
4831 pline("Where do you want to travel to?");
4832 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4833 /* user pressed ESC */
4834 iflags.getloc_travelmode = FALSE;
4835 return 0;
4838 iflags.getloc_travelmode = FALSE;
4839 iflags.travelcc.x = u.tx = cc.x;
4840 iflags.travelcc.y = u.ty = cc.y;
4841 cmd[0] = Cmd.spkeys[NHKF_TRAVEL];
4842 readchar_queue = cmd;
4843 return 0;
4846 #ifdef PORT_DEBUG
4847 extern void NDECL(win32con_debug_keystrokes);
4848 extern void NDECL(win32con_handler_info);
4851 wiz_port_debug()
4853 int n, k;
4854 winid win;
4855 anything any;
4856 int item = 'a';
4857 int num_menu_selections;
4858 struct menu_selection_struct {
4859 char *menutext;
4860 void NDECL((*fn));
4861 } menu_selections[] = {
4862 #ifdef WIN32
4863 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4864 { "show keystroke handler information (tty only)",
4865 win32con_handler_info },
4866 #endif
4867 { (char *) 0, (void NDECL((*))) 0 } /* array terminator */
4870 num_menu_selections = SIZE(menu_selections) - 1;
4871 if (num_menu_selections > 0) {
4872 menu_item *pick_list;
4873 win = create_nhwindow(NHW_MENU);
4874 start_menu(win);
4875 for (k = 0; k < num_menu_selections; ++k) {
4876 any.a_int = k + 1;
4877 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4878 menu_selections[k].menutext, MENU_UNSELECTED);
4880 end_menu(win, "Which port debugging feature?");
4881 n = select_menu(win, PICK_ONE, &pick_list);
4882 destroy_nhwindow(win);
4883 if (n > 0) {
4884 n = pick_list[0].item.a_int - 1;
4885 free((genericptr_t) pick_list);
4886 /* execute the function */
4887 (*menu_selections[n].fn)();
4889 } else
4890 pline("No port-specific debug capability defined.");
4891 return 0;
4893 #endif /*PORT_DEBUG*/
4896 * Parameter validator for generic yes/no function to prevent
4897 * the core from sending too long a prompt string to the
4898 * window port causing a buffer overflow there.
4900 char
4901 yn_function(query, resp, def)
4902 const char *query, *resp;
4903 char def;
4905 char qbuf[QBUFSZ];
4907 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4909 /* maximum acceptable length is QBUFSZ-1 */
4910 if (strlen(query) >= QBUFSZ) {
4911 /* caller shouldn't have passed anything this long */
4912 paniclog("Query truncated: ", query);
4913 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4914 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4915 query = qbuf;
4917 return (*windowprocs.win_yn_function)(query, resp, def);
4920 /* for paranoid_confirm:quit,die,attack prompting */
4921 boolean
4922 paranoid_query(be_paranoid, prompt)
4923 boolean be_paranoid;
4924 const char *prompt;
4926 boolean confirmed_ok;
4928 /* when paranoid, player must respond with "yes" rather than just 'y'
4929 to give the go-ahead for this query; default is "no" unless the
4930 ParanoidConfirm flag is set in which case there's no default */
4931 if (be_paranoid) {
4932 char qbuf[QBUFSZ], ans[BUFSZ];
4933 const char *promptprefix = "", *responsetype = ParanoidConfirm
4934 ? "(yes|no)"
4935 : "(yes) [no]";
4936 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4938 /* in addition to being paranoid about this particular
4939 query, we might be even more paranoid about all paranoia
4940 responses (ie, ParanoidConfirm is set) in which case we
4941 require "no" to reject in addition to "yes" to confirm
4942 (except we won't loop if response is ESC; it means no) */
4943 do {
4944 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4945 getlin(qbuf, ans);
4946 (void) mungspaces(ans);
4947 confirmed_ok = !strcmpi(ans, "yes");
4948 if (confirmed_ok || *ans == '\033')
4949 break;
4950 promptprefix = "\"Yes\" or \"No\": ";
4951 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4952 } else
4953 confirmed_ok = (yn(prompt) == 'y');
4955 return confirmed_ok;
4959 dosuspend_core()
4961 #ifdef SUSPEND
4962 /* Does current window system support suspend? */
4963 if ((*windowprocs.win_can_suspend)()) {
4964 /* NB: SYSCF SHELLERS handled in port code. */
4965 dosuspend();
4966 } else
4967 #endif
4968 Norep("Suspend command not available.");
4969 return 0;
4972 /*cmd.c*/