Separate function to check for prefix command keys
[aNetHack.git] / src / cmd.c
blob31e2a4bd150afcd9c5c7a13cf6a217e036955e06
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(dooverview_or_wiz_where);
121 STATIC_PTR int NDECL(dotravel);
122 STATIC_PTR int NDECL(doterrain);
123 STATIC_PTR int NDECL(wiz_wish);
124 STATIC_PTR int NDECL(wiz_identify);
125 STATIC_PTR int NDECL(wiz_intrinsic);
126 STATIC_PTR int NDECL(wiz_map);
127 STATIC_PTR int NDECL(wiz_genesis);
128 STATIC_PTR int NDECL(wiz_where);
129 STATIC_PTR int NDECL(wiz_detect);
130 STATIC_PTR int NDECL(wiz_panic);
131 STATIC_PTR int NDECL(wiz_polyself);
132 STATIC_PTR int NDECL(wiz_level_tele);
133 STATIC_PTR int NDECL(wiz_level_change);
134 STATIC_PTR int NDECL(wiz_show_seenv);
135 STATIC_PTR int NDECL(wiz_show_vision);
136 STATIC_PTR int NDECL(wiz_smell);
137 STATIC_PTR int NDECL(wiz_mon_polycontrol);
138 STATIC_PTR int NDECL(wiz_show_wmodes);
139 STATIC_DCL void NDECL(wiz_map_levltyp);
140 STATIC_DCL void NDECL(wiz_levltyp_legend);
141 #if defined(__BORLANDC__) && !defined(_WIN32)
142 extern void FDECL(show_borlandc_stats, (winid));
143 #endif
144 #ifdef DEBUG_MIGRATING_MONS
145 STATIC_PTR int NDECL(wiz_migrate_mons);
146 #endif
147 STATIC_DCL int FDECL(size_monst, (struct monst *, BOOLEAN_P));
148 STATIC_DCL int FDECL(size_obj, (struct obj *));
149 STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *,
150 BOOLEAN_P, BOOLEAN_P));
151 STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *,
152 BOOLEAN_P, long *, long *));
153 STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *,
154 long *, long *));
155 STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *,
156 BOOLEAN_P, long *, long *));
157 STATIC_DCL void FDECL(contained_stats, (winid, const char *, long *, long *));
158 STATIC_DCL void FDECL(misc_stats, (winid, long *, long *));
159 STATIC_PTR int NDECL(wiz_show_stats);
160 STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*))));
161 #ifdef PORT_DEBUG
162 STATIC_DCL int NDECL(wiz_port_debug);
163 #endif
164 STATIC_PTR int NDECL(wiz_rumor_check);
165 STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*))));
166 STATIC_PTR int NDECL(doattributes);
167 STATIC_PTR int NDECL(doconduct); /**/
169 STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *,
170 const char *));
171 STATIC_DCL char *FDECL(enlght_combatinc, (const char *, int, int, char *));
172 STATIC_DCL void FDECL(enlght_halfdmg, (int, int));
173 STATIC_DCL boolean NDECL(walking_on_water);
174 STATIC_DCL boolean FDECL(cause_known, (int));
175 STATIC_DCL char *FDECL(attrval, (int, int, char *));
176 STATIC_DCL void FDECL(background_enlightenment, (int, int));
177 STATIC_DCL void FDECL(characteristics_enlightenment, (int, int));
178 STATIC_DCL void FDECL(one_characteristic, (int, int, int));
179 STATIC_DCL void FDECL(status_enlightenment, (int, int));
180 STATIC_DCL void FDECL(attributes_enlightenment, (int, int));
182 static const char *readchar_queue = "";
183 static coord clicklook_cc;
185 STATIC_DCL char *NDECL(parse);
186 STATIC_DCL void FDECL(show_direction_keys, (winid, BOOLEAN_P));
187 STATIC_DCL boolean FDECL(help_dir, (CHAR_P, const char *));
189 STATIC_PTR int
190 doprev_message(VOID_ARGS)
192 return nh_doprev_message();
195 /* Count down by decrementing multi */
196 STATIC_PTR int
197 timed_occupation(VOID_ARGS)
199 (*timed_occ_fn)();
200 if (multi > 0)
201 multi--;
202 return multi > 0;
205 /* If you have moved since initially setting some occupations, they
206 * now shouldn't be able to restart.
208 * The basic rule is that if you are carrying it, you can continue
209 * since it is with you. If you are acting on something at a distance,
210 * your orientation to it must have changed when you moved.
212 * The exception to this is taking off items, since they can be taken
213 * off in a number of ways in the intervening time, screwing up ordering.
215 * Currently: Take off all armor.
216 * Picking Locks / Forcing Chests.
217 * Setting traps.
219 void
220 reset_occupations()
222 reset_remarm();
223 reset_pick();
224 reset_trapset();
227 /* If a time is given, use it to timeout this function, otherwise the
228 * function times out by its own means.
230 void
231 set_occupation(fn, txt, xtime)
232 int NDECL((*fn));
233 const char *txt;
234 int xtime;
236 if (xtime) {
237 occupation = timed_occupation;
238 timed_occ_fn = fn;
239 } else
240 occupation = fn;
241 occtxt = txt;
242 occtime = 0;
243 return;
246 STATIC_DCL char NDECL(popch);
248 /* Provide a means to redo the last command. The flag `in_doagain' is set
249 * to true while redoing the command. This flag is tested in commands that
250 * require additional input (like `throw' which requires a thing and a
251 * direction), and the input prompt is not shown. Also, while in_doagain is
252 * TRUE, no keystrokes can be saved into the saveq.
254 #define BSIZE 20
255 static char pushq[BSIZE], saveq[BSIZE];
256 static NEARDATA int phead, ptail, shead, stail;
258 STATIC_OVL char
259 popch()
261 /* If occupied, return '\0', letting tgetch know a character should
262 * be read from the keyboard. If the character read is not the
263 * ABORT character (as checked in pcmain.c), that character will be
264 * pushed back on the pushq.
266 if (occupation)
267 return '\0';
268 if (in_doagain)
269 return (char) ((shead != stail) ? saveq[stail++] : '\0');
270 else
271 return (char) ((phead != ptail) ? pushq[ptail++] : '\0');
274 char
275 pgetchar() /* courtesy of aeb@cwi.nl */
277 register int ch;
279 if (!(ch = popch()))
280 ch = nhgetch();
281 return (char) ch;
284 /* A ch == 0 resets the pushq */
285 void
286 pushch(ch)
287 char ch;
289 if (!ch)
290 phead = ptail = 0;
291 if (phead < BSIZE)
292 pushq[phead++] = ch;
293 return;
296 /* A ch == 0 resets the saveq. Only save keystrokes when not
297 * replaying a previous command.
299 void
300 savech(ch)
301 char ch;
303 if (!in_doagain) {
304 if (!ch)
305 phead = ptail = shead = stail = 0;
306 else if (shead < BSIZE)
307 saveq[shead++] = ch;
309 return;
312 /* here after # - now read a full-word command */
313 STATIC_PTR int
314 doextcmd(VOID_ARGS)
316 int idx, retval;
317 int NDECL((*func));
319 /* keep repeating until we don't run help or quit */
320 do {
321 idx = get_ext_cmd();
322 if (idx < 0)
323 return 0; /* quit */
325 func = extcmdlist[idx].ef_funct;
326 if (iflags.menu_requested && !accept_menu_prefix(func)) {
327 pline("'m' prefix has no effect for this command.");
328 iflags.menu_requested = FALSE;
330 retval = (*func)();
331 } while (func == doextlist);
333 return retval;
336 /* here after #? - now list all full-word commands */
338 doextlist(VOID_ARGS)
340 register const struct ext_func_tab *efp;
341 char buf[BUFSZ];
342 winid datawin;
344 datawin = create_nhwindow(NHW_TEXT);
345 putstr(datawin, 0, "");
346 putstr(datawin, 0, " Extended Commands List");
347 putstr(datawin, 0, "");
348 putstr(datawin, 0, " Press '#', then type:");
349 putstr(datawin, 0, "");
351 for (efp = extcmdlist; efp->ef_txt; efp++) {
352 Sprintf(buf, " %-15s - %s.", efp->ef_txt, efp->ef_desc);
353 putstr(datawin, 0, buf);
355 display_nhwindow(datawin, FALSE);
356 destroy_nhwindow(datawin);
357 return 0;
360 #ifdef TTY_GRAPHICS
361 #define MAX_EXT_CMD 50 /* Change if we ever have > 50 ext cmds */
364 * This is currently used only by the tty port and is
365 * controlled via runtime option 'extmenu'.
366 * ``# ?'' is counted towards the limit of the number of commands,
367 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
369 * Here after # - now show pick-list of possible commands.
372 extcmd_via_menu()
374 const struct ext_func_tab *efp;
375 menu_item *pick_list = (menu_item *) 0;
376 winid win;
377 anything any;
378 const struct ext_func_tab *choices[MAX_EXT_CMD + 1];
379 char buf[BUFSZ];
380 char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
381 int i, n, nchoices, acount;
382 int ret, biggest;
383 int accelerator, prevaccelerator;
384 int matchlevel = 0;
386 ret = 0;
387 cbuf[0] = '\0';
388 biggest = 0;
389 while (!ret) {
390 i = n = 0;
391 any = zeroany;
392 /* populate choices */
393 for (efp = extcmdlist; efp->ef_txt; efp++) {
394 if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
395 choices[i] = efp;
396 if ((int) strlen(efp->ef_desc) > biggest) {
397 biggest = strlen(efp->ef_desc);
398 Sprintf(fmtstr, "%%-%ds", biggest + 15);
400 if (++i > MAX_EXT_CMD) {
401 #if defined(BETA)
402 impossible(
403 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
404 MAX_EXT_CMD);
405 #endif /* BETA */
406 iflags.extmenu = 0;
407 return -1;
411 choices[i] = (struct ext_func_tab *) 0;
412 nchoices = i;
413 /* if we're down to one, we have our selection so get out of here */
414 if (nchoices == 1) {
415 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
416 if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) {
417 ret = i;
418 break;
420 break;
423 /* otherwise... */
424 win = create_nhwindow(NHW_MENU);
425 start_menu(win);
426 accelerator = prevaccelerator = 0;
427 acount = 0;
428 for (i = 0; choices[i]; ++i) {
429 accelerator = choices[i]->ef_txt[matchlevel];
430 if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) {
431 if (acount) {
432 /* flush extended cmds for that letter already in buf */
433 Sprintf(buf, fmtstr, prompt);
434 any.a_char = prevaccelerator;
435 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE,
436 buf, FALSE);
437 acount = 0;
440 prevaccelerator = accelerator;
441 if (!acount || nchoices < (ROWNO - 3)) {
442 Sprintf(prompt, "%s [%s]", choices[i]->ef_txt,
443 choices[i]->ef_desc);
444 } else if (acount == 1) {
445 Sprintf(prompt, "%s or %s", choices[i - 1]->ef_txt,
446 choices[i]->ef_txt);
447 } else {
448 Strcat(prompt, " or ");
449 Strcat(prompt, choices[i]->ef_txt);
451 ++acount;
453 if (acount) {
454 /* flush buf */
455 Sprintf(buf, fmtstr, prompt);
456 any.a_char = prevaccelerator;
457 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf,
458 FALSE);
460 Sprintf(prompt, "Extended Command: %s", cbuf);
461 end_menu(win, prompt);
462 n = select_menu(win, PICK_ONE, &pick_list);
463 destroy_nhwindow(win);
464 if (n == 1) {
465 if (matchlevel > (QBUFSZ - 2)) {
466 free((genericptr_t) pick_list);
467 #if defined(BETA)
468 impossible("Too many chars (%d) entered in extcmd_via_menu()",
469 matchlevel);
470 #endif
471 ret = -1;
472 } else {
473 cbuf[matchlevel++] = pick_list[0].item.a_char;
474 cbuf[matchlevel] = '\0';
475 free((genericptr_t) pick_list);
477 } else {
478 if (matchlevel) {
479 ret = 0;
480 matchlevel = 0;
481 } else
482 ret = -1;
485 return ret;
487 #endif /* TTY_GRAPHICS */
489 /* #monster command - use special monster ability while polymorphed */
490 STATIC_PTR int
491 domonability(VOID_ARGS)
493 if (can_breathe(youmonst.data))
494 return dobreathe();
495 else if (attacktype(youmonst.data, AT_SPIT))
496 return dospit();
497 else if (youmonst.data->mlet == S_NYMPH)
498 return doremove();
499 else if (attacktype(youmonst.data, AT_GAZE))
500 return dogaze();
501 else if (is_were(youmonst.data))
502 return dosummon();
503 else if (webmaker(youmonst.data))
504 return dospinweb();
505 else if (is_hider(youmonst.data))
506 return dohide();
507 else if (is_mind_flayer(youmonst.data))
508 return domindblast();
509 else if (u.umonnum == PM_GREMLIN) {
510 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
511 if (split_mon(&youmonst, (struct monst *) 0))
512 dryup(u.ux, u.uy, TRUE);
513 } else
514 There("is no fountain here.");
515 } else if (is_unicorn(youmonst.data)) {
516 use_unicorn_horn((struct obj *) 0);
517 return 1;
518 } else if (youmonst.data->msound == MS_SHRIEK) {
519 You("shriek.");
520 if (u.uburied)
521 pline("Unfortunately sound does not carry well through rock.");
522 else
523 aggravate();
524 } else if (youmonst.data->mlet == S_VAMPIRE)
525 return dopoly();
526 else if (Upolyd)
527 pline("Any special ability you may have is purely reflexive.");
528 else
529 You("don't have a special ability in your normal form!");
530 return 0;
534 enter_explore_mode(VOID_ARGS)
536 if (wizard) {
537 You("are in debug mode.");
538 } else if (discover) {
539 You("are already in explore mode.");
540 } else {
541 #ifdef SYSCF
542 #if defined(UNIX)
543 if (!sysopt.explorers || !sysopt.explorers[0]
544 || !check_user_string(sysopt.explorers)) {
545 You("cannot access explore mode.");
546 return 0;
548 #endif
549 #endif
550 pline(
551 "Beware! From explore mode there will be no return to normal game.");
552 if (paranoid_query(ParanoidQuit,
553 "Do you want to enter explore mode?")) {
554 clear_nhwindow(WIN_MESSAGE);
555 You("are now in non-scoring explore mode.");
556 discover = TRUE;
557 } else {
558 clear_nhwindow(WIN_MESSAGE);
559 pline("Resuming normal game.");
562 return 0;
565 STATIC_PTR int
566 dooverview_or_wiz_where(VOID_ARGS)
568 if (wizard)
569 return wiz_where();
570 else
571 dooverview();
572 return 0;
575 /* ^W command - wish for something */
576 STATIC_PTR int
577 wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */
579 if (wizard) {
580 boolean save_verbose = flags.verbose;
582 flags.verbose = FALSE;
583 makewish();
584 flags.verbose = save_verbose;
585 (void) encumber_msg();
586 } else
587 pline("Unavailable command '%s'.",
588 visctrl((int) cmd_from_func(wiz_wish)));
589 return 0;
592 /* ^I command - reveal and optionally identify hero's inventory */
593 STATIC_PTR int
594 wiz_identify(VOID_ARGS)
596 if (wizard) {
597 iflags.override_ID = (int) cmd_from_func(wiz_identify);
598 if (display_inventory((char *) 0, TRUE) == -1)
599 identify_pack(0, FALSE);
600 iflags.override_ID = 0;
601 } else
602 pline("Unavailable command '%s'.",
603 visctrl((int) cmd_from_func(wiz_identify)));
604 return 0;
607 /* ^F command - reveal the level map and any traps on it */
608 STATIC_PTR int
609 wiz_map(VOID_ARGS)
611 if (wizard) {
612 struct trap *t;
613 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
615 HConfusion = HHallucination = 0L;
616 for (t = ftrap; t != 0; t = t->ntrap) {
617 t->tseen = 1;
618 map_trap(t, TRUE);
620 do_mapping();
621 HConfusion = save_Hconf;
622 HHallucination = save_Hhallu;
623 } else
624 pline("Unavailable command '%s'.",
625 visctrl((int) cmd_from_func(wiz_map)));
626 return 0;
629 /* ^G command - generate monster(s); a count prefix will be honored */
630 STATIC_PTR int
631 wiz_genesis(VOID_ARGS)
633 if (wizard)
634 (void) create_particular();
635 else
636 pline("Unavailable command '%s'.",
637 visctrl((int) cmd_from_func(wiz_genesis)));
638 return 0;
641 /* ^O command - display dungeon layout */
642 STATIC_PTR int
643 wiz_where(VOID_ARGS)
645 if (wizard)
646 (void) print_dungeon(FALSE, (schar *) 0, (xchar *) 0);
647 else
648 pline("Unavailable command '%s'.",
649 visctrl((int) cmd_from_func(wiz_where)));
650 return 0;
653 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
654 STATIC_PTR int
655 wiz_detect(VOID_ARGS)
657 if (wizard)
658 (void) findit();
659 else
660 pline("Unavailable command '%s'.",
661 visctrl((int) cmd_from_func(wiz_detect)));
662 return 0;
665 /* ^V command - level teleport */
666 STATIC_PTR int
667 wiz_level_tele(VOID_ARGS)
669 if (wizard)
670 level_tele();
671 else
672 pline("Unavailable command '%s'.",
673 visctrl((int) cmd_from_func(wiz_level_tele)));
674 return 0;
677 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
678 STATIC_PTR int
679 wiz_mon_polycontrol(VOID_ARGS)
681 iflags.mon_polycontrol = !iflags.mon_polycontrol;
682 pline("Monster polymorph control is %s.",
683 iflags.mon_polycontrol ? "on" : "off");
684 return 0;
687 /* #levelchange command - adjust hero's experience level */
688 STATIC_PTR int
689 wiz_level_change(VOID_ARGS)
691 char buf[BUFSZ];
692 int newlevel;
693 int ret;
695 getlin("To what experience level do you want to be set?", buf);
696 (void) mungspaces(buf);
697 if (buf[0] == '\033' || buf[0] == '\0')
698 ret = 0;
699 else
700 ret = sscanf(buf, "%d", &newlevel);
702 if (ret != 1) {
703 pline1(Never_mind);
704 return 0;
706 if (newlevel == u.ulevel) {
707 You("are already that experienced.");
708 } else if (newlevel < u.ulevel) {
709 if (u.ulevel == 1) {
710 You("are already as inexperienced as you can get.");
711 return 0;
713 if (newlevel < 1)
714 newlevel = 1;
715 while (u.ulevel > newlevel)
716 losexp("#levelchange");
717 } else {
718 if (u.ulevel >= MAXULEV) {
719 You("are already as experienced as you can get.");
720 return 0;
722 if (newlevel > MAXULEV)
723 newlevel = MAXULEV;
724 while (u.ulevel < newlevel)
725 pluslvl(FALSE);
727 u.ulevelmax = u.ulevel;
728 return 0;
731 /* #panic command - test program's panic handling */
732 STATIC_PTR int
733 wiz_panic(VOID_ARGS)
735 if (yn("Do you want to call panic() and end your game?") == 'y')
736 panic("Crash test.");
737 return 0;
740 /* #polyself command - change hero's form */
741 STATIC_PTR int
742 wiz_polyself(VOID_ARGS)
744 polyself(1);
745 return 0;
748 /* #seenv command */
749 STATIC_PTR int
750 wiz_show_seenv(VOID_ARGS)
752 winid win;
753 int x, y, v, startx, stopx, curx;
754 char row[COLNO + 1];
756 win = create_nhwindow(NHW_TEXT);
758 * Each seenv description takes up 2 characters, so center
759 * the seenv display around the hero.
761 startx = max(1, u.ux - (COLNO / 4));
762 stopx = min(startx + (COLNO / 2), COLNO);
763 /* can't have a line exactly 80 chars long */
764 if (stopx - startx == COLNO / 2)
765 startx++;
767 for (y = 0; y < ROWNO; y++) {
768 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
769 if (x == u.ux && y == u.uy) {
770 row[curx] = row[curx + 1] = '@';
771 } else {
772 v = levl[x][y].seenv & 0xff;
773 if (v == 0)
774 row[curx] = row[curx + 1] = ' ';
775 else
776 Sprintf(&row[curx], "%02x", v);
779 /* remove trailing spaces */
780 for (x = curx - 1; x >= 0; x--)
781 if (row[x] != ' ')
782 break;
783 row[x + 1] = '\0';
785 putstr(win, 0, row);
787 display_nhwindow(win, TRUE);
788 destroy_nhwindow(win);
789 return 0;
792 /* #vision command */
793 STATIC_PTR int
794 wiz_show_vision(VOID_ARGS)
796 winid win;
797 int x, y, v;
798 char row[COLNO + 1];
800 win = create_nhwindow(NHW_TEXT);
801 Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
802 COULD_SEE, IN_SIGHT, TEMP_LIT);
803 putstr(win, 0, row);
804 putstr(win, 0, "");
805 for (y = 0; y < ROWNO; y++) {
806 for (x = 1; x < COLNO; x++) {
807 if (x == u.ux && y == u.uy)
808 row[x] = '@';
809 else {
810 v = viz_array[y][x]; /* data access should be hidden */
811 if (v == 0)
812 row[x] = ' ';
813 else
814 row[x] = '0' + viz_array[y][x];
817 /* remove trailing spaces */
818 for (x = COLNO - 1; x >= 1; x--)
819 if (row[x] != ' ')
820 break;
821 row[x + 1] = '\0';
823 putstr(win, 0, &row[1]);
825 display_nhwindow(win, TRUE);
826 destroy_nhwindow(win);
827 return 0;
830 /* #wmode command */
831 STATIC_PTR int
832 wiz_show_wmodes(VOID_ARGS)
834 winid win;
835 int x, y;
836 char row[COLNO + 1];
837 struct rm *lev;
838 boolean istty = !strcmp(windowprocs.name, "tty");
840 win = create_nhwindow(NHW_TEXT);
841 if (istty)
842 putstr(win, 0, ""); /* tty only: blank top line */
843 for (y = 0; y < ROWNO; y++) {
844 for (x = 0; x < COLNO; x++) {
845 lev = &levl[x][y];
846 if (x == u.ux && y == u.uy)
847 row[x] = '@';
848 else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
849 row[x] = '0' + (lev->wall_info & WM_MASK);
850 else if (lev->typ == CORR)
851 row[x] = '#';
852 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
853 row[x] = '.';
854 else
855 row[x] = 'x';
857 row[COLNO] = '\0';
858 /* map column 0, levl[0][], is off the left edge of the screen */
859 putstr(win, 0, &row[1]);
861 display_nhwindow(win, TRUE);
862 destroy_nhwindow(win);
863 return 0;
866 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
867 STATIC_OVL void
868 wiz_map_levltyp(VOID_ARGS)
870 winid win;
871 int x, y, terrain;
872 char row[COLNO + 1];
873 boolean istty = !strcmp(windowprocs.name, "tty");
875 win = create_nhwindow(NHW_TEXT);
876 /* map row 0, levl[][0], is drawn on the second line of tty screen */
877 if (istty)
878 putstr(win, 0, ""); /* tty only: blank top line */
879 for (y = 0; y < ROWNO; y++) {
880 /* map column 0, levl[0][], is off the left edge of the screen;
881 it should always have terrain type "undiggable stone" */
882 for (x = 1; x < COLNO; x++) {
883 terrain = levl[x][y].typ;
884 /* assumes there aren't more than 10+26+26 terrain types */
885 row[x - 1] = (char) ((terrain == 0 && !may_dig(x, y))
886 ? '*'
887 : (terrain < 10)
888 ? '0' + terrain
889 : (terrain < 36)
890 ? 'a' + terrain - 10
891 : 'A' + terrain - 36);
893 if (levl[0][y].typ != 0 || may_dig(0, y))
894 row[x++] = '!';
895 row[x] = '\0';
896 putstr(win, 0, row);
900 char dsc[BUFSZ];
901 s_level *slev = Is_special(&u.uz);
903 Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel);
904 /* [dungeon branch features currently omitted] */
905 /* special level features */
906 if (slev) {
907 Sprintf(eos(dsc), " \"%s\"", slev->proto);
908 /* special level flags (note: dungeon.def doesn't set `maze'
909 or `hell' for any specific levels so those never show up) */
910 if (slev->flags.maze_like)
911 Strcat(dsc, " mazelike");
912 if (slev->flags.hellish)
913 Strcat(dsc, " hellish");
914 if (slev->flags.town)
915 Strcat(dsc, " town");
916 if (slev->flags.rogue_like)
917 Strcat(dsc, " roguelike");
918 /* alignment currently omitted to save space */
920 /* level features */
921 if (level.flags.nfountains)
922 Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym,
923 (int) level.flags.nfountains);
924 if (level.flags.nsinks)
925 Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym,
926 (int) level.flags.nsinks);
927 if (level.flags.has_vault)
928 Strcat(dsc, " vault");
929 if (level.flags.has_shop)
930 Strcat(dsc, " shop");
931 if (level.flags.has_temple)
932 Strcat(dsc, " temple");
933 if (level.flags.has_court)
934 Strcat(dsc, " throne");
935 if (level.flags.has_zoo)
936 Strcat(dsc, " zoo");
937 if (level.flags.has_morgue)
938 Strcat(dsc, " morgue");
939 if (level.flags.has_barracks)
940 Strcat(dsc, " barracks");
941 if (level.flags.has_beehive)
942 Strcat(dsc, " hive");
943 if (level.flags.has_swamp)
944 Strcat(dsc, " swamp");
945 /* level flags */
946 if (level.flags.noteleport)
947 Strcat(dsc, " noTport");
948 if (level.flags.hardfloor)
949 Strcat(dsc, " noDig");
950 if (level.flags.nommap)
951 Strcat(dsc, " noMMap");
952 if (!level.flags.hero_memory)
953 Strcat(dsc, " noMem");
954 if (level.flags.shortsighted)
955 Strcat(dsc, " shortsight");
956 if (level.flags.graveyard)
957 Strcat(dsc, " graveyard");
958 if (level.flags.is_maze_lev)
959 Strcat(dsc, " maze");
960 if (level.flags.is_cavernous_lev)
961 Strcat(dsc, " cave");
962 if (level.flags.arboreal)
963 Strcat(dsc, " tree");
964 if (Sokoban)
965 Strcat(dsc, " sokoban-rules");
966 /* non-flag info; probably should include dungeon branching
967 checks (extra stairs and magic portals) here */
968 if (Invocation_lev(&u.uz))
969 Strcat(dsc, " invoke");
970 if (On_W_tower_level(&u.uz))
971 Strcat(dsc, " tower");
972 /* append a branch identifier for completeness' sake */
973 if (u.uz.dnum == 0)
974 Strcat(dsc, " dungeon");
975 else if (u.uz.dnum == mines_dnum)
976 Strcat(dsc, " mines");
977 else if (In_sokoban(&u.uz))
978 Strcat(dsc, " sokoban");
979 else if (u.uz.dnum == quest_dnum)
980 Strcat(dsc, " quest");
981 else if (Is_knox(&u.uz))
982 Strcat(dsc, " ludios");
983 else if (u.uz.dnum == 1)
984 Strcat(dsc, " gehennom");
985 else if (u.uz.dnum == tower_dnum)
986 Strcat(dsc, " vlad");
987 else if (In_endgame(&u.uz))
988 Strcat(dsc, " endgame");
989 else {
990 /* somebody's added a dungeon branch we're not expecting */
991 const char *brname = dungeons[u.uz.dnum].dname;
993 if (!brname || !*brname)
994 brname = "unknown";
995 if (!strncmpi(brname, "the ", 4))
996 brname += 4;
997 Sprintf(eos(dsc), " %s", brname);
999 /* limit the line length to map width */
1000 if (strlen(dsc) >= COLNO)
1001 dsc[COLNO - 1] = '\0'; /* truncate */
1002 putstr(win, 0, dsc);
1005 display_nhwindow(win, TRUE);
1006 destroy_nhwindow(win);
1007 return;
1010 /* temporary? hack, since level type codes aren't the same as screen
1011 symbols and only the latter have easily accessible descriptions */
1012 static const char *levltyp[] = {
1013 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1014 "top-right corner wall", "bottom-left corner wall",
1015 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1016 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1017 "secret door", "secret corridor", "pool", "moat", "water",
1018 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1019 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1020 "drawbridge down", "air", "cloud",
1021 /* not a real terrain type, but used for undiggable stone
1022 by wiz_map_levltyp() */
1023 "unreachable/undiggable",
1024 /* padding in case the number of entries above is odd */
1028 /* explanation of base-36 output from wiz_map_levltyp() */
1029 STATIC_OVL void
1030 wiz_levltyp_legend(VOID_ARGS)
1032 winid win;
1033 int i, j, last, c;
1034 const char *dsc, *fmt;
1035 char buf[BUFSZ];
1037 win = create_nhwindow(NHW_TEXT);
1038 putstr(win, 0, "#terrain encodings:");
1039 putstr(win, 0, "");
1040 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1041 *buf = '\0';
1042 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1043 and right hand column holds [N/2],[N/2+1],...,[N-1];
1044 N ('last') will always be even, and may or may not include
1045 the empty string entry to pad out the final pair, depending
1046 upon how many other entries are present in levltyp[] */
1047 last = SIZE(levltyp) & ~1;
1048 for (i = 0; i < last / 2; ++i)
1049 for (j = i; j < last; j += last / 2) {
1050 dsc = levltyp[j];
1051 c = !*dsc ? ' '
1052 : !strncmp(dsc, "unreachable", 11) ? '*'
1053 /* same int-to-char conversion as wiz_map_levltyp() */
1054 : (j < 10) ? '0' + j
1055 : (j < 36) ? 'a' + j - 10
1056 : 'A' + j - 36;
1057 Sprintf(eos(buf), fmt, c, dsc);
1058 if (j > i) {
1059 putstr(win, 0, buf);
1060 *buf = '\0';
1063 display_nhwindow(win, TRUE);
1064 destroy_nhwindow(win);
1065 return;
1068 /* #wizsmell command - test usmellmon(). */
1069 STATIC_PTR int
1070 wiz_smell(VOID_ARGS)
1072 int ans = 0;
1073 int mndx; /* monster index */
1074 coord cc; /* screen pos of unknown glyph */
1075 int glyph; /* glyph at selected position */
1077 cc.x = u.ux;
1078 cc.y = u.uy;
1079 mndx = 0; /* gcc -Wall lint */
1080 if (!olfaction(youmonst.data)) {
1081 You("are incapable of detecting odors in your present form.");
1082 return 0;
1085 pline("You can move the cursor to a monster that you want to smell.");
1086 do {
1087 pline("Pick a monster to smell.");
1088 ans = getpos(&cc, TRUE, "a monster");
1089 if (ans < 0 || cc.x < 0) {
1090 return 0; /* done */
1092 /* Convert the glyph at the selected position to a mndxbol. */
1093 glyph = glyph_at(cc.x, cc.y);
1094 if (glyph_is_monster(glyph))
1095 mndx = glyph_to_mon(glyph);
1096 else
1097 mndx = 0;
1098 /* Is it a monster? */
1099 if (mndx) {
1100 if (!usmellmon(&mons[mndx]))
1101 pline("That monster seems to give off no smell.");
1102 } else
1103 pline("That is not a monster.");
1104 } while (TRUE);
1105 return 0;
1108 /* #wizinstrinsic command to set some intrinsics for testing */
1109 STATIC_PTR int
1110 wiz_intrinsic(VOID_ARGS)
1112 if (wizard) {
1113 winid win;
1114 anything any;
1115 int i, n, accelerator;
1116 menu_item *pick_list = (menu_item *) 0;
1118 static const char *const intrinsics[] = {
1119 "deafness",
1122 win = create_nhwindow(NHW_MENU);
1123 start_menu(win);
1124 accelerator = 0;
1126 for (i = 0; i < SIZE(intrinsics); ++i) {
1127 accelerator = intrinsics[i][0];
1128 any.a_int = i + 1;
1129 add_menu(win, NO_GLYPH, &any, accelerator, 0,
1130 ATR_NONE, intrinsics[i], FALSE);
1132 end_menu(win, "Which intrinsic?");
1133 n = select_menu(win, PICK_ONE, &pick_list);
1134 destroy_nhwindow(win);
1136 if (n >= 1) {
1137 i = pick_list[0].item.a_int-1;
1138 free((genericptr_t) pick_list);
1139 } else {
1140 return 0;
1143 if (!strcmp(intrinsics[i], "deafness")) {
1144 You("go deaf.");
1145 incr_itimeout(&HDeaf, 30);
1146 context.botl = TRUE;
1148 } else
1149 pline("Unavailable command '%s'.",
1150 visctrl((int) cmd_from_func(wiz_intrinsic)));
1151 return 0;
1154 /* #wizrumorcheck command - verify each rumor access */
1155 STATIC_PTR int
1156 wiz_rumor_check(VOID_ARGS)
1158 rumor_check();
1159 return 0;
1162 /* #terrain command -- show known map, inspired by crawl's '|' command */
1163 STATIC_PTR int
1164 doterrain(VOID_ARGS)
1166 winid men;
1167 menu_item *sel;
1168 anything any;
1169 int n;
1170 int which;
1173 * normal play: choose between known map without mons, obj, and traps
1174 * (to see underlying terrain only), or
1175 * known map without mons and objs (to see traps under mons and objs), or
1176 * known map without mons (to see objects under monsters);
1177 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1178 * wizard mode: normal and explore choices plus
1179 * a dump of the internal levl[][].typ codes w/ level flags, or
1180 * a legend for the levl[][].typ codes dump
1182 men = create_nhwindow(NHW_MENU);
1183 start_menu(men);
1184 any = zeroany;
1185 any.a_int = 1;
1186 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1187 "known map without monsters, objects, and traps",
1188 MENU_SELECTED);
1189 any.a_int = 2;
1190 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1191 "known map without monsters and objects",
1192 MENU_UNSELECTED);
1193 any.a_int = 3;
1194 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1195 "known map without monsters",
1196 MENU_UNSELECTED);
1197 if (discover || wizard) {
1198 any.a_int = 4;
1199 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1200 "full map without monsters, objects, and traps",
1201 MENU_UNSELECTED);
1202 if (wizard) {
1203 any.a_int = 5;
1204 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1205 "internal levl[][].typ codes in base-36",
1206 MENU_UNSELECTED);
1207 any.a_int = 6;
1208 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1209 "legend of base-36 levl[][].typ codes",
1210 MENU_UNSELECTED);
1213 end_menu(men, "View which?");
1215 n = select_menu(men, PICK_ONE, &sel);
1216 destroy_nhwindow(men);
1218 * n < 0: player used ESC to cancel;
1219 * n == 0: preselected entry was explicitly chosen and got toggled off;
1220 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1221 * n == 2: another entry was explicitly chosen, so skip preselected one.
1223 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1224 if (n > 1 && which == 1)
1225 which = sel[1].item.a_int;
1226 if (n > 0)
1227 free((genericptr_t) sel);
1229 switch (which) {
1230 case 1: /* known map */
1231 reveal_terrain(0, TER_MAP);
1232 break;
1233 case 2: /* known map with known traps */
1234 reveal_terrain(0, TER_MAP | TER_TRP);
1235 break;
1236 case 3: /* known map with known traps and objects */
1237 reveal_terrain(0, TER_MAP | TER_TRP | TER_OBJ);
1238 break;
1239 case 4: /* full map */
1240 reveal_terrain(1, TER_MAP);
1241 break;
1242 case 5: /* map internals */
1243 wiz_map_levltyp();
1244 break;
1245 case 6: /* internal details */
1246 wiz_levltyp_legend();
1247 break;
1248 default:
1249 break;
1251 return 0; /* no time elapses */
1254 /* -enlightenment and conduct- */
1255 static winid en_win = WIN_ERR;
1256 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1257 have[] = "have ", had[] = "had ", can[] = "can ",
1258 could[] = "could ";
1259 static const char have_been[] = "have been ", have_never[] = "have never ",
1260 never[] = "never ";
1262 #define enl_msg(prefix, present, past, suffix, ps) \
1263 enlght_line(prefix, final ? past : present, suffix, ps)
1264 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1265 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1266 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1267 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1268 #define you_have_never(badthing) \
1269 enl_msg(You_, have_never, never, badthing, "")
1270 #define you_have_X(something) \
1271 enl_msg(You_, have, (const char *) "", something, "")
1273 static void
1274 enlght_line(start, middle, end, ps)
1275 const char *start, *middle, *end, *ps;
1277 char buf[BUFSZ];
1279 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1280 putstr(en_win, 0, buf);
1283 /* format increased chance to hit or damage or defense (Protection) */
1284 static char *
1285 enlght_combatinc(inctyp, incamt, final, outbuf)
1286 const char *inctyp;
1287 int incamt, final;
1288 char *outbuf;
1290 const char *modif, *bonus;
1291 boolean invrt;
1292 int absamt;
1294 absamt = abs(incamt);
1295 /* Protection amount is typically larger than damage or to-hit;
1296 reduce magnitude by a third in order to stretch modifier ranges
1297 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1298 if (!strcmp(inctyp, "defense"))
1299 absamt = (absamt * 2) / 3;
1301 if (absamt <= 3)
1302 modif = "small";
1303 else if (absamt <= 6)
1304 modif = "moderate";
1305 else if (absamt <= 12)
1306 modif = "large";
1307 else
1308 modif = "huge";
1310 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1311 bonus = (incamt >= 0) ? "bonus" : "penalty";
1312 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1313 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1315 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1316 invrt ? bonus : inctyp);
1317 if (final || wizard)
1318 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1320 return outbuf;
1323 /* report half physical or half spell damage */
1324 STATIC_OVL void
1325 enlght_halfdmg(category, final)
1326 int category;
1327 int final;
1329 const char *category_name;
1330 char buf[BUFSZ];
1332 switch (category) {
1333 case HALF_PHDAM:
1334 category_name = "physical";
1335 break;
1336 case HALF_SPDAM:
1337 category_name = "spell";
1338 break;
1339 default:
1340 category_name = "unknown";
1341 break;
1343 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1344 category_name);
1345 enl_msg(You_, "take", "took", buf, from_what(category));
1348 /* is hero actively using water walking capability on water (or lava)? */
1349 STATIC_OVL boolean
1350 walking_on_water()
1352 if (u.uinwater || Levitation || Flying)
1353 return FALSE;
1354 return (boolean) (Wwalking
1355 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1358 /* check whether hero is wearing something that player definitely knows
1359 confers the target property; item must have been seen and its type
1360 discovered but it doesn't necessarily have to be fully identified */
1361 STATIC_OVL boolean
1362 cause_known(propindx)
1363 int propindx; /* index of a property which can be conveyed by worn item */
1365 register struct obj *o;
1366 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1368 /* simpler than from_what()/what_gives(); we don't attempt to
1369 handle artifacts and we deliberately ignore wielded items */
1370 for (o = invent; o; o = o->nobj) {
1371 if (!(o->owornmask & mask))
1372 continue;
1373 if ((int) objects[o->otyp].oc_oprop == propindx
1374 && objects[o->otyp].oc_name_known && o->dknown)
1375 return TRUE;
1377 return FALSE;
1380 /* format a characteristic value, accommodating Strength's strangeness */
1381 STATIC_OVL char *
1382 attrval(attrindx, attrvalue, resultbuf)
1383 int attrindx, attrvalue;
1384 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1386 if (attrindx != A_STR || attrvalue <= 18)
1387 Sprintf(resultbuf, "%d", attrvalue);
1388 else if (attrvalue > STR18(100)) /* 19 to 25 */
1389 Sprintf(resultbuf, "%d", attrvalue - 100);
1390 else /* simplify "18/ **" to be "18/100" */
1391 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1392 return resultbuf;
1395 void
1396 enlightenment(mode, final)
1397 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1398 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1400 char buf[BUFSZ], tmpbuf[BUFSZ];
1402 Strcpy(tmpbuf, plname);
1403 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1404 /* as in background_enlightenment, when poly'd we need to use the saved
1405 gender in u.mfemale rather than the current you-as-monster gender */
1406 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1407 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1408 ? urole.name.f
1409 : urole.name.m);
1411 en_win = create_nhwindow(NHW_MENU);
1412 /* title */
1413 putstr(en_win, 0, buf); /* "Conan the Archeologist's attributes:" */
1414 /* background and characteristics; ^X or end-of-game disclosure */
1415 if (mode & BASICENLIGHTENMENT) {
1416 /* role, race, alignment, deities */
1417 background_enlightenment(mode, final);
1418 /* strength, dexterity, &c */
1419 characteristics_enlightenment(mode, final);
1421 /* expanded status line information, including things which aren't
1422 included there due to space considerations--such as obvious
1423 alternative movement indicators (riding, levitation, &c), and
1424 various troubles (turning to stone, trapped, confusion, &c);
1425 shown for both basic and magic enlightenment */
1426 status_enlightenment(mode, final);
1427 /* remaining attributes; shown for potion,&c or wizard mode and
1428 explore mode ^X or end of game disclosure */
1429 if (mode & MAGICENLIGHTENMENT) {
1430 /* intrinsics and other traditional enlightenment feedback */
1431 attributes_enlightenment(mode, final);
1433 display_nhwindow(en_win, TRUE);
1434 destroy_nhwindow(en_win);
1435 en_win = WIN_ERR;
1438 /*ARGSUSED*/
1439 /* display role, race, alignment and such to en_win */
1440 STATIC_OVL void
1441 background_enlightenment(unused_mode, final)
1442 int unused_mode UNUSED;
1443 int final;
1445 const char *role_titl, *rank_titl;
1446 int innategend, difgend, difalgn;
1447 char buf[BUFSZ], tmpbuf[BUFSZ];
1449 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1450 to access hero's saved gender-as-human/elf/&c rather than current one */
1451 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1452 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1453 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1455 putstr(en_win, 0, ""); /* separator after title */
1456 putstr(en_win, 0, "Background:");
1458 /* if polymorphed, report current shape before underlying role;
1459 will be repeated as first status: "you are transformed" and also
1460 among various attributes: "you are in beast form" (after being
1461 told about lycanthropy) or "you are polymorphed into <a foo>"
1462 (with countdown timer appended for wizard mode); we really want
1463 the player to know he's not a samurai at the moment... */
1464 if (Upolyd) {
1465 struct permonst *uasmon = youmonst.data;
1467 tmpbuf[0] = '\0';
1468 /* here we always use current gender, not saved role gender */
1469 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1470 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1471 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1472 uasmon->mname);
1473 you_are(buf, "");
1476 /* report role; omit gender if it's redundant (eg, "female priestess") */
1477 tmpbuf[0] = '\0';
1478 if (!urole.name.f
1479 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1480 || innategend != flags.initgend))
1481 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1482 buf[0] = '\0';
1483 if (Upolyd)
1484 Strcpy(buf, "actually "); /* "You are actually a ..." */
1485 if (!strcmpi(rank_titl, role_titl)) {
1486 /* omit role when rank title matches it */
1487 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1488 tmpbuf, urace.noun);
1489 } else {
1490 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1491 tmpbuf, urace.adj, role_titl);
1493 you_are(buf, "");
1495 /* report alignment (bypass you_are() in order to omit ending period);
1496 adverb is used to distinguish between temporary change (helm of opp.
1497 alignment), permanent change (one-time conversion), and original */
1498 Sprintf(buf, " %s%s%s, %son a mission for %s",
1499 You_, !final ? are : were,
1500 align_str(u.ualign.type),
1501 /* helm of opposite alignment (might hide conversion) */
1502 (u.ualign.type != u.ualignbase[A_CURRENT])
1503 /* what's the past tense of "currently"? if we used "formerly"
1504 it would sound like a reference to the original alignment */
1505 ? (!final ? "currently " : "temporarily ")
1506 /* permanent conversion */
1507 : (u.ualign.type != u.ualignbase[A_ORIGINAL])
1508 /* and what's the past tense of "now"? certainly not "then"
1509 in a context like this...; "belatedly" == weren't that
1510 way sooner (in other words, didn't start that way) */
1511 ? (!final ? "now " : "belatedly ")
1512 /* atheist (ignored in very early game) */
1513 : (!u.uconduct.gnostic && moves > 1000L)
1514 ? "nominally "
1515 /* lastly, normal case */
1516 : "",
1517 u_gname());
1518 putstr(en_win, 0, buf);
1519 /* show the rest of this game's pantheon (finishes previous sentence)
1520 [appending "also Moloch" at the end would allow for straightforward
1521 trailing "and" on all three aligned entries but looks too verbose] */
1522 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1523 if (u.ualign.type != A_LAWFUL)
1524 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1525 align_str(A_LAWFUL));
1526 if (u.ualign.type != A_NEUTRAL)
1527 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1528 align_str(A_NEUTRAL),
1529 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1530 if (u.ualign.type != A_CHAOTIC)
1531 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1532 align_str(A_CHAOTIC));
1533 Strcat(buf, "."); /* terminate sentence */
1534 putstr(en_win, 0, buf);
1536 /* show original alignment,gender,race,role if any have been changed;
1537 giving separate message for temporary alignment change bypasses need
1538 for tricky phrasing otherwise necessitated by possibility of having
1539 helm of opposite alignment mask a permanent alignment conversion */
1540 difgend = (innategend != flags.initgend);
1541 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1542 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1543 ? 2 : 0));
1544 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1545 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1546 you_are(buf, "");
1547 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1549 if (difgend || difalgn) { /* sex change or perm align change or both */
1550 Sprintf(buf, " You started out %s%s%s.",
1551 difgend ? genders[flags.initgend].adj : "",
1552 (difgend && difalgn) ? " and " : "",
1553 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1554 putstr(en_win, 0, buf);
1558 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1559 STATIC_OVL void
1560 characteristics_enlightenment(mode, final)
1561 int mode;
1562 int final;
1564 putstr(en_win, 0, ""); /* separator after background */
1565 putstr(en_win, 0,
1566 final ? "Final Characteristics:" : "Current Characteristics:");
1568 /* bottom line order */
1569 one_characteristic(mode, final, A_STR); /* strength */
1570 one_characteristic(mode, final, A_DEX); /* dexterity */
1571 one_characteristic(mode, final, A_CON); /* constitution */
1572 one_characteristic(mode, final, A_INT); /* intelligence */
1573 one_characteristic(mode, final, A_WIS); /* wisdom */
1574 one_characteristic(mode, final, A_CHA); /* charisma */
1577 /* display one attribute value for characteristics_enlightenment() */
1578 STATIC_OVL void
1579 one_characteristic(mode, final, attrindx)
1580 int mode, final, attrindx;
1582 boolean hide_innate_value = FALSE, interesting_alimit;
1583 int acurrent, abase, apeak, alimit;
1584 const char *attrname, *paren_pfx;
1585 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
1587 /* being polymorphed or wearing certain cursed items prevents
1588 hero from reliably tracking changes to characteristics so
1589 we don't show base & peak values then; when the items aren't
1590 cursed, hero could take them off to check underlying values
1591 and we show those in such case so that player doesn't need
1592 to actually resort to doing that */
1593 if (Upolyd) {
1594 hide_innate_value = TRUE;
1595 } else if (Fixed_abil) {
1596 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
1597 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
1598 hide_innate_value = TRUE;
1600 switch (attrindx) {
1601 case A_STR:
1602 attrname = "strength";
1603 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
1604 hide_innate_value = TRUE;
1605 break;
1606 case A_DEX:
1607 attrname = "dexterity";
1608 break;
1609 case A_CON:
1610 attrname = "constitution";
1611 break;
1612 case A_INT:
1613 attrname = "intelligence";
1614 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1615 hide_innate_value = TRUE;
1616 break;
1617 case A_WIS:
1618 attrname = "wisdom";
1619 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1620 hide_innate_value = TRUE;
1621 break;
1622 case A_CHA:
1623 attrname = "charisma";
1624 break;
1625 default:
1626 return; /* impossible */
1628 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1629 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
1630 hide_innate_value = FALSE;
1632 acurrent = ACURR(attrindx);
1633 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
1634 Sprintf(subjbuf, "Your %s ", attrname);
1636 if (!hide_innate_value) {
1637 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1638 (a magic bonus or penalty is in effect) or abase doesn't match
1639 amax (some points have been lost to poison or exercise abuse
1640 and are restorable) or attrmax is different from normal human
1641 (while game is in progress; trying to reduce dependency on
1642 spoilers to keep track of such stuff) or attrmax was different
1643 from abase (at end of game; this attribute wasn't maxed out) */
1644 abase = ABASE(attrindx);
1645 apeak = AMAX(attrindx);
1646 alimit = ATTRMAX(attrindx);
1647 /* criterium for whether the limit is interesting varies */
1648 interesting_alimit =
1649 final ? TRUE /* was originally `(abase != alimit)' */
1650 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
1651 paren_pfx = final ? " (" : " (current; ";
1652 if (acurrent != abase) {
1653 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
1654 attrval(attrindx, abase, valstring));
1655 paren_pfx = ", ";
1657 if (abase != apeak) {
1658 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
1659 attrval(attrindx, apeak, valstring));
1660 paren_pfx = ", ";
1662 if (interesting_alimit) {
1663 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
1664 /* more verbose if exceeding 'limit' due to magic bonus */
1665 (acurrent > alimit) ? "innate " : "",
1666 attrval(attrindx, alimit, valstring));
1667 /* paren_pfx = ", "; */
1669 if (acurrent != abase || abase != apeak || interesting_alimit)
1670 Strcat(valubuf, ")");
1672 enl_msg(subjbuf, "is ", "was ", valubuf, "");
1675 /* status: selected obvious capabilities, assorted troubles */
1676 STATIC_OVL void
1677 status_enlightenment(mode, final)
1678 int mode;
1679 int final;
1681 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
1682 int cap;
1683 char buf[BUFSZ], youtoo[BUFSZ];
1684 boolean Riding = (u.usteed
1685 /* if hero dies while dismounting, u.usteed will still
1686 be set; we want to ignore steed in that situation */
1687 && !(final == ENL_GAMEOVERDEAD
1688 && !strcmp(killer.name, "riding accident")));
1689 const char *steedname = (!Riding ? (char *) 0
1690 : x_monnam(u.usteed,
1691 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
1692 (char *) 0,
1693 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
1694 FALSE));
1697 * Status (many are abbreviated on bottom line; others are or
1698 * should be discernible to the hero hence to the player)
1700 putstr(en_win, 0, ""); /* separator after title or characteristics */
1701 putstr(en_win, 0, final ? "Final Status:" : "Current Status:");
1703 Strcpy(youtoo, You_);
1704 /* not a traditional status but inherently obvious to player; more
1705 detail given below (attributes section) for magic enlightenment */
1706 if (Upolyd)
1707 you_are("transformed", "");
1708 /* not a trouble, but we want to display riding status before maybe
1709 reporting steed as trapped or hero stuck to cursed saddle */
1710 if (Riding) {
1711 Sprintf(buf, "riding %s", steedname);
1712 you_are(buf, "");
1713 Sprintf(eos(youtoo), "and %s ", steedname);
1715 /* other movement situations that hero should always know */
1716 if (Levitation) {
1717 if (Lev_at_will && magic)
1718 you_are("levitating, at will", "");
1719 else
1720 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
1721 } else if (Flying) { /* can only fly when not levitating */
1722 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
1724 if (Underwater) {
1725 you_are("underwater", "");
1726 } else if (u.uinwater) {
1727 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
1728 } else if (walking_on_water()) {
1729 /* show active Wwalking here, potential Wwalking elsewhere */
1730 Sprintf(buf, "walking on %s",
1731 is_pool(u.ux, u.uy) ? "water"
1732 : is_lava(u.ux, u.uy) ? "lava"
1733 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
1734 you_are(buf, from_what(WWALKING));
1736 if (Upolyd && (u.uundetected || youmonst.m_ap_type != M_AP_NOTHING))
1737 youhiding(TRUE, final);
1739 /* internal troubles, mostly in the order that prayer ranks them */
1740 if (Stoned)
1741 you_are("turning to stone", "");
1742 if (Slimed)
1743 you_are("turning into slime", "");
1744 if (Strangled) {
1745 if (u.uburied) {
1746 you_are("buried", "");
1747 } else {
1748 Strcpy(buf, "being strangled");
1749 if (wizard)
1750 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
1751 you_are(buf, from_what(STRANGLED));
1754 if (Sick) {
1755 /* prayer lumps these together; botl puts Ill before FoodPois */
1756 if (u.usick_type & SICK_NONVOMITABLE)
1757 you_are("terminally sick from illness", "");
1758 if (u.usick_type & SICK_VOMITABLE)
1759 you_are("terminally sick from food poisoning", "");
1761 if (Vomiting)
1762 you_are("nauseated", "");
1763 if (Stunned)
1764 you_are("stunned", "");
1765 if (Confusion)
1766 you_are("confused", "");
1767 if (Hallucination)
1768 you_are("hallucinating", "");
1769 if (Blind) {
1770 /* from_what() (currently wizard-mode only) checks !haseyes()
1771 before u.uroleplay.blind, so we should too */
1772 Sprintf(buf, "%s blind",
1773 !haseyes(youmonst.data) ? "innately"
1774 : u.uroleplay.blind ? "permanently"
1775 /* better phrasing desperately wanted... */
1776 : Blindfolded_only ? "deliberately"
1777 : "temporarily");
1778 if (wizard && (Blinded & TIMEOUT) != 0L
1779 && !u.uroleplay.blind && haseyes(youmonst.data))
1780 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
1781 /* !haseyes: avoid "you are innately blind innately" */
1782 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
1784 if (Deaf)
1785 you_are("deaf", from_what(DEAF));
1787 /* external troubles, more or less */
1788 if (Punished) {
1789 if (uball) {
1790 Sprintf(buf, "chained to %s", ansimpleoname(uball));
1791 } else {
1792 impossible("Punished without uball?");
1793 Strcpy(buf, "punished");
1795 you_are(buf, "");
1797 if (u.utrap) {
1798 char predicament[BUFSZ];
1799 struct trap *t;
1800 boolean anchored = (u.utraptype == TT_BURIEDBALL);
1802 if (anchored) {
1803 Strcpy(predicament, "tethered to something buried");
1804 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
1805 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
1806 } else {
1807 Strcpy(predicament, "trapped");
1808 if ((t = t_at(u.ux, u.uy)) != 0)
1809 Sprintf(eos(predicament), " in %s",
1810 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
1812 if (u.usteed) { /* not `Riding' here */
1813 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
1814 *buf = highc(*buf);
1815 enl_msg(buf, (anchored ? "are " : "is "),
1816 (anchored ? "were " : "was "), predicament, "");
1817 } else
1818 you_are(predicament, "");
1819 } /* (u.utrap) */
1820 if (u.uswallow) {
1821 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
1822 if (wizard)
1823 Sprintf(eos(buf), " (%u)", u.uswldtim);
1824 you_are(buf, "");
1825 } else if (u.ustuck) {
1826 Sprintf(buf, "%s %s",
1827 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
1828 a_monnam(u.ustuck));
1829 you_are(buf, "");
1831 if (Riding) {
1832 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
1834 if (saddle && saddle->cursed) {
1835 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
1836 simpleonames(saddle));
1837 you_are(buf, "");
1840 if (Wounded_legs) {
1841 /* when mounted, Wounded_legs applies to steed rather than to
1842 hero; we only report steed's wounded legs in wizard mode */
1843 if (u.usteed) { /* not `Riding' here */
1844 if (wizard && steedname) {
1845 Strcpy(buf, steedname);
1846 *buf = highc(*buf);
1847 enl_msg(buf, " has", " had", " wounded legs", "");
1849 } else {
1850 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
1851 you_have(buf, "");
1854 if (Glib) {
1855 Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
1856 you_have(buf, "");
1858 if (Fumbling) {
1859 if (magic || cause_known(FUMBLING))
1860 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
1862 if (Sleepy) {
1863 if (magic || cause_known(SLEEPY)) {
1864 Strcpy(buf, from_what(SLEEPY));
1865 if (wizard)
1866 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
1867 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
1870 /* hunger/nutrition */
1871 if (Hunger) {
1872 if (magic || cause_known(HUNGER))
1873 enl_msg(You_, "hunger", "hungered", " rapidly",
1874 from_what(HUNGER));
1876 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
1877 mungspaces(buf); /* strip trailing spaces */
1878 if (*buf) {
1879 *buf = lowc(*buf); /* override capitalization */
1880 if (!strcmp(buf, "weak"))
1881 Strcat(buf, " from severe hunger");
1882 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
1883 Strcat(buf, " due to starvation");
1884 you_are(buf, "");
1886 /* encumbrance */
1887 if ((cap = near_capacity()) > UNENCUMBERED) {
1888 const char *adj = "?_?"; /* (should always get overridden) */
1890 Strcpy(buf, enc_stat[cap]);
1891 *buf = lowc(*buf);
1892 switch (cap) {
1893 case SLT_ENCUMBER:
1894 adj = "slightly";
1895 break; /* burdened */
1896 case MOD_ENCUMBER:
1897 adj = "moderately";
1898 break; /* stressed */
1899 case HVY_ENCUMBER:
1900 adj = "very";
1901 break; /* strained */
1902 case EXT_ENCUMBER:
1903 adj = "extremely";
1904 break; /* overtaxed */
1905 case OVERLOADED:
1906 adj = "not possible";
1907 break;
1909 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
1910 (cap < OVERLOADED) ? " slowed" : "");
1911 you_are(buf, "");
1912 } else {
1913 /* last resort entry, guarantees Status section is non-empty
1914 (no longer needed for that purpose since weapon status added;
1915 still useful though) */
1916 you_are("unencumbered", "");
1918 /* report being weaponless; distinguish whether gloves are worn */
1919 if (!uwep) {
1920 you_are(uarmg ? "empty handed" /* gloves imply hands */
1921 : humanoid(youmonst.data)
1922 /* hands but no weapon and no gloves */
1923 ? "bare handed"
1924 /* alternate phrasing for paws or lack of hands */
1925 : "not wielding anything",
1926 "");
1927 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1928 } else if (u.twoweap) {
1929 you_are("wielding two weapons at once", "");
1930 /* report most weapons by their skill class (so a katana will be
1931 described as a long sword, for instance; mattock and hook are
1932 exceptions), or wielded non-weapon item by its object class */
1933 } else {
1934 const char *what = weapon_descr(uwep);
1936 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
1937 || !strcmpi(what, "venom"))
1938 Sprintf(buf, "wielding some %s", what);
1939 else
1940 Sprintf(buf, "wielding %s",
1941 (uwep->quan == 1L) ? an(what) : makeplural(what));
1942 you_are(buf, "");
1944 /* report 'nudity' */
1945 if (!uarm && !uarmu && !uarmc && !uarmg && !uarmf && !uarmh) {
1946 if (u.uroleplay.nudist)
1947 enl_msg(You_, "do", "did", " not wear any armor", "");
1948 else
1949 you_are("not wearing any armor", "");
1953 /* attributes: intrinsics and the like, other non-obvious capabilities */
1954 void
1955 attributes_enlightenment(unused_mode, final)
1956 int unused_mode UNUSED;
1957 int final;
1959 static NEARDATA const char if_surroundings_permitted[] =
1960 " if surroundings permitted";
1961 int ltmp, armpro;
1962 char buf[BUFSZ];
1965 * Attributes
1967 putstr(en_win, 0, "");
1968 putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
1970 if (u.uevent.uhand_of_elbereth) {
1971 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
1972 "the Envoy of Balance",
1973 "the Glory of Arioch" };
1974 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
1977 Sprintf(buf, "%s", piousness(TRUE, "aligned"));
1978 if (u.ualign.record >= 0)
1979 you_are(buf, "");
1980 else
1981 you_have(buf, "");
1983 if (wizard) {
1984 Sprintf(buf, " %d", u.ualign.record);
1985 enl_msg("Your alignment ", "is", "was", buf, "");
1988 /*** Resistances to troubles ***/
1989 if (Invulnerable)
1990 you_are("invulnerable", from_what(INVULNERABLE));
1991 if (Antimagic)
1992 you_are("magic-protected", from_what(ANTIMAGIC));
1993 if (Fire_resistance)
1994 you_are("fire resistant", from_what(FIRE_RES));
1995 if (Cold_resistance)
1996 you_are("cold resistant", from_what(COLD_RES));
1997 if (Sleep_resistance)
1998 you_are("sleep resistant", from_what(SLEEP_RES));
1999 if (Disint_resistance)
2000 you_are("disintegration-resistant", from_what(DISINT_RES));
2001 if (Shock_resistance)
2002 you_are("shock resistant", from_what(SHOCK_RES));
2003 if (Poison_resistance)
2004 you_are("poison resistant", from_what(POISON_RES));
2005 if (Acid_resistance)
2006 you_are("acid resistant", from_what(ACID_RES));
2007 if (Drain_resistance)
2008 you_are("level-drain resistant", from_what(DRAIN_RES));
2009 if (Sick_resistance)
2010 you_are("immune to sickness", from_what(SICK_RES));
2011 if (Stone_resistance)
2012 you_are("petrification resistant", from_what(STONE_RES));
2013 if (Halluc_resistance)
2014 enl_msg(You_, "resist", "resisted", " hallucinations",
2015 from_what(HALLUC_RES));
2016 if (u.uedibility)
2017 you_can("recognize detrimental food", "");
2019 /*** Vision and senses ***/
2020 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2021 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2022 if (See_invisible) {
2023 if (!Blind)
2024 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2025 else
2026 enl_msg(You_, "will see", "would have seen",
2027 " invisible when not blind", from_what(SEE_INVIS));
2029 if (Blind_telepat)
2030 you_are("telepathic", from_what(TELEPAT));
2031 if (Warning)
2032 you_are("warned", from_what(WARNING));
2033 if (Warn_of_mon && context.warntype.obj) {
2034 Sprintf(buf, "aware of the presence of %s",
2035 (context.warntype.obj & M2_ORC) ? "orcs"
2036 : (context.warntype.obj & M2_ELF) ? "elves"
2037 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2038 you_are(buf, from_what(WARN_OF_MON));
2040 if (Warn_of_mon && context.warntype.polyd) {
2041 Sprintf(buf, "aware of the presence of %s",
2042 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2043 == (M2_HUMAN | M2_ELF))
2044 ? "humans and elves"
2045 : (context.warntype.polyd & M2_HUMAN)
2046 ? "humans"
2047 : (context.warntype.polyd & M2_ELF)
2048 ? "elves"
2049 : (context.warntype.polyd & M2_ORC)
2050 ? "orcs"
2051 : (context.warntype.polyd & M2_DEMON)
2052 ? "demons"
2053 : "certain monsters");
2054 you_are(buf, "");
2056 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2057 Sprintf(buf, "aware of the presence of %s",
2058 makeplural(mons[context.warntype.speciesidx].mname));
2059 you_are(buf, from_what(WARN_OF_MON));
2061 if (Undead_warning)
2062 you_are("warned of undead", from_what(WARN_UNDEAD));
2063 if (Searching)
2064 you_have("automatic searching", from_what(SEARCHING));
2065 if (Clairvoyant)
2066 you_are("clairvoyant", from_what(CLAIRVOYANT));
2067 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2068 Strcpy(buf, from_what(-CLAIRVOYANT));
2069 if (!strncmp(buf, " because of ", 12))
2070 /* overwrite substring; strncpy doesn't add terminator */
2071 (void) strncpy(buf, " if not for ", 12);
2072 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2074 if (Infravision)
2075 you_have("infravision", from_what(INFRAVISION));
2076 if (Detect_monsters)
2077 you_are("sensing the presence of monsters", "");
2078 if (u.umconf)
2079 you_are("going to confuse monsters", "");
2081 /*** Appearance and behavior ***/
2082 if (Adornment) {
2083 int adorn = 0;
2085 if (uleft && uleft->otyp == RIN_ADORNMENT)
2086 adorn += uleft->spe;
2087 if (uright && uright->otyp == RIN_ADORNMENT)
2088 adorn += uright->spe;
2089 /* the sum might be 0 (+0 ring or two which negate each other);
2090 that yields "you are charismatic" (which isn't pointless
2091 because it potentially impacts seduction attacks) */
2092 Sprintf(buf, "%scharismatic",
2093 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2094 you_are(buf, from_what(ADORNED));
2096 if (Invisible)
2097 you_are("invisible", from_what(INVIS));
2098 else if (Invis)
2099 you_are("invisible to others", from_what(INVIS));
2100 /* ordinarily "visible" is redundant; this is a special case for
2101 the situation when invisibility would be an expected attribute */
2102 else if ((HInvis || EInvis) && BInvis)
2103 you_are("visible", from_what(-INVIS));
2104 if (Displaced)
2105 you_are("displaced", from_what(DISPLACED));
2106 if (Stealth)
2107 you_are("stealthy", from_what(STEALTH));
2108 if (Aggravate_monster)
2109 enl_msg("You aggravate", "", "d", " monsters",
2110 from_what(AGGRAVATE_MONSTER));
2111 if (Conflict)
2112 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2114 /*** Transportation ***/
2115 if (Jumping)
2116 you_can("jump", from_what(JUMPING));
2117 if (Teleportation)
2118 you_can("teleport", from_what(TELEPORT));
2119 if (Teleport_control)
2120 you_have("teleport control", from_what(TELEPORT_CONTROL));
2121 /* actively levitating handled earlier as a status condition */
2122 if (BLevitation) { /* levitation is blocked */
2123 long save_BLev = BLevitation;
2125 BLevitation = 0L;
2126 if (Levitation)
2127 enl_msg(You_, "would levitate", "would have levitated",
2128 if_surroundings_permitted, "");
2129 BLevitation = save_BLev;
2131 /* actively flying handled earlier as a status condition */
2132 if (BFlying) { /* flight is blocked */
2133 long save_BFly = BFlying;
2135 BFlying = 0L;
2136 if (Flying)
2137 enl_msg(You_, "would fly", "would have flown",
2138 Levitation
2139 ? "if you weren't levitating"
2140 : (save_BFly == FROMOUTSIDE)
2141 ? if_surroundings_permitted
2142 /* both surroundings and [latent] levitation */
2143 : " if circumstances permitted",
2144 "");
2145 BFlying = save_BFly;
2147 /* actively walking on water handled earlier as a status condition */
2148 if (Wwalking && !walking_on_water())
2149 you_can("walk on water", from_what(WWALKING));
2150 /* actively swimming (in water but not under it) handled earlier */
2151 if (Swimming && (Underwater || !u.uinwater))
2152 you_can("swim", from_what(SWIMMING));
2153 if (Breathless)
2154 you_can("survive without air", from_what(MAGICAL_BREATHING));
2155 else if (Amphibious)
2156 you_can("breathe water", from_what(MAGICAL_BREATHING));
2157 if (Passes_walls)
2158 you_can("walk through walls", from_what(PASSES_WALLS));
2160 /*** Physical attributes ***/
2161 if (Regeneration)
2162 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2163 if (Slow_digestion)
2164 you_have("slower digestion", from_what(SLOW_DIGESTION));
2165 if (u.uhitinc)
2166 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2167 if (u.udaminc)
2168 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2169 if (u.uspellprot || Protection) {
2170 int prot = 0;
2172 if (uleft && uleft->otyp == RIN_PROTECTION)
2173 prot += uleft->spe;
2174 if (uright && uright->otyp == RIN_PROTECTION)
2175 prot += uright->spe;
2176 if (HProtection & INTRINSIC)
2177 prot += u.ublessed;
2178 prot += u.uspellprot;
2179 if (prot)
2180 you_have(enlght_combatinc("defense", prot, final, buf), "");
2182 if ((armpro = magic_negation(&youmonst)) > 0) {
2183 /* magic cancellation factor, conferred by worn armor */
2184 static const char *const mc_types[] = {
2185 "" /*ordinary*/, "warded", "guarded", "protected",
2187 /* sanity check */
2188 if (armpro >= SIZE(mc_types))
2189 armpro = SIZE(mc_types) - 1;
2190 you_are(mc_types[armpro], "");
2192 if (Half_physical_damage)
2193 enlght_halfdmg(HALF_PHDAM, final);
2194 if (Half_spell_damage)
2195 enlght_halfdmg(HALF_SPDAM, final);
2196 /* polymorph and other shape change */
2197 if (Protection_from_shape_changers)
2198 you_are("protected from shape changers",
2199 from_what(PROT_FROM_SHAPE_CHANGERS));
2200 if (Unchanging) {
2201 const char *what = 0;
2203 if (!Upolyd) /* Upolyd handled below after current form */
2204 you_can("not change from your current form",
2205 from_what(UNCHANGING));
2206 /* blocked shape changes */
2207 if (Polymorph)
2208 what = !final ? "polymorph" : "have polymorphed";
2209 else if (u.ulycn >= LOW_PM)
2210 what = !final ? "change shape" : "have changed shape";
2211 if (what) {
2212 Sprintf(buf, "would %s periodically", what);
2213 /* omit from_what(UNCHANGING); too verbose */
2214 enl_msg(You_, buf, buf, " if not locked into your current form",
2215 "");
2217 } else if (Polymorph) {
2218 you_are("polymorphing periodically", from_what(POLYMORPH));
2220 if (Polymorph_control)
2221 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2222 if (Upolyd && u.umonnum != u.ulycn) {
2223 /* foreign shape (except were-form which is handled below) */
2224 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2225 if (wizard)
2226 Sprintf(eos(buf), " (%d)", u.mtimedone);
2227 you_are(buf, "");
2229 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2230 you_can("lay eggs", "");
2231 if (u.ulycn >= LOW_PM) {
2232 /* "you are a werecreature [in beast form]" */
2233 Strcpy(buf, an(mons[u.ulycn].mname));
2234 if (u.umonnum == u.ulycn) {
2235 Strcat(buf, " in beast form");
2236 if (wizard)
2237 Sprintf(eos(buf), " (%d)", u.mtimedone);
2239 you_are(buf, "");
2241 if (Unchanging && Upolyd) /* !Upolyd handled above */
2242 you_can("not change from your current form", from_what(UNCHANGING));
2243 if (Hate_silver)
2244 you_are("harmed by silver", "");
2245 /* movement and non-armor-based protection */
2246 if (Fast)
2247 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2248 if (Reflecting)
2249 you_have("reflection", from_what(REFLECTING));
2250 if (Free_action)
2251 you_have("free action", from_what(FREE_ACTION));
2252 if (Fixed_abil)
2253 you_have("fixed abilities", from_what(FIXED_ABIL));
2254 if (Lifesaved)
2255 enl_msg("Your life ", "will be", "would have been", " saved", "");
2257 /*** Miscellany ***/
2258 if (Luck) {
2259 ltmp = abs((int) Luck);
2260 Sprintf(buf, "%s%slucky",
2261 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2262 Luck < 0 ? "un" : "");
2263 if (wizard)
2264 Sprintf(eos(buf), " (%d)", Luck);
2265 you_are(buf, "");
2266 } else if (wizard)
2267 enl_msg("Your luck ", "is", "was", " zero", "");
2268 if (u.moreluck > 0)
2269 you_have("extra luck", "");
2270 else if (u.moreluck < 0)
2271 you_have("reduced luck", "");
2272 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2273 ltmp = stone_luck(0);
2274 if (ltmp <= 0)
2275 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2276 if (ltmp >= 0)
2277 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2280 if (u.ugangr) {
2281 Sprintf(buf, " %sangry with you",
2282 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2283 if (wizard)
2284 Sprintf(eos(buf), " (%d)", u.ugangr);
2285 enl_msg(u_gname(), " is", " was", buf, "");
2286 } else {
2288 * We need to suppress this when the game is over, because death
2289 * can change the value calculated by can_pray(), potentially
2290 * resulting in a false claim that you could have prayed safely.
2292 if (!final) {
2293 #if 0
2294 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2295 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2296 final ? "have " : "", final ? "ed" : "");
2297 #else
2298 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2299 #endif
2300 if (wizard)
2301 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2302 you_can(buf, "");
2306 #ifdef DEBUG
2307 /* named fruit debugging (doesn't really belong here...); to enable,
2308 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2309 if (wizard && explicitdebug("fruit")) {
2310 int fcount = 0;
2311 struct fruit *f;
2312 char buf2[BUFSZ];
2314 for (f = ffruit; f; f = f->nextf) {
2315 Sprintf(buf, "Fruit %d ", ++fcount);
2316 Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
2317 enl_msg(buf, "is ", "was ", buf2, "");
2319 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
2320 Sprintf(buf, "%d", flags.made_fruit);
2321 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
2323 #endif
2326 const char *p;
2328 buf[0] = '\0';
2329 if (final < 2) { /* still in progress, or quit/escaped/ascended */
2330 p = "survived after being killed ";
2331 switch (u.umortality) {
2332 case 0:
2333 p = !final ? (char *) 0 : "survived";
2334 break;
2335 case 1:
2336 Strcpy(buf, "once");
2337 break;
2338 case 2:
2339 Strcpy(buf, "twice");
2340 break;
2341 case 3:
2342 Strcpy(buf, "thrice");
2343 break;
2344 default:
2345 Sprintf(buf, "%d times", u.umortality);
2346 break;
2348 } else { /* game ended in character's death */
2349 p = "are dead";
2350 switch (u.umortality) {
2351 case 0:
2352 impossible("dead without dying?");
2353 case 1:
2354 break; /* just "are dead" */
2355 default:
2356 Sprintf(buf, " (%d%s time!)", u.umortality,
2357 ordin(u.umortality));
2358 break;
2361 if (p)
2362 enl_msg(You_, "have been killed ", p, buf, "");
2366 #if 0 /* no longer used */
2367 STATIC_DCL boolean NDECL(minimal_enlightenment);
2370 * Courtesy function for non-debug, non-explorer mode players
2371 * to help refresh them about who/what they are.
2372 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2374 STATIC_OVL boolean
2375 minimal_enlightenment()
2377 winid tmpwin;
2378 menu_item *selected;
2379 anything any;
2380 int genidx, n;
2381 char buf[BUFSZ], buf2[BUFSZ];
2382 static const char untabbed_fmtstr[] = "%-15s: %-12s";
2383 static const char untabbed_deity_fmtstr[] = "%-17s%s";
2384 static const char tabbed_fmtstr[] = "%s:\t%-12s";
2385 static const char tabbed_deity_fmtstr[] = "%s\t%s";
2386 static const char *fmtstr;
2387 static const char *deity_fmtstr;
2389 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
2390 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
2391 : untabbed_deity_fmtstr;
2392 any = zeroany;
2393 buf[0] = buf2[0] = '\0';
2394 tmpwin = create_nhwindow(NHW_MENU);
2395 start_menu(tmpwin);
2396 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2397 "Starting", FALSE);
2399 /* Starting name, race, role, gender */
2400 Sprintf(buf, fmtstr, "name", plname);
2401 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2402 Sprintf(buf, fmtstr, "race", urace.noun);
2403 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2404 Sprintf(buf, fmtstr, "role",
2405 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
2406 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2407 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
2408 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2410 /* Starting alignment */
2411 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
2412 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2414 /* Current name, race, role, gender */
2415 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2416 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2417 "Current", FALSE);
2418 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
2419 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2420 if (Upolyd) {
2421 Sprintf(buf, fmtstr, "role (base)",
2422 (u.mfemale && urole.name.f) ? urole.name.f
2423 : urole.name.m);
2424 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2425 } else {
2426 Sprintf(buf, fmtstr, "role",
2427 (flags.female && urole.name.f) ? urole.name.f
2428 : urole.name.m);
2429 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2431 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2432 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
2433 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
2434 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2435 if (Upolyd && (int) u.mfemale != genidx) {
2436 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
2437 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2440 /* Current alignment */
2441 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
2442 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2444 /* Deity list */
2445 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2446 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2447 "Deities", FALSE);
2448 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
2449 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2450 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
2451 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
2452 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
2453 Sprintf(buf, fmtstr, "Chaotic", buf2);
2454 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2456 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
2457 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2458 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
2459 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
2460 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
2461 Sprintf(buf, fmtstr, "Neutral", buf2);
2462 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2464 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
2465 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2466 && u.ualign.type == A_LAWFUL) ? " (s,c)"
2467 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
2468 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
2469 Sprintf(buf, fmtstr, "Lawful", buf2);
2470 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2472 end_menu(tmpwin, "Base Attributes");
2473 n = select_menu(tmpwin, PICK_NONE, &selected);
2474 destroy_nhwindow(tmpwin);
2475 return (boolean) (n != -1);
2477 #endif /*0*/
2479 /* ^X command */
2480 STATIC_PTR int
2481 doattributes(VOID_ARGS)
2483 int mode = BASICENLIGHTENMENT;
2485 /* show more--as if final disclosure--for wizard and explore modes */
2486 if (wizard || discover)
2487 mode |= MAGICENLIGHTENMENT;
2489 enlightenment(mode, ENL_GAMEINPROGRESS);
2490 return 0;
2493 void
2494 youhiding(via_enlghtmt, msgflag)
2495 boolean via_enlghtmt; /* englightment line vs topl message */
2496 int msgflag; /* for variant message phrasing */
2498 char *bp, buf[BUFSZ];
2500 Strcpy(buf, "hiding");
2501 if (youmonst.m_ap_type != M_AP_NOTHING) {
2502 /* mimic; hero is only able to mimic a strange object or gold
2503 or hallucinatory alternative to gold, so we skip the details
2504 for the hypothetical furniture and monster cases */
2505 bp = eos(strcpy(buf, "mimicking"));
2506 if (youmonst.m_ap_type == M_AP_OBJECT) {
2507 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
2508 } else if (youmonst.m_ap_type == M_AP_FURNITURE) {
2509 Strcpy(bp, " something");
2510 } else if (youmonst.m_ap_type == M_AP_MONSTER) {
2511 Strcpy(bp, " someone");
2512 } else {
2513 ; /* something unexpected; leave 'buf' as-is */
2515 } else if (u.uundetected) {
2516 bp = eos(buf); /* points past "hiding" */
2517 if (youmonst.data->mlet == S_EEL) {
2518 if (is_pool(u.ux, u.uy))
2519 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
2520 } else if (hides_under(youmonst.data)) {
2521 struct obj *o = level.objects[u.ux][u.uy];
2523 if (o)
2524 Sprintf(bp, " underneath %s", ansimpleoname(o));
2525 } else if (is_clinger(youmonst.data) || Flying) {
2526 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2527 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
2528 } else {
2529 /* on floor; is_hider() but otherwise not special: 'trapper' */
2530 if (u.utrap && u.utraptype == TT_PIT) {
2531 struct trap *t = t_at(u.ux, u.uy);
2533 Sprintf(bp, " in a %spit",
2534 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
2535 } else
2536 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
2538 } else {
2539 ; /* shouldn't happen; will result in generic "you are hiding" */
2542 if (via_enlghtmt) {
2543 int final = msgflag; /* 'final' is used by you_are() macro */
2545 you_are(buf, "");
2546 } else {
2547 /* for dohide(), when player uses '#monster' command */
2548 You("are %s %s.", msgflag ? "already" : "now", buf);
2552 /* KMH, #conduct
2553 * (shares enlightenment's tense handling)
2555 STATIC_PTR int
2556 doconduct(VOID_ARGS)
2558 show_conduct(0);
2559 return 0;
2562 void
2563 show_conduct(final)
2564 int final;
2566 char buf[BUFSZ];
2567 int ngenocided;
2569 /* Create the conduct window */
2570 en_win = create_nhwindow(NHW_MENU);
2571 putstr(en_win, 0, "Voluntary challenges:");
2573 if (u.uroleplay.blind)
2574 you_have_been("blind from birth");
2575 if (u.uroleplay.nudist)
2576 you_have_been("faithfully nudist");
2578 if (!u.uconduct.food)
2579 enl_msg(You_, "have gone", "went", " without food", "");
2580 /* But beverages are okay */
2581 else if (!u.uconduct.unvegan)
2582 you_have_X("followed a strict vegan diet");
2583 else if (!u.uconduct.unvegetarian)
2584 you_have_been("vegetarian");
2586 if (!u.uconduct.gnostic)
2587 you_have_been("an atheist");
2589 if (!u.uconduct.weaphit) {
2590 you_have_never("hit with a wielded weapon");
2591 } else if (wizard) {
2592 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
2593 plur(u.uconduct.weaphit));
2594 you_have_X(buf);
2596 if (!u.uconduct.killer)
2597 you_have_been("a pacifist");
2599 if (!u.uconduct.literate) {
2600 you_have_been("illiterate");
2601 } else if (wizard) {
2602 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
2603 plur(u.uconduct.literate));
2604 you_have_X(buf);
2607 ngenocided = num_genocides();
2608 if (ngenocided == 0) {
2609 you_have_never("genocided any monsters");
2610 } else {
2611 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
2612 plur(ngenocided), plur(ngenocided));
2613 you_have_X(buf);
2616 if (!u.uconduct.polypiles) {
2617 you_have_never("polymorphed an object");
2618 } else if (wizard) {
2619 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
2620 plur(u.uconduct.polypiles));
2621 you_have_X(buf);
2624 if (!u.uconduct.polyselfs) {
2625 you_have_never("changed form");
2626 } else if (wizard) {
2627 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
2628 plur(u.uconduct.polyselfs));
2629 you_have_X(buf);
2632 if (!u.uconduct.wishes) {
2633 you_have_X("used no wishes");
2634 } else {
2635 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
2636 (u.uconduct.wishes > 1L) ? "es" : "");
2637 if (u.uconduct.wisharti) {
2638 /* if wisharti == wishes
2639 * 1 wish (for an artifact)
2640 * 2 wishes (both for artifacts)
2641 * N wishes (all for artifacts)
2642 * else (N is at least 2 in order to get here; M < N)
2643 * N wishes (1 for an artifact)
2644 * N wishes (M for artifacts)
2646 if (u.uconduct.wisharti == u.uconduct.wishes)
2647 Sprintf(eos(buf), " (%s",
2648 (u.uconduct.wisharti > 2L) ? "all "
2649 : (u.uconduct.wisharti == 2L) ? "both " : "");
2650 else
2651 Sprintf(eos(buf), " (%ld ", u.uconduct.wisharti);
2653 Sprintf(eos(buf), "for %s)",
2654 (u.uconduct.wisharti == 1L) ? "an artifact"
2655 : "artifacts");
2657 you_have_X(buf);
2659 if (!u.uconduct.wisharti)
2660 enl_msg(You_, "have not wished", "did not wish",
2661 " for any artifacts", "");
2664 /* Pop up the window and wait for a key */
2665 display_nhwindow(en_win, TRUE);
2666 destroy_nhwindow(en_win);
2667 en_win = WIN_ERR;
2670 #ifndef M
2671 #ifndef NHSTDC
2672 #define M(c) (0x80 | (c))
2673 #else
2674 #define M(c) ((c) -128)
2675 #endif /* NHSTDC */
2676 #endif
2677 #ifndef C
2678 #define C(c) (0x1f & (c))
2679 #endif
2681 static const struct func_tab cmdlist[] = {
2682 { C('d'), FALSE, dokick }, /* "D" is for door!...? Msg is in dokick.c */
2683 { C('e'), TRUE, wiz_detect },
2684 { C('f'), TRUE, wiz_map },
2685 { C('g'), TRUE, wiz_genesis },
2686 { C('i'), TRUE, wiz_identify },
2687 { C('l'), TRUE, doredraw }, /* if number_pad is set */
2688 { C('n'), TRUE, donamelevel }, /* if number_pad is set */
2689 { C('o'), TRUE, dooverview_or_wiz_where }, /* depends on wizard status */
2690 { C('p'), TRUE, doprev_message },
2691 { C('r'), TRUE, doredraw },
2692 { C('t'), TRUE, dotele },
2693 { C('v'), TRUE, wiz_level_tele },
2694 { C('w'), TRUE, wiz_wish },
2695 { C('x'), TRUE, doattributes },
2696 { C('z'), TRUE, dosuspend_core },
2697 { 'a', FALSE, doapply },
2698 { 'A', FALSE, doddoremarm },
2699 { M('a'), TRUE, doorganize },
2700 { M('A'), TRUE, donamelevel }, /* #annotate */
2701 /* 'b', 'B' : go sw */
2702 { 'c', FALSE, doclose },
2703 { 'C', TRUE, docallcmd },
2704 { M('c'), TRUE, dotalk },
2705 { M('C'), TRUE, doconduct }, /* #conduct */
2706 { 'd', FALSE, dodrop },
2707 { 'D', FALSE, doddrop },
2708 { M('d'), FALSE, dodip },
2709 { 'e', FALSE, doeat },
2710 { 'E', FALSE, doengrave },
2711 { M('e'), TRUE, enhance_weapon_skill },
2712 { 'f', FALSE, dofire },
2713 /* 'F' : fight (one time) */
2714 { M('f'), FALSE, doforce },
2715 /* 'g', 'G' : multiple go */
2716 /* 'h', 'H' : go west */
2717 { 'h', TRUE, dohelp }, /* if number_pad is set */
2718 { 'i', TRUE, ddoinv },
2719 { 'I', TRUE, dotypeinv }, /* Robert Viduya */
2720 { M('i'), TRUE, doinvoke },
2721 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2722 { 'j', FALSE, dojump }, /* if number_pad is on */
2723 { M('j'), FALSE, dojump },
2724 { 'k', FALSE, dokick }, /* if number_pad is on */
2725 { 'l', FALSE, doloot }, /* if number_pad is on */
2726 { M('l'), FALSE, doloot },
2727 /* 'n' prefixes a count if number_pad is on */
2728 { M('m'), TRUE, domonability },
2729 { 'N', TRUE, docallcmd }, /* if number_pad is on */
2730 { M('n'), TRUE, docallcmd },
2731 { M('N'), TRUE, docallcmd },
2732 { 'o', FALSE, doopen },
2733 { 'O', TRUE, doset },
2734 { M('o'), FALSE, dosacrifice },
2735 { M('O'), TRUE, dooverview }, /* #overview */
2736 { 'p', FALSE, dopay },
2737 { 'P', FALSE, doputon },
2738 { M('p'), TRUE, dopray },
2739 { 'q', FALSE, dodrink },
2740 { 'Q', FALSE, dowieldquiver },
2741 { M('q'), TRUE, done2 },
2742 { 'r', FALSE, doread },
2743 { 'R', FALSE, doremring },
2744 { M('r'), FALSE, dorub },
2745 { M('R'), FALSE, doride }, /* #ride */
2746 { 's', TRUE, dosearch, "searching" },
2747 { 'S', TRUE, dosave },
2748 { M('s'), FALSE, dosit },
2749 { 't', FALSE, dothrow },
2750 { 'T', FALSE, dotakeoff },
2751 { M('t'), TRUE, doturn },
2752 { M('T'), FALSE, dotip }, /* #tip */
2753 /* 'u', 'U' : go ne */
2754 { 'u', FALSE, dountrap }, /* if number_pad is on */
2755 { M('u'), FALSE, dountrap },
2756 { 'v', TRUE, doversion },
2757 { 'V', TRUE, dohistory },
2758 { M('v'), TRUE, doextversion },
2759 { 'w', FALSE, dowield },
2760 { 'W', FALSE, dowear },
2761 { M('w'), FALSE, dowipe },
2762 { 'x', FALSE, doswapweapon },
2763 { 'X', FALSE, dotwoweapon },
2764 /* 'y', 'Y' : go nw */
2765 { 'z', FALSE, dozap },
2766 { 'Z', TRUE, docast },
2767 { '<', FALSE, doup },
2768 { '>', FALSE, dodown },
2769 { '/', TRUE, dowhatis },
2770 { '&', TRUE, dowhatdoes },
2771 { '?', TRUE, dohelp },
2772 { M('?'), TRUE, doextlist },
2773 #ifdef SHELL
2774 { '!', TRUE, dosh },
2775 #endif
2776 { '.', TRUE, donull, "waiting" },
2777 { ' ', TRUE, donull, "waiting" },
2778 { ',', FALSE, dopickup },
2779 { ':', TRUE, dolook },
2780 { ';', TRUE, doquickwhatis },
2781 { '^', TRUE, doidtrap },
2782 { '\\', TRUE, dodiscovered }, /* Robert Viduya */
2783 { '`', TRUE, doclassdisco },
2784 { '@', TRUE, dotogglepickup },
2785 { M('2'), FALSE, dotwoweapon },
2786 { WEAPON_SYM, TRUE, doprwep },
2787 { ARMOR_SYM, TRUE, doprarm },
2788 { RING_SYM, TRUE, doprring },
2789 { AMULET_SYM, TRUE, dopramulet },
2790 { TOOL_SYM, TRUE, doprtool },
2791 { '*', TRUE, doprinuse }, /* inventory of all equipment in use */
2792 { GOLD_SYM, TRUE, doprgold },
2793 { SPBOOK_SYM, TRUE, dovspell }, /* Mike Stephenson */
2794 { '#', TRUE, doextcmd },
2795 { '_', TRUE, dotravel },
2796 { 0, 0, 0, 0 }
2799 struct ext_func_tab extcmdlist[] = {
2800 { "adjust", "adjust inventory letters", doorganize, TRUE },
2801 { "annotate", "name current level", donamelevel, TRUE },
2802 { "chat", "talk to someone", dotalk, TRUE }, /* converse? */
2803 { "conduct", "list voluntary challenges you have maintained", doconduct,
2804 TRUE },
2805 { "dip", "dip an object into something", dodip, FALSE },
2806 { "enhance", "advance or check weapon and spell skills",
2807 enhance_weapon_skill, TRUE },
2808 { "exploremode", "enter explore mode", enter_explore_mode, TRUE },
2809 { "force", "force a lock", doforce, FALSE },
2810 { "invoke", "invoke an object's powers", doinvoke, TRUE },
2811 { "jump", "jump to a location", dojump, FALSE },
2812 { "kick", "kick something", dokick, FALSE },
2813 { "loot", "loot a box on the floor", doloot, FALSE },
2814 { "monster", "use a monster's special ability", domonability, TRUE },
2815 { "name", "name a monster or an object", docallcmd, TRUE },
2816 { "offer", "offer a sacrifice to the gods", dosacrifice, FALSE },
2817 { "overview", "show an overview of the dungeon", dooverview, TRUE },
2818 { "pray", "pray to the gods for help", dopray, TRUE },
2819 { "quit", "exit without saving current game", done2, TRUE },
2820 { "ride", "ride (or stop riding) a monster", doride, FALSE },
2821 { "rub", "rub a lamp or a stone", dorub, FALSE },
2822 { "sit", "sit down", dosit, FALSE },
2823 { "terrain", "show map without obstructions", doterrain, TRUE },
2824 { "tip", "empty a container", dotip, FALSE },
2825 { "turn", "turn undead", doturn, TRUE },
2826 { "twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE },
2827 { "untrap", "untrap something", dountrap, FALSE },
2828 { "version", "list compile time options for this version of NetHack",
2829 doextversion, TRUE },
2830 { "wipe", "wipe off your face", dowipe, FALSE },
2831 { "?", "get this list of extended commands", doextlist, TRUE },
2833 * There must be a blank entry here for every entry in the table
2834 * below.
2836 { (char *) 0, (char *) 0, donull, TRUE }, /* levelchange */
2837 { (char *) 0, (char *) 0, donull, TRUE }, /* lightsources */
2838 #ifdef DEBUG_MIGRATING_MONS
2839 { (char *) 0, (char *) 0, donull, TRUE }, /* migratemons */
2840 #endif
2841 { (char *) 0, (char *) 0, donull, TRUE }, /* monpolycontrol */
2842 { (char *) 0, (char *) 0, donull, TRUE }, /* panic */
2843 { (char *) 0, (char *) 0, donull, TRUE }, /* polyself */
2844 #ifdef PORT_DEBUG
2845 { (char *) 0, (char *) 0, donull, TRUE }, /* portdebug */
2846 #endif
2847 { (char *) 0, (char *) 0, donull, TRUE }, /* seenv */
2848 { (char *) 0, (char *) 0, donull, TRUE }, /* stats */
2849 { (char *) 0, (char *) 0, donull, TRUE }, /* timeout */
2850 { (char *) 0, (char *) 0, donull, TRUE }, /* vanquished */
2851 { (char *) 0, (char *) 0, donull, TRUE }, /* vision */
2852 { (char *) 0, (char *) 0, donull, TRUE }, /* wizsmell */
2853 { (char *) 0, (char *) 0, donull, TRUE }, /* wizintrinsic */
2854 #ifdef DEBUG
2855 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_traveldisplay */
2856 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_bury */
2857 #endif
2858 { (char *) 0, (char *) 0, donull, TRUE }, /* wizrumorcheck */
2859 { (char *) 0, (char *) 0, donull, TRUE }, /* wmode */
2860 { (char *) 0, (char *) 0, donull, TRUE } /* sentinel */
2863 /* there must be a placeholder in the table above for every entry here */
2864 static const struct ext_func_tab debug_extcmdlist[] = {
2865 { "levelchange", "change experience level", wiz_level_change, TRUE },
2866 { "lightsources", "show mobile light sources", wiz_light_sources, TRUE },
2867 #ifdef DEBUG_MIGRATING_MONS
2868 { "migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE },
2869 #endif
2870 { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol,
2871 TRUE },
2872 { "panic", "test panic routine (fatal to game)", wiz_panic, TRUE },
2873 { "polyself", "polymorph self", wiz_polyself, TRUE },
2874 #ifdef PORT_DEBUG
2875 { "portdebug", "wizard port debug command", wiz_port_debug, TRUE },
2876 #endif
2877 { "seenv", "show seen vectors", wiz_show_seenv, TRUE },
2878 { "stats", "show memory statistics", wiz_show_stats, TRUE },
2879 { "timeout", "look at timeout queue", wiz_timeout_queue, TRUE },
2880 { "vanquished", "list vanquished monsters", dovanquished, TRUE },
2881 { "vision", "show vision array", wiz_show_vision, TRUE },
2882 { "wizsmell", "smell monster", wiz_smell, TRUE },
2883 { "wizintrinsic", "set intrinsic", wiz_intrinsic, TRUE },
2884 #ifdef DEBUG
2885 { "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2886 wiz_debug_cmd_traveldisplay, TRUE },
2887 { "wizdebug_bury", "wizard debug: bury objs under and around you",
2888 wiz_debug_cmd_bury, TRUE },
2889 #endif
2890 { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, TRUE },
2891 { "wmode", "show wall modes", wiz_show_wmodes, TRUE },
2892 { (char *) 0, (char *) 0, donull, TRUE }
2896 * Insert debug commands into the extended command list. This function
2897 * assumes that the last entry will be the help entry.
2899 * You must add entries in ext_func_tab every time you add one to the
2900 * debug_extcmdlist().
2902 void
2903 add_debug_extended_commands()
2905 int i, j, k, n;
2907 /* count the # of help entries */
2908 for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++)
2911 for (i = 0; debug_extcmdlist[i].ef_txt; i++) {
2912 /* need enough room for "?" entry plus terminator */
2913 if (n + 2 >= SIZE(extcmdlist))
2914 panic("Too many debugging commands!");
2915 for (j = 0; j < n; j++)
2916 if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0)
2917 break;
2919 /* insert i'th debug entry into extcmdlist[j], pushing down */
2920 for (k = n; k >= j; --k)
2921 extcmdlist[k + 1] = extcmdlist[k];
2922 extcmdlist[j] = debug_extcmdlist[i];
2923 n++; /* now an extra entry */
2927 STATIC_OVL char
2928 cmd_from_func(fn)
2929 int NDECL((*fn));
2931 int i;
2932 for (i = 0; i < SIZE(cmdlist); ++i)
2933 if (cmdlist[i].f_funct == fn)
2934 return cmdlist[i].f_char;
2935 return 0;
2938 static const char template[] = "%-27s %4ld %6ld";
2939 static const char stats_hdr[] = " count bytes";
2940 static const char stats_sep[] = "--------------------------- ----- -------";
2942 STATIC_OVL int
2943 size_obj(otmp)
2944 struct obj *otmp;
2946 int sz = (int) sizeof(struct obj);
2948 if (otmp->oextra) {
2949 sz += (int) sizeof(struct oextra);
2950 if (ONAME(otmp))
2951 sz += (int) strlen(ONAME(otmp)) + 1;
2952 if (OMONST(otmp))
2953 sz += (int) sizeof(struct monst);
2954 if (OMID(otmp))
2955 sz += (int) sizeof(unsigned);
2956 if (OLONG(otmp))
2957 sz += (int) sizeof(long);
2958 if (OMAILCMD(otmp))
2959 sz += (int) strlen(OMAILCMD(otmp)) + 1;
2961 return sz;
2964 STATIC_OVL void
2965 count_obj(chain, total_count, total_size, top, recurse)
2966 struct obj *chain;
2967 long *total_count;
2968 long *total_size;
2969 boolean top;
2970 boolean recurse;
2972 long count, size;
2973 struct obj *obj;
2975 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
2976 if (top) {
2977 count++;
2978 size += size_obj(obj);
2980 if (recurse && obj->cobj)
2981 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
2983 *total_count += count;
2984 *total_size += size;
2987 STATIC_OVL void
2988 obj_chain(win, src, chain, force, total_count, total_size)
2989 winid win;
2990 const char *src;
2991 struct obj *chain;
2992 boolean force;
2993 long *total_count;
2994 long *total_size;
2996 char buf[BUFSZ];
2997 long count = 0L, size = 0L;
2999 count_obj(chain, &count, &size, TRUE, FALSE);
3001 if (count || size || force) {
3002 *total_count += count;
3003 *total_size += size;
3004 Sprintf(buf, template, src, count, size);
3005 putstr(win, 0, buf);
3009 STATIC_OVL void
3010 mon_invent_chain(win, src, chain, total_count, total_size)
3011 winid win;
3012 const char *src;
3013 struct monst *chain;
3014 long *total_count;
3015 long *total_size;
3017 char buf[BUFSZ];
3018 long count = 0, size = 0;
3019 struct monst *mon;
3021 for (mon = chain; mon; mon = mon->nmon)
3022 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
3024 if (count || size) {
3025 *total_count += count;
3026 *total_size += size;
3027 Sprintf(buf, template, src, count, size);
3028 putstr(win, 0, buf);
3032 STATIC_OVL void
3033 contained_stats(win, src, total_count, total_size)
3034 winid win;
3035 const char *src;
3036 long *total_count;
3037 long *total_size;
3039 char buf[BUFSZ];
3040 long count = 0, size = 0;
3041 struct monst *mon;
3043 count_obj(invent, &count, &size, FALSE, TRUE);
3044 count_obj(fobj, &count, &size, FALSE, TRUE);
3045 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3046 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3047 /* DEADMONSTER check not required in this loop since they have no
3048 * inventory */
3049 for (mon = fmon; mon; mon = mon->nmon)
3050 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3051 for (mon = migrating_mons; mon; mon = mon->nmon)
3052 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3054 if (count || size) {
3055 *total_count += count;
3056 *total_size += size;
3057 Sprintf(buf, template, src, count, size);
3058 putstr(win, 0, buf);
3062 STATIC_OVL int
3063 size_monst(mtmp, incl_wsegs)
3064 struct monst *mtmp;
3065 boolean incl_wsegs;
3067 int sz = (int) sizeof (struct monst);
3069 if (mtmp->wormno && incl_wsegs)
3070 sz += size_wseg(mtmp);
3072 if (mtmp->mextra) {
3073 sz += (int) sizeof (struct mextra);
3074 if (MNAME(mtmp))
3075 sz += (int) strlen(MNAME(mtmp)) + 1;
3076 if (EGD(mtmp))
3077 sz += (int) sizeof (struct egd);
3078 if (EPRI(mtmp))
3079 sz += (int) sizeof (struct epri);
3080 if (ESHK(mtmp))
3081 sz += (int) sizeof (struct eshk);
3082 if (EMIN(mtmp))
3083 sz += (int) sizeof (struct emin);
3084 if (EDOG(mtmp))
3085 sz += (int) sizeof (struct edog);
3086 /* mextra->mcorpsenm doesn't point to more memory */
3088 return sz;
3091 STATIC_OVL void
3092 mon_chain(win, src, chain, force, total_count, total_size)
3093 winid win;
3094 const char *src;
3095 struct monst *chain;
3096 boolean force;
3097 long *total_count;
3098 long *total_size;
3100 char buf[BUFSZ];
3101 long count, size;
3102 struct monst *mon;
3103 /* mon->wormno means something different for migrating_mons and mydogs */
3104 boolean incl_wsegs = !strcmpi(src, "fmon");
3106 count = size = 0L;
3107 for (mon = chain; mon; mon = mon->nmon) {
3108 count++;
3109 size += size_monst(mon, incl_wsegs);
3111 if (count || size || force) {
3112 *total_count += count;
3113 *total_size += size;
3114 Sprintf(buf, template, src, count, size);
3115 putstr(win, 0, buf);
3119 STATIC_OVL void
3120 misc_stats(win, total_count, total_size)
3121 winid win;
3122 long *total_count;
3123 long *total_size;
3125 char buf[BUFSZ], hdrbuf[QBUFSZ];
3126 long count, size;
3127 int idx;
3128 struct trap *tt;
3129 struct damage *sd; /* shop damage */
3130 struct cemetery *bi; /* bones info */
3132 /* traps and engravings are output unconditionally;
3133 * others only if nonzero
3135 count = size = 0L;
3136 for (tt = ftrap; tt; tt = tt->ntrap) {
3137 ++count;
3138 size += (long) sizeof *tt;
3140 *total_count += count;
3141 *total_size += size;
3142 Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap));
3143 Sprintf(buf, template, hdrbuf, count, size);
3144 putstr(win, 0, buf);
3146 count = size = 0L;
3147 engr_stats("engravings, size %ld+text", hdrbuf, &count, &size);
3148 *total_count += count;
3149 *total_size += size;
3150 Sprintf(buf, template, hdrbuf, count, size);
3151 putstr(win, 0, buf);
3153 count = size = 0L;
3154 light_stats("light sources, size %ld", hdrbuf, &count, &size);
3155 if (count || size) {
3156 *total_count += count;
3157 *total_size += size;
3158 Sprintf(buf, template, hdrbuf, count, size);
3159 putstr(win, 0, buf);
3162 count = size = 0L;
3163 timer_stats("timers, size %ld", hdrbuf, &count, &size);
3164 if (count || size) {
3165 *total_count += count;
3166 *total_size += size;
3167 Sprintf(buf, template, hdrbuf, count, size);
3168 putstr(win, 0, buf);
3171 count = size = 0L;
3172 for (sd = level.damagelist; sd; sd = sd->next) {
3173 ++count;
3174 size += (long) sizeof *sd;
3176 if (count || size) {
3177 *total_count += count;
3178 *total_size += size;
3179 Sprintf(hdrbuf, "shop damage, size %ld",
3180 (long) sizeof (struct damage));
3181 Sprintf(buf, template, hdrbuf, count, size);
3182 putstr(win, 0, buf);
3185 count = size = 0L;
3186 region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size);
3187 if (count || size) {
3188 *total_count += count;
3189 *total_size += size;
3190 Sprintf(buf, template, hdrbuf, count, size);
3191 putstr(win, 0, buf);
3194 count = size = 0L;
3195 for (bi = level.bonesinfo; bi; bi = bi->next) {
3196 ++count;
3197 size += (long) sizeof *bi;
3199 if (count || size) {
3200 *total_count += count;
3201 *total_size += size;
3202 Sprintf(hdrbuf, "bones history, size %ld",
3203 (long) sizeof (struct cemetery));
3204 Sprintf(buf, template, hdrbuf, count, size);
3205 putstr(win, 0, buf);
3208 count = size = 0L;
3209 for (idx = 0; idx < NUM_OBJECTS; ++idx)
3210 if (objects[idx].oc_uname) {
3211 ++count;
3212 size += (long) (strlen(objects[idx].oc_uname) + 1);
3214 if (count || size) {
3215 *total_count += count;
3216 *total_size += size;
3217 Strcpy(hdrbuf, "object type names, text");
3218 Sprintf(buf, template, hdrbuf, count, size);
3219 putstr(win, 0, buf);
3224 * Display memory usage of all monsters and objects on the level.
3226 static int
3227 wiz_show_stats()
3229 char buf[BUFSZ];
3230 winid win;
3231 long total_obj_size, total_obj_count,
3232 total_mon_size, total_mon_count,
3233 total_ovr_size, total_ovr_count,
3234 total_misc_size, total_misc_count;
3236 win = create_nhwindow(NHW_TEXT);
3237 putstr(win, 0, "Current memory statistics:");
3239 total_obj_count = total_obj_size = 0L;
3240 putstr(win, 0, stats_hdr);
3241 Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj));
3242 putstr(win, 0, buf);
3243 obj_chain(win, "invent", invent, TRUE, &total_obj_count, &total_obj_size);
3244 obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size);
3245 obj_chain(win, "buried", level.buriedobjlist, FALSE,
3246 &total_obj_count, &total_obj_size);
3247 obj_chain(win, "migrating obj", migrating_objs, FALSE,
3248 &total_obj_count, &total_obj_size);
3249 obj_chain(win, "billobjs", billobjs, FALSE,
3250 &total_obj_count, &total_obj_size);
3251 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3252 mon_invent_chain(win, "migrating minvent", migrating_mons,
3253 &total_obj_count, &total_obj_size);
3254 contained_stats(win, "contained", &total_obj_count, &total_obj_size);
3255 putstr(win, 0, stats_sep);
3256 Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size);
3257 putstr(win, 0, buf);
3259 total_mon_count = total_mon_size = 0L;
3260 putstr(win, 0, "");
3261 Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst));
3262 putstr(win, 0, buf);
3263 mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size);
3264 mon_chain(win, "migrating", migrating_mons, FALSE,
3265 &total_mon_count, &total_mon_size);
3266 /* 'mydogs' is only valid during level change or end of game disclosure,
3267 but conceivably we've been called from within debugger at such time */
3268 if (mydogs) /* monsters accompanying hero */
3269 mon_chain(win, "mydogs", mydogs, FALSE,
3270 &total_mon_count, &total_mon_size);
3271 putstr(win, 0, stats_sep);
3272 Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size);
3273 putstr(win, 0, buf);
3275 total_ovr_count = total_ovr_size = 0L;
3276 putstr(win, 0, "");
3277 putstr(win, 0, " Overview");
3278 overview_stats(win, template, &total_ovr_count, &total_ovr_size);
3279 putstr(win, 0, stats_sep);
3280 Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size);
3281 putstr(win, 0, buf);
3283 total_misc_count = total_misc_size = 0L;
3284 putstr(win, 0, "");
3285 putstr(win, 0, " Miscellaneous");
3286 misc_stats(win, &total_misc_count, &total_misc_size);
3287 putstr(win, 0, stats_sep);
3288 Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size);
3289 putstr(win, 0, buf);
3291 putstr(win, 0, "");
3292 putstr(win, 0, stats_sep);
3293 Sprintf(buf, template, " Grand total",
3294 (total_obj_count + total_mon_count
3295 + total_ovr_count + total_misc_count),
3296 (total_obj_size + total_mon_size
3297 + total_ovr_size + total_misc_size));
3298 putstr(win, 0, buf);
3300 #if defined(__BORLANDC__) && !defined(_WIN32)
3301 show_borlandc_stats(win);
3302 #endif
3304 display_nhwindow(win, FALSE);
3305 destroy_nhwindow(win);
3306 return 0;
3309 void
3310 sanity_check()
3312 obj_sanity_check();
3313 timer_sanity_check();
3314 mon_sanity_check();
3315 light_sources_sanity_check();
3318 #ifdef DEBUG_MIGRATING_MONS
3319 static int
3320 wiz_migrate_mons()
3322 int mcount = 0;
3323 char inbuf[BUFSZ];
3324 struct permonst *ptr;
3325 struct monst *mtmp;
3326 d_level tolevel;
3328 getlin("How many random monsters to migrate? [0]", inbuf);
3329 if (*inbuf == '\033')
3330 return 0;
3331 mcount = atoi(inbuf);
3332 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3333 return 0;
3334 while (mcount > 0) {
3335 if (Is_stronghold(&u.uz))
3336 assign_level(&tolevel, &valley_level);
3337 else
3338 get_level(&tolevel, depth(&u.uz) + 1);
3339 ptr = rndmonst();
3340 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3341 if (mtmp)
3342 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3343 (coord *) 0);
3344 mcount--;
3346 return 0;
3348 #endif
3350 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3351 #define unmeta(c) (0x7f & (c))
3353 /* called at startup and after number_pad is twiddled */
3354 void
3355 reset_commands(initial)
3356 boolean initial;
3358 static const char sdir[] = "hykulnjb><",
3359 sdir_swap_yz[] = "hzkulnjb><",
3360 ndir[] = "47896321><",
3361 ndir_phone_layout[] = "41236987><";
3362 static const int ylist[] = {
3363 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3365 const struct func_tab *cmdtmp;
3366 boolean flagtemp;
3367 int c, i, updated = 0;
3369 if (initial) {
3370 updated = 1;
3371 for (i = 0; i < SIZE(cmdlist); i++) {
3372 c = cmdlist[i].f_char & 0xff;
3373 Cmd.commands[c] = &cmdlist[i];
3375 Cmd.num_pad = FALSE;
3376 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3377 } else {
3378 /* basic num_pad */
3379 flagtemp = iflags.num_pad;
3380 if (flagtemp != Cmd.num_pad) {
3381 Cmd.num_pad = flagtemp;
3382 ++updated;
3384 /* swap_yz mode (only applicable for !num_pad); intended for
3385 QWERTZ keyboard used in Central Europe, particularly Germany */
3386 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3387 if (flagtemp != Cmd.swap_yz) {
3388 Cmd.swap_yz = flagtemp;
3389 ++updated;
3390 /* Cmd.swap_yz has been toggled;
3391 perform the swap (or reverse previous one) */
3392 for (i = 0; i < SIZE(ylist); i++) {
3393 c = ylist[i] & 0xff;
3394 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3395 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3396 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3399 /* MSDOS compatibility mode (only applicable for num_pad) */
3400 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3401 if (flagtemp != Cmd.pcHack_compat) {
3402 Cmd.pcHack_compat = flagtemp;
3403 ++updated;
3404 /* pcHack_compat has been toggled */
3405 c = M('5') & 0xff;
3406 cmdtmp = Cmd.commands['5'];
3407 Cmd.commands['5'] = Cmd.commands[c];
3408 Cmd.commands[c] = cmdtmp;
3409 c = M('0') & 0xff;
3410 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3412 /* phone keypad layout (only applicable for num_pad) */
3413 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3414 if (flagtemp != Cmd.phone_layout) {
3415 Cmd.phone_layout = flagtemp;
3416 ++updated;
3417 /* phone_layout has been toggled */
3418 for (i = 0; i < 3; i++) {
3419 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3420 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3421 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3422 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3423 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3424 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3425 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3426 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3429 } /*?initial*/
3431 if (updated)
3432 Cmd.serialno++;
3433 Cmd.dirchars = !Cmd.num_pad
3434 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3435 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3436 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3438 Cmd.move_W = Cmd.dirchars[0];
3439 Cmd.move_NW = Cmd.dirchars[1];
3440 Cmd.move_N = Cmd.dirchars[2];
3441 Cmd.move_NE = Cmd.dirchars[3];
3442 Cmd.move_E = Cmd.dirchars[4];
3443 Cmd.move_SE = Cmd.dirchars[5];
3444 Cmd.move_S = Cmd.dirchars[6];
3445 Cmd.move_SW = Cmd.dirchars[7];
3448 /* non-movement commands which accept 'm' prefix to request menu operation */
3449 STATIC_OVL boolean
3450 accept_menu_prefix(cmd_func)
3451 int NDECL((*cmd_func));
3453 if (cmd_func == dopickup || cmd_func == dotip
3454 /* eat, #offer, and apply tinning-kit all use floorfood() to pick
3455 an item on floor or in invent; 'm' skips picking from floor
3456 (ie, inventory only) rather than request use of menu operation */
3457 || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
3458 /* 'm' for removing saddle from adjacent monster without checking
3459 for containers at <u.ux,u.uy> */
3460 || cmd_func == doloot
3461 /* 'm' prefix allowed for some extended commands */
3462 || cmd_func == doextcmd || cmd_func == doextlist)
3463 return TRUE;
3464 return FALSE;
3467 void
3468 rhack(cmd)
3469 register char *cmd;
3471 boolean do_walk, do_rush, prefix_seen, bad_command,
3472 firsttime = (cmd == 0);
3474 iflags.menu_requested = FALSE;
3475 #ifdef SAFERHANGUP
3476 if (program_state.done_hup)
3477 end_of_input();
3478 #endif
3479 if (firsttime) {
3480 context.nopick = 0;
3481 cmd = parse();
3483 if (*cmd == '\033') {
3484 context.move = FALSE;
3485 return;
3487 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3488 in_doagain = TRUE;
3489 stail = 0;
3490 rhack((char *) 0); /* read and execute command */
3491 in_doagain = FALSE;
3492 return;
3494 /* Special case of *cmd == ' ' handled better below */
3495 if (!*cmd || *cmd == (char) 0377) {
3496 nhbell();
3497 context.move = FALSE;
3498 return; /* probably we just had an interrupt */
3501 /* handle most movement commands */
3502 do_walk = do_rush = prefix_seen = FALSE;
3503 context.travel = context.travel1 = 0;
3504 switch (*cmd) {
3505 case 'g':
3506 if (movecmd(cmd[1])) {
3507 context.run = 2;
3508 do_rush = TRUE;
3509 } else
3510 prefix_seen = TRUE;
3511 break;
3512 case '5':
3513 if (!Cmd.num_pad)
3514 break; /* else FALLTHRU */
3515 case 'G':
3516 if (movecmd(lowc(cmd[1]))) {
3517 context.run = 3;
3518 do_rush = TRUE;
3519 } else
3520 prefix_seen = TRUE;
3521 break;
3522 case '-':
3523 if (!Cmd.num_pad)
3524 break; /* else FALLTHRU */
3525 /* Effects of movement commands and invisible monsters:
3526 * m: always move onto space (even if 'I' remembered)
3527 * F: always attack space (even if 'I' not remembered)
3528 * normal movement: attack if 'I', move otherwise.
3530 case 'F':
3531 if (movecmd(cmd[1])) {
3532 context.forcefight = 1;
3533 do_walk = TRUE;
3534 } else
3535 prefix_seen = TRUE;
3536 break;
3537 case 'm':
3538 if (movecmd(cmd[1]) || u.dz) {
3539 context.run = 0;
3540 context.nopick = 1;
3541 if (!u.dz)
3542 do_walk = TRUE;
3543 else
3544 cmd[0] = cmd[1]; /* "m<" or "m>" */
3545 } else
3546 prefix_seen = TRUE;
3547 break;
3548 case 'M':
3549 if (movecmd(lowc(cmd[1]))) {
3550 context.run = 1;
3551 context.nopick = 1;
3552 do_rush = TRUE;
3553 } else
3554 prefix_seen = TRUE;
3555 break;
3556 case '0':
3557 if (!Cmd.num_pad)
3558 break;
3559 (void) ddoinv(); /* a convenience borrowed from the PC */
3560 context.move = FALSE;
3561 multi = 0;
3562 return;
3563 case CMD_CLICKLOOK:
3564 if (iflags.clicklook) {
3565 context.move = FALSE;
3566 do_look(2, &clicklook_cc);
3568 return;
3569 case CMD_TRAVEL:
3570 if (flags.travelcmd) {
3571 context.travel = 1;
3572 context.travel1 = 1;
3573 context.run = 8;
3574 context.nopick = 1;
3575 do_rush = TRUE;
3576 break;
3578 /*FALLTHRU*/
3579 default:
3580 if (movecmd(*cmd)) { /* ordinary movement */
3581 context.run = 0; /* only matters here if it was 8 */
3582 do_walk = TRUE;
3583 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
3584 context.run = 1;
3585 do_rush = TRUE;
3586 } else if (movecmd(unctrl(*cmd))) {
3587 context.run = 3;
3588 do_rush = TRUE;
3590 break;
3593 /* some special prefix handling */
3594 /* overload 'm' prefix to mean "request a menu" */
3595 if (prefix_seen && cmd[0] == 'm') {
3596 /* (for func_tab cast, see below) */
3597 const struct func_tab *ft = Cmd.commands[cmd[1] & 0xff];
3598 int NDECL((*func)) = ft ? ((struct func_tab *) ft)->f_funct : 0;
3600 if (func && accept_menu_prefix(func)) {
3601 iflags.menu_requested = TRUE;
3602 ++cmd;
3606 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
3607 /* trying to move diagonally as a grid bug;
3608 this used to be treated by movecmd() as not being
3609 a movement attempt, but that didn't provide for any
3610 feedback and led to strangeness if the key pressed
3611 ('u' in particular) was overloaded for num_pad use */
3612 You_cant("get there from here...");
3613 context.run = 0;
3614 context.nopick = context.forcefight = FALSE;
3615 context.move = context.mv = FALSE;
3616 multi = 0;
3617 return;
3620 if (do_walk) {
3621 if (multi)
3622 context.mv = TRUE;
3623 domove();
3624 context.forcefight = 0;
3625 return;
3626 } else if (do_rush) {
3627 if (firsttime) {
3628 if (!multi)
3629 multi = max(COLNO, ROWNO);
3630 u.last_str_turn = 0;
3632 context.mv = TRUE;
3633 domove();
3634 return;
3635 } else if (prefix_seen && cmd[1] == '\033') { /* <prefix><escape> */
3636 /* don't report "unknown command" for change of heart... */
3637 bad_command = FALSE;
3638 } else if (*cmd == ' ' && !flags.rest_on_space) {
3639 bad_command = TRUE; /* skip cmdlist[] loop */
3641 /* handle all other commands */
3642 } else {
3643 register const struct func_tab *tlist;
3644 int res, NDECL((*func));
3646 /* current - use *cmd to directly index cmdlist array */
3647 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
3648 if (u.uburied && !tlist->can_if_buried) {
3649 You_cant("do that while you are buried!");
3650 res = 0;
3651 } else {
3652 /* we discard 'const' because some compilers seem to have
3653 trouble with the pointer passed to set_occupation() */
3654 func = ((struct func_tab *) tlist)->f_funct;
3655 if (tlist->f_text && !occupation && multi)
3656 set_occupation(func, tlist->f_text, multi);
3657 res = (*func)(); /* perform the command */
3659 if (!res) {
3660 context.move = FALSE;
3661 multi = 0;
3663 return;
3665 /* if we reach here, cmd wasn't found in cmdlist[] */
3666 bad_command = TRUE;
3669 if (bad_command) {
3670 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3671 register char c;
3673 expcmd[0] = '\0';
3674 while ((c = *cmd++) != '\0')
3675 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
3677 if (!prefix_seen || !iflags.cmdassist
3678 || !help_dir(0, "Invalid direction key!"))
3679 Norep("Unknown command '%s'.", expcmd);
3681 /* didn't move */
3682 context.move = FALSE;
3683 multi = 0;
3684 return;
3687 /* convert an x,y pair into a direction code */
3689 xytod(x, y)
3690 schar x, y;
3692 register int dd;
3694 for (dd = 0; dd < 8; dd++)
3695 if (x == xdir[dd] && y == ydir[dd])
3696 return dd;
3697 return -1;
3700 /* convert a direction code into an x,y pair */
3701 void
3702 dtoxy(cc, dd)
3703 coord *cc;
3704 register int dd;
3706 cc->x = xdir[dd];
3707 cc->y = ydir[dd];
3708 return;
3711 /* also sets u.dz, but returns false for <> */
3713 movecmd(sym)
3714 char sym;
3716 register const char *dp = index(Cmd.dirchars, sym);
3718 u.dz = 0;
3719 if (!dp || !*dp)
3720 return 0;
3721 u.dx = xdir[dp - Cmd.dirchars];
3722 u.dy = ydir[dp - Cmd.dirchars];
3723 u.dz = zdir[dp - Cmd.dirchars];
3724 #if 0 /* now handled elsewhere */
3725 if (u.dx && u.dy && NODIAG(u.umonnum)) {
3726 u.dx = u.dy = 0;
3727 return 0;
3729 #endif
3730 return !u.dz;
3733 /* grid bug handling which used to be in movecmd() */
3735 dxdy_moveok()
3737 if (u.dx && u.dy && NODIAG(u.umonnum))
3738 u.dx = u.dy = 0;
3739 return u.dx || u.dy;
3742 /* decide whether a character (user input keystroke) requests screen repaint */
3743 boolean
3744 redraw_cmd(c)
3745 char c;
3747 return (boolean) (c == C('r') || (Cmd.num_pad && c == C('l')));
3750 boolean
3751 prefix_cmd(c)
3752 char c;
3754 return (boolean) (c == 'g' || c == 'G'
3755 || c == 'm' || c == 'M'
3756 || c == 'F'
3757 || (Cmd.num_pad && (c == '5' || c == '-')));
3761 * uses getdir() but unlike getdir() it specifically
3762 * produces coordinates using the direction from getdir()
3763 * and verifies that those coordinates are ok.
3765 * If the call to getdir() returns 0, Never_mind is displayed.
3766 * If the resulting coordinates are not okay, emsg is displayed.
3768 * Returns non-zero if coordinates in cc are valid.
3771 get_adjacent_loc(prompt, emsg, x, y, cc)
3772 const char *prompt, *emsg;
3773 xchar x, y;
3774 coord *cc;
3776 xchar new_x, new_y;
3777 if (!getdir(prompt)) {
3778 pline1(Never_mind);
3779 return 0;
3781 new_x = x + u.dx;
3782 new_y = y + u.dy;
3783 if (cc && isok(new_x, new_y)) {
3784 cc->x = new_x;
3785 cc->y = new_y;
3786 } else {
3787 if (emsg)
3788 pline1(emsg);
3789 return 0;
3791 return 1;
3795 getdir(s)
3796 const char *s;
3798 char dirsym;
3799 int is_mov;
3801 retry:
3802 if (in_doagain || *readchar_queue)
3803 dirsym = readchar();
3804 else
3805 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
3806 (char *) 0, '\0');
3807 /* remove the prompt string so caller won't have to */
3808 clear_nhwindow(WIN_MESSAGE);
3810 if (redraw_cmd(dirsym)) { /* ^R */
3811 docrt(); /* redraw */
3812 goto retry;
3814 savech(dirsym);
3816 if (dirsym == '.' || dirsym == 's') {
3817 u.dx = u.dy = u.dz = 0;
3818 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
3819 boolean did_help = FALSE, help_requested;
3821 if (!index(quitchars, dirsym)) {
3822 help_requested = (dirsym == '?');
3823 if (help_requested || iflags.cmdassist) {
3824 did_help = help_dir((s && *s == '^') ? dirsym : 0,
3825 help_requested ? (const char *) 0
3826 : "Invalid direction key!");
3827 if (help_requested)
3828 goto retry;
3830 if (!did_help)
3831 pline("What a strange direction!");
3833 return 0;
3834 } else if (is_mov && !dxdy_moveok()) {
3835 You_cant("orient yourself that direction.");
3836 return 0;
3838 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
3839 confdir();
3840 return 1;
3843 STATIC_OVL void
3844 show_direction_keys(win, nodiag)
3845 winid win;
3846 boolean nodiag;
3848 char buf[BUFSZ];
3850 if (nodiag) {
3851 Sprintf(buf, " %c ", Cmd.move_N);
3852 putstr(win, 0, buf);
3853 putstr(win, 0, " | ");
3854 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3855 putstr(win, 0, buf);
3856 putstr(win, 0, " | ");
3857 Sprintf(buf, " %c ", Cmd.move_S);
3858 putstr(win, 0, buf);
3859 } else {
3860 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
3861 Cmd.move_NE);
3862 putstr(win, 0, buf);
3863 putstr(win, 0, " \\ | / ");
3864 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3865 putstr(win, 0, buf);
3866 putstr(win, 0, " / | \\ ");
3867 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
3868 Cmd.move_SE);
3869 putstr(win, 0, buf);
3874 STATIC_OVL boolean
3875 help_dir(sym, msg)
3876 char sym;
3877 const char *msg;
3879 static const char wiz_only_list[] = "EFGIVW";
3880 char ctrl;
3881 winid win;
3882 char buf[BUFSZ], buf2[BUFSZ], *explain;
3884 win = create_nhwindow(NHW_TEXT);
3885 if (!win)
3886 return FALSE;
3887 if (msg) {
3888 Sprintf(buf, "cmdassist: %s", msg);
3889 putstr(win, 0, buf);
3890 putstr(win, 0, "");
3892 if (letter(sym) || sym == '[') { /* 'dat/cmdhelp' shows ESC as ^[ */
3893 sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */
3894 ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */
3895 if ((explain = dowhatdoes_core(ctrl, buf2)) != 0
3896 && (!index(wiz_only_list, sym) || wizard)) {
3897 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
3898 index(wiz_only_list, sym)
3899 ? ""
3900 : " as specified in the Guidebook");
3901 putstr(win, 0, buf);
3902 putstr(win, 0, "");
3903 putstr(win, 0, explain);
3904 putstr(win, 0, "");
3905 putstr(win, 0,
3906 "To use that command, hold down the <Ctrl> key as a shift");
3907 Sprintf(buf, "and press the <%c> key.", sym);
3908 putstr(win, 0, buf);
3909 putstr(win, 0, "");
3913 Sprintf(buf, "Valid direction keys %sare:",
3914 NODIAG(u.umonnum) ? "in your current form " : "");
3915 putstr(win, 0, buf);
3916 show_direction_keys(win, NODIAG(u.umonnum));
3918 putstr(win, 0, "");
3919 putstr(win, 0, " < up");
3920 putstr(win, 0, " > down");
3921 putstr(win, 0, " . direct at yourself");
3922 if (msg) {
3923 /* non-null msg means that this wasn't an explicit user request */
3924 putstr(win, 0, "");
3925 putstr(win, 0,
3926 "(Suppress this message with !cmdassist in config file.)");
3928 display_nhwindow(win, FALSE);
3929 destroy_nhwindow(win);
3930 return TRUE;
3933 void
3934 confdir()
3936 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
3938 u.dx = xdir[x];
3939 u.dy = ydir[x];
3940 return;
3943 const char *
3944 directionname(dir)
3945 int dir;
3947 static NEARDATA const char *const dirnames[] = {
3948 "west", "northwest", "north", "northeast", "east",
3949 "southeast", "south", "southwest", "down", "up",
3952 if (dir < 0 || dir >= SIZE(dirnames))
3953 return "invalid";
3954 return dirnames[dir];
3958 isok(x, y)
3959 register int x, y;
3961 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
3962 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
3965 static NEARDATA int last_multi;
3968 * convert a MAP window position into a movecmd
3970 const char *
3971 click_to_cmd(x, y, mod)
3972 int x, y, mod;
3974 int dir;
3975 static char cmd[4];
3976 cmd[1] = 0;
3978 if (iflags.clicklook && mod == CLICK_2) {
3979 clicklook_cc.x = x;
3980 clicklook_cc.y = y;
3981 cmd[0] = CMD_CLICKLOOK;
3982 return cmd;
3985 x -= u.ux;
3986 y -= u.uy;
3988 if (flags.travelcmd) {
3989 if (abs(x) <= 1 && abs(y) <= 1) {
3990 x = sgn(x), y = sgn(y);
3991 } else {
3992 u.tx = u.ux + x;
3993 u.ty = u.uy + y;
3994 cmd[0] = CMD_TRAVEL;
3995 return cmd;
3998 if (x == 0 && y == 0) {
3999 /* here */
4000 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
4001 || IS_SINK(levl[u.ux][u.uy].typ)) {
4002 cmd[0] = mod == CLICK_1 ? 'q' : M('d');
4003 return cmd;
4004 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
4005 cmd[0] = M('s');
4006 return cmd;
4007 } else if ((u.ux == xupstair && u.uy == yupstair)
4008 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4009 && sstairs.up)
4010 || (u.ux == xupladder && u.uy == yupladder)) {
4011 return "<";
4012 } else if ((u.ux == xdnstair && u.uy == ydnstair)
4013 || (u.ux == sstairs.sx && u.uy == sstairs.sy
4014 && !sstairs.up)
4015 || (u.ux == xdnladder && u.uy == ydnladder)) {
4016 return ">";
4017 } else if (OBJ_AT(u.ux, u.uy)) {
4018 cmd[0] =
4019 Is_container(level.objects[u.ux][u.uy]) ? M('l') : ',';
4020 return cmd;
4021 } else {
4022 return "."; /* just rest */
4026 /* directional commands */
4028 dir = xytod(x, y);
4030 if (!m_at(u.ux + x, u.uy + y)
4031 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
4032 cmd[1] = Cmd.dirchars[dir];
4033 cmd[2] = '\0';
4034 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
4035 /* slight assistance to the player: choose kick/open for them
4037 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
4038 cmd[0] = C('d');
4039 return cmd;
4041 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
4042 cmd[0] = 'o';
4043 return cmd;
4046 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
4047 cmd[0] = 's';
4048 cmd[1] = 0;
4049 return cmd;
4052 } else {
4053 /* convert without using floating point, allowing sloppy clicking */
4054 if (x > 2 * abs(y))
4055 x = 1, y = 0;
4056 else if (y > 2 * abs(x))
4057 x = 0, y = 1;
4058 else if (x < -2 * abs(y))
4059 x = -1, y = 0;
4060 else if (y < -2 * abs(x))
4061 x = 0, y = -1;
4062 else
4063 x = sgn(x), y = sgn(y);
4065 if (x == 0 && y == 0) /* map click on player to "rest" command */
4066 return ".";
4068 dir = xytod(x, y);
4071 /* move, attack, etc. */
4072 cmd[1] = 0;
4073 if (mod == CLICK_1) {
4074 cmd[0] = Cmd.dirchars[dir];
4075 } else {
4076 cmd[0] = (Cmd.num_pad
4077 ? M(Cmd.dirchars[dir])
4078 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
4081 return cmd;
4084 char
4085 get_count(allowchars, inkey, maxcount, count)
4086 char *allowchars;
4087 char inkey;
4088 long maxcount;
4089 long *count;
4091 char qbuf[QBUFSZ];
4092 int key;
4093 long cnt = 0L;
4094 boolean backspaced = FALSE;
4095 /* this should be done in port code so that we have erase_char
4096 and kill_char available; we can at least fake erase_char */
4097 #define STANDBY_erase_char '\177'
4099 for (;;) {
4100 if (inkey) {
4101 key = inkey;
4102 inkey = '\0';
4103 } else
4104 key = readchar();
4106 if (digit(key)) {
4107 cnt = 10L * cnt + (long) (key - '0');
4108 if (cnt < 0)
4109 cnt = 0;
4110 else if (maxcount > 0 && cnt > maxcount)
4111 cnt = maxcount;
4112 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
4113 cnt = cnt / 10;
4114 backspaced = TRUE;
4115 } else if (key == '\033') {
4116 break;
4117 } else if (!allowchars || index(allowchars, key)) {
4118 *count = cnt;
4119 break;
4122 if (cnt > 9 || backspaced) {
4123 clear_nhwindow(WIN_MESSAGE);
4124 if (backspaced && !cnt) {
4125 Sprintf(qbuf, "Count: ");
4126 } else {
4127 Sprintf(qbuf, "Count: %ld", cnt);
4128 backspaced = FALSE;
4130 pline1(qbuf);
4131 mark_synch();
4134 return key;
4138 STATIC_OVL char *
4139 parse()
4141 #ifdef LINT /* static char in_line[COLNO]; */
4142 char in_line[COLNO];
4143 #else
4144 static char in_line[COLNO];
4145 #endif
4146 register int foo;
4147 boolean prezero = FALSE;
4149 multi = 0;
4150 context.move = 1;
4151 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
4153 #ifdef ALTMETA
4154 alt_esc = iflags.altmeta; /* readchar() hack */
4155 #endif
4156 if (!Cmd.num_pad || (foo = readchar()) == 'n') {
4157 long tmpmulti = multi;
4159 foo = get_count(NULL, '\0', LARGEST_INT, &tmpmulti);
4160 last_multi = multi = tmpmulti;
4162 #ifdef ALTMETA
4163 alt_esc = FALSE; /* readchar() reset */
4164 #endif
4166 if (foo == '\033') { /* esc cancels count (TH) */
4167 clear_nhwindow(WIN_MESSAGE);
4168 multi = last_multi = 0;
4169 } else if (foo == DOAGAIN || in_doagain) {
4170 multi = last_multi;
4171 } else {
4172 last_multi = multi;
4173 savech(0); /* reset input queue */
4174 savech((char) foo);
4177 if (multi) {
4178 multi--;
4179 save_cm = in_line;
4180 } else {
4181 save_cm = (char *) 0;
4183 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
4184 if (Cmd.pcHack_compat) {
4185 /* This handles very old inconsistent DOS/Windows behaviour
4186 in a different way: earlier, the keyboard handler mapped
4187 these, which caused counts to be strange when entered
4188 from the number pad. Now do not map them until here. */
4189 switch (foo) {
4190 case '5':
4191 foo = 'g';
4192 break;
4193 case M('5'):
4194 foo = 'G';
4195 break;
4196 case M('0'):
4197 foo = 'I';
4198 break;
4199 default:
4200 break; /* as is */
4204 in_line[0] = foo;
4205 in_line[1] = '\0';
4206 if (prefix_cmd(foo)) {
4207 foo = readchar();
4208 savech((char) foo);
4209 in_line[1] = foo;
4210 in_line[2] = 0;
4212 clear_nhwindow(WIN_MESSAGE);
4213 if (prezero)
4214 in_line[0] = '\033';
4215 return in_line;
4218 #ifdef HANGUPHANDLING
4219 /* some very old systems, or descendents of such systems, expect signal
4220 handlers to have return type `int', but they don't actually inspect
4221 the return value so we should be safe using `void' unconditionally */
4222 /*ARGUSED*/
4223 void
4224 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4225 int sig_unused UNUSED;
4227 if (program_state.exiting)
4228 program_state.in_moveloop = 0;
4229 nhwindows_hangup();
4230 #ifdef SAFERHANGUP
4231 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4232 and a couple of other places; actual hangup handling occurs then.
4233 This is 'safer' because it disallows certain cheats and also
4234 protects against losing objects in the process of being thrown,
4235 but also potentially riskier because the disconnected program
4236 must continue running longer before attempting a hangup save. */
4237 program_state.done_hup++;
4238 /* defer hangup iff game appears to be in progress */
4239 if (program_state.in_moveloop && program_state.something_worth_saving)
4240 return;
4241 #endif /* SAFERHANGUP */
4242 end_of_input();
4245 void
4246 end_of_input()
4248 #ifdef NOSAVEONHANGUP
4249 #ifdef INSURANCE
4250 if (flags.ins_chkpt && program_state.something_worth_saving)
4251 program_statue.preserve_locks = 1; /* keep files for recovery */
4252 #endif
4253 program_state.something_worth_saving = 0; /* don't save */
4254 #endif
4256 #ifndef SAFERHANGUP
4257 if (!program_state.done_hup++)
4258 #endif
4259 if (program_state.something_worth_saving)
4260 (void) dosave0();
4261 if (iflags.window_inited)
4262 exit_nhwindows((char *) 0);
4263 clearlocks();
4264 terminate(EXIT_SUCCESS);
4265 /*NOTREACHED*/ /* not necessarily true for vms... */
4266 return;
4268 #endif /* HANGUPHANDLING */
4270 char
4271 readchar()
4273 register int sym;
4274 int x = u.ux, y = u.uy, mod = 0;
4276 if (*readchar_queue)
4277 sym = *readchar_queue++;
4278 else
4279 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4281 #ifdef NR_OF_EOFS
4282 if (sym == EOF) {
4283 register int cnt = NR_OF_EOFS;
4285 * Some SYSV systems seem to return EOFs for various reasons
4286 * (?like when one hits break or for interrupted systemcalls?),
4287 * and we must see several before we quit.
4289 do {
4290 clearerr(stdin); /* omit if clearerr is undefined */
4291 sym = pgetchar();
4292 } while (--cnt && sym == EOF);
4294 #endif /* NR_OF_EOFS */
4296 if (sym == EOF) {
4297 #ifdef HANGUPHANDLING
4298 hangup(0); /* call end_of_input() or set program_state.done_hup */
4299 #endif
4300 sym = '\033';
4301 #ifdef ALTMETA
4302 } else if (sym == '\033' && alt_esc) {
4303 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4304 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4305 if (sym == EOF || sym == 0)
4306 sym = '\033';
4307 else if (sym != '\033')
4308 sym |= 0200; /* force 8th bit on */
4309 #endif /*ALTMETA*/
4310 } else if (sym == 0) {
4311 /* click event */
4312 readchar_queue = click_to_cmd(x, y, mod);
4313 sym = *readchar_queue++;
4315 return (char) sym;
4318 STATIC_PTR int
4319 dotravel(VOID_ARGS)
4321 /* Keyboard travel command */
4322 static char cmd[2];
4323 coord cc;
4325 if (!flags.travelcmd)
4326 return 0;
4327 cmd[1] = 0;
4328 cc.x = iflags.travelcc.x;
4329 cc.y = iflags.travelcc.y;
4330 if (cc.x == -1 && cc.y == -1) {
4331 /* No cached destination, start attempt from current position */
4332 cc.x = u.ux;
4333 cc.y = u.uy;
4335 iflags.getloc_travelmode = TRUE;
4336 pline("Where do you want to travel to?");
4337 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4338 /* user pressed ESC */
4339 iflags.getloc_travelmode = FALSE;
4340 return 0;
4342 iflags.getloc_travelmode = FALSE;
4343 iflags.travelcc.x = u.tx = cc.x;
4344 iflags.travelcc.y = u.ty = cc.y;
4345 cmd[0] = CMD_TRAVEL;
4346 readchar_queue = cmd;
4347 return 0;
4350 #ifdef PORT_DEBUG
4351 extern void NDECL(win32con_debug_keystrokes);
4352 extern void NDECL(win32con_handler_info);
4355 wiz_port_debug()
4357 int n, k;
4358 winid win;
4359 anything any;
4360 int item = 'a';
4361 int num_menu_selections;
4362 struct menu_selection_struct {
4363 char *menutext;
4364 void NDECL((*fn));
4365 } menu_selections[] = {
4366 #ifdef WIN32
4367 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4368 { "show keystroke handler information (tty only)",
4369 win32con_handler_info },
4370 #endif
4371 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4374 num_menu_selections = SIZE(menu_selections) - 1;
4375 if (num_menu_selections > 0) {
4376 menu_item *pick_list;
4377 win = create_nhwindow(NHW_MENU);
4378 start_menu(win);
4379 for (k = 0; k < num_menu_selections; ++k) {
4380 any.a_int = k + 1;
4381 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4382 menu_selections[k].menutext, MENU_UNSELECTED);
4384 end_menu(win, "Which port debugging feature?");
4385 n = select_menu(win, PICK_ONE, &pick_list);
4386 destroy_nhwindow(win);
4387 if (n > 0) {
4388 n = pick_list[0].item.a_int - 1;
4389 free((genericptr_t) pick_list);
4390 /* execute the function */
4391 (*menu_selections[n].fn)();
4393 } else
4394 pline("No port-specific debug capability defined.");
4395 return 0;
4397 #endif /*PORT_DEBUG*/
4400 * Parameter validator for generic yes/no function to prevent
4401 * the core from sending too long a prompt string to the
4402 * window port causing a buffer overflow there.
4404 char
4405 yn_function(query, resp, def)
4406 const char *query, *resp;
4407 char def;
4409 char qbuf[QBUFSZ];
4411 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4413 /* maximum acceptable length is QBUFSZ-1 */
4414 if (strlen(query) >= QBUFSZ) {
4415 /* caller shouldn't have passed anything this long */
4416 paniclog("Query truncated: ", query);
4417 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4418 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4419 query = qbuf;
4421 return (*windowprocs.win_yn_function)(query, resp, def);
4424 /* for paranoid_confirm:quit,die,attack prompting */
4425 boolean
4426 paranoid_query(be_paranoid, prompt)
4427 boolean be_paranoid;
4428 const char *prompt;
4430 boolean confirmed_ok;
4432 /* when paranoid, player must respond with "yes" rather than just 'y'
4433 to give the go-ahead for this query; default is "no" unless the
4434 ParanoidConfirm flag is set in which case there's no default */
4435 if (be_paranoid) {
4436 char qbuf[QBUFSZ], ans[BUFSZ];
4437 const char *promptprefix = "", *responsetype = ParanoidConfirm
4438 ? "(yes|no)"
4439 : "(yes) [no]";
4440 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4442 /* in addition to being paranoid about this particular
4443 query, we might be even more paranoid about all paranoia
4444 responses (ie, ParanoidConfirm is set) in which case we
4445 require "no" to reject in addition to "yes" to confirm
4446 (except we won't loop if response is ESC; it means no) */
4447 do {
4448 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4449 getlin(qbuf, ans);
4450 (void) mungspaces(ans);
4451 confirmed_ok = !strcmpi(ans, "yes");
4452 if (confirmed_ok || *ans == '\033')
4453 break;
4454 promptprefix = "\"Yes\" or \"No\": ";
4455 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4456 } else
4457 confirmed_ok = (yn(prompt) == 'y');
4459 return confirmed_ok;
4463 dosuspend_core()
4465 #ifdef SUSPEND
4466 /* Does current window system support suspend? */
4467 if ((*windowprocs.win_can_suspend)()) {
4468 /* NB: SYSCF SHELLERS handled in port code. */
4469 dosuspend();
4470 } else
4471 #endif
4472 Norep("Suspend command not available.");
4473 return 0;
4476 /*cmd.c*/