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