tuning: succubi
[aNetHack.git] / src / cmd.c
blob40c711bd0e4f732acb527bbe4e5ab6b28df01bd2
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 *));
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 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 long *, long *));
157 STATIC_DCL void FDECL(contained, (winid, const char *, long *, long *));
158 STATIC_PTR int NDECL(wiz_show_stats);
159 STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*))));
160 #ifdef PORT_DEBUG
161 STATIC_DCL int NDECL(wiz_port_debug);
162 #endif
163 STATIC_PTR int NDECL(wiz_rumor_check);
164 STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*))));
165 STATIC_PTR int NDECL(doattributes);
166 STATIC_PTR int NDECL(doconduct); /**/
168 STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *,
169 const char *));
170 STATIC_DCL char *FDECL(enlght_combatinc, (const char *, int, int, char *));
171 STATIC_DCL void FDECL(enlght_halfdmg, (int, int));
172 STATIC_DCL boolean NDECL(walking_on_water);
173 STATIC_DCL boolean FDECL(cause_known, (int));
174 STATIC_DCL char *FDECL(attrval, (int, int, char *));
175 STATIC_DCL void FDECL(background_enlightenment, (int, int));
176 STATIC_DCL void FDECL(characteristics_enlightenment, (int, int));
177 STATIC_DCL void FDECL(one_characteristic, (int, int, int));
178 STATIC_DCL void FDECL(status_enlightenment, (int, int));
179 STATIC_DCL void FDECL(attributes_enlightenment, (int, int));
181 static const char *readchar_queue = "";
182 static coord clicklook_cc;
184 STATIC_DCL char *NDECL(parse);
185 STATIC_DCL boolean FDECL(help_dir, (CHAR_P, const char *));
187 STATIC_PTR int
188 doprev_message(VOID_ARGS)
190 return nh_doprev_message();
193 /* Count down by decrementing multi */
194 STATIC_PTR int
195 timed_occupation(VOID_ARGS)
197 (*timed_occ_fn)();
198 if (multi > 0)
199 multi--;
200 return multi > 0;
203 /* If you have moved since initially setting some occupations, they
204 * now shouldn't be able to restart.
206 * The basic rule is that if you are carrying it, you can continue
207 * since it is with you. If you are acting on something at a distance,
208 * your orientation to it must have changed when you moved.
210 * The exception to this is taking off items, since they can be taken
211 * off in a number of ways in the intervening time, screwing up ordering.
213 * Currently: Take off all armor.
214 * Picking Locks / Forcing Chests.
215 * Setting traps.
217 void
218 reset_occupations()
220 reset_remarm();
221 reset_pick();
222 reset_trapset();
225 /* If a time is given, use it to timeout this function, otherwise the
226 * function times out by its own means.
228 void
229 set_occupation(fn, txt, xtime)
230 int NDECL((*fn));
231 const char *txt;
232 int xtime;
234 if (xtime) {
235 occupation = timed_occupation;
236 timed_occ_fn = fn;
237 } else
238 occupation = fn;
239 occtxt = txt;
240 occtime = 0;
241 return;
244 STATIC_DCL char NDECL(popch);
246 /* Provide a means to redo the last command. The flag `in_doagain' is set
247 * to true while redoing the command. This flag is tested in commands that
248 * require additional input (like `throw' which requires a thing and a
249 * direction), and the input prompt is not shown. Also, while in_doagain is
250 * TRUE, no keystrokes can be saved into the saveq.
252 #define BSIZE 20
253 static char pushq[BSIZE], saveq[BSIZE];
254 static NEARDATA int phead, ptail, shead, stail;
256 STATIC_OVL char
257 popch()
259 /* If occupied, return '\0', letting tgetch know a character should
260 * be read from the keyboard. If the character read is not the
261 * ABORT character (as checked in pcmain.c), that character will be
262 * pushed back on the pushq.
264 if (occupation)
265 return '\0';
266 if (in_doagain)
267 return (char) ((shead != stail) ? saveq[stail++] : '\0');
268 else
269 return (char) ((phead != ptail) ? pushq[ptail++] : '\0');
272 char
273 pgetchar() /* courtesy of aeb@cwi.nl */
275 register int ch;
277 if (!(ch = popch()))
278 ch = nhgetch();
279 return (char) ch;
282 /* A ch == 0 resets the pushq */
283 void
284 pushch(ch)
285 char ch;
287 if (!ch)
288 phead = ptail = 0;
289 if (phead < BSIZE)
290 pushq[phead++] = ch;
291 return;
294 /* A ch == 0 resets the saveq. Only save keystrokes when not
295 * replaying a previous command.
297 void
298 savech(ch)
299 char ch;
301 if (!in_doagain) {
302 if (!ch)
303 phead = ptail = shead = stail = 0;
304 else if (shead < BSIZE)
305 saveq[shead++] = ch;
307 return;
310 /* here after # - now read a full-word command */
311 STATIC_PTR int
312 doextcmd(VOID_ARGS)
314 int idx, retval;
315 int NDECL((*func));
317 /* keep repeating until we don't run help or quit */
318 do {
319 idx = get_ext_cmd();
320 if (idx < 0)
321 return 0; /* quit */
323 func = extcmdlist[idx].ef_funct;
324 if (iflags.menu_requested && !accept_menu_prefix(func)) {
325 pline("'m' prefix has no effect for this command.");
326 iflags.menu_requested = FALSE;
328 retval = (*func)();
329 } while (func == doextlist);
331 return retval;
334 /* here after #? - now list all full-word commands */
336 doextlist(VOID_ARGS)
338 register const struct ext_func_tab *efp;
339 char buf[BUFSZ];
340 winid datawin;
342 datawin = create_nhwindow(NHW_TEXT);
343 putstr(datawin, 0, "");
344 putstr(datawin, 0, " Extended Commands List");
345 putstr(datawin, 0, "");
346 putstr(datawin, 0, " Press '#', then type:");
347 putstr(datawin, 0, "");
349 for (efp = extcmdlist; efp->ef_txt; efp++) {
350 Sprintf(buf, " %-15s - %s.", efp->ef_txt, efp->ef_desc);
351 putstr(datawin, 0, buf);
353 display_nhwindow(datawin, FALSE);
354 destroy_nhwindow(datawin);
355 return 0;
358 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
359 #define MAX_EXT_CMD 50 /* Change if we ever have > 50 ext cmds */
362 * This is currently used only by the tty port and is
363 * controlled via runtime option 'extmenu'.
364 * ``# ?'' is counted towards the limit of the number of commands,
365 * so we actually support MAX_EXT_CMD-1 "real" extended commands.
367 * Here after # - now show pick-list of possible commands.
370 extcmd_via_menu()
372 const struct ext_func_tab *efp;
373 menu_item *pick_list = (menu_item *) 0;
374 winid win;
375 anything any;
376 const struct ext_func_tab *choices[MAX_EXT_CMD + 1];
377 char buf[BUFSZ];
378 char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20];
379 int i, n, nchoices, acount;
380 int ret, biggest;
381 int accelerator, prevaccelerator;
382 int matchlevel = 0;
384 ret = 0;
385 cbuf[0] = '\0';
386 biggest = 0;
387 while (!ret) {
388 i = n = 0;
389 any = zeroany;
390 /* populate choices */
391 for (efp = extcmdlist; efp->ef_txt; efp++) {
392 if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) {
393 choices[i] = efp;
394 if ((int) strlen(efp->ef_desc) > biggest) {
395 biggest = strlen(efp->ef_desc);
396 Sprintf(fmtstr, "%%-%ds", biggest + 15);
398 if (++i > MAX_EXT_CMD) {
399 #if defined(BETA)
400 impossible(
401 "Exceeded %d extended commands in doextcmd() menu; 'extmenu' disabled.",
402 MAX_EXT_CMD);
403 #endif /* BETA */
404 iflags.extmenu = 0;
405 return -1;
409 choices[i] = (struct ext_func_tab *) 0;
410 nchoices = i;
411 /* if we're down to one, we have our selection so get out of here */
412 if (nchoices == 1) {
413 for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
414 if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) {
415 ret = i;
416 break;
418 break;
421 /* otherwise... */
422 win = create_nhwindow(NHW_MENU);
423 start_menu(win);
424 accelerator = prevaccelerator = 0;
425 acount = 0;
426 for (i = 0; choices[i]; ++i) {
427 accelerator = choices[i]->ef_txt[matchlevel];
428 if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) {
429 if (acount) {
430 /* flush extended cmds for that letter already in buf */
431 Sprintf(buf, fmtstr, prompt);
432 any.a_char = prevaccelerator;
433 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE,
434 buf, FALSE);
435 acount = 0;
438 prevaccelerator = accelerator;
439 if (!acount || nchoices < (ROWNO - 3)) {
440 Sprintf(prompt, "%s [%s]", choices[i]->ef_txt,
441 choices[i]->ef_desc);
442 } else if (acount == 1) {
443 Sprintf(prompt, "%s or %s", choices[i - 1]->ef_txt,
444 choices[i]->ef_txt);
445 } else {
446 Strcat(prompt, " or ");
447 Strcat(prompt, choices[i]->ef_txt);
449 ++acount;
451 if (acount) {
452 /* flush buf */
453 Sprintf(buf, fmtstr, prompt);
454 any.a_char = prevaccelerator;
455 add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf,
456 FALSE);
458 Sprintf(prompt, "Extended Command: %s", cbuf);
459 end_menu(win, prompt);
460 n = select_menu(win, PICK_ONE, &pick_list);
461 destroy_nhwindow(win);
462 if (n == 1) {
463 if (matchlevel > (QBUFSZ - 2)) {
464 free((genericptr_t) pick_list);
465 #if defined(BETA)
466 impossible("Too many chars (%d) entered in extcmd_via_menu()",
467 matchlevel);
468 #endif
469 ret = -1;
470 } else {
471 cbuf[matchlevel++] = pick_list[0].item.a_char;
472 cbuf[matchlevel] = '\0';
473 free((genericptr_t) pick_list);
475 } else {
476 if (matchlevel) {
477 ret = 0;
478 matchlevel = 0;
479 } else
480 ret = -1;
483 return ret;
485 #endif /* TTY_GRAPHICS */
487 /* #monster command - use special monster ability while polymorphed */
488 STATIC_PTR int
489 domonability(VOID_ARGS)
491 if (can_breathe(youmonst.data))
492 return dobreathe();
493 else if (attacktype(youmonst.data, AT_SPIT))
494 return dospit();
495 else if (youmonst.data->mlet == S_NYMPH)
496 return doremove();
497 else if (attacktype(youmonst.data, AT_GAZE))
498 return dogaze();
499 else if (is_were(youmonst.data))
500 return dosummon();
501 else if (webmaker(youmonst.data))
502 return dospinweb();
503 else if (is_hider(youmonst.data))
504 return dohide();
505 else if (is_mind_flayer(youmonst.data))
506 return domindblast();
507 else if (u.umonnum == PM_GREMLIN) {
508 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)) {
509 if (split_mon(&youmonst, (struct monst *) 0))
510 dryup(u.ux, u.uy, TRUE);
511 } else
512 There("is no fountain here.");
513 } else if (is_unicorn(youmonst.data)) {
514 use_unicorn_horn((struct obj *) 0);
515 return 1;
516 } else if (youmonst.data->msound == MS_SHRIEK) {
517 You("shriek.");
518 if (u.uburied)
519 pline("Unfortunately sound does not carry well through rock.");
520 else
521 aggravate();
522 } else if (youmonst.data->mlet == S_VAMPIRE)
523 return dopoly();
524 else if (Upolyd)
525 pline("Any special ability you may have is purely reflexive.");
526 else
527 You("don't have a special ability in your normal form!");
528 return 0;
532 enter_explore_mode(VOID_ARGS)
534 if (wizard) {
535 You("are in debug mode.");
536 } else if (discover) {
537 You("are already in explore mode.");
538 } else {
539 #ifdef SYSCF
540 #if defined(UNIX)
541 if (!sysopt.explorers || !sysopt.explorers[0]
542 || !check_user_string(sysopt.explorers)) {
543 You("cannot access explore mode.");
544 return 0;
546 #endif
547 #endif
548 pline(
549 "Beware! From explore mode there will be no return to normal game.");
550 if (paranoid_query(ParanoidQuit,
551 "Do you want to enter explore mode?")) {
552 clear_nhwindow(WIN_MESSAGE);
553 You("are now in non-scoring explore mode.");
554 discover = TRUE;
555 } else {
556 clear_nhwindow(WIN_MESSAGE);
557 pline("Resuming normal game.");
560 return 0;
563 STATIC_PTR int
564 dooverview_or_wiz_where(VOID_ARGS)
566 if (wizard)
567 return wiz_where();
568 else
569 dooverview();
570 return 0;
573 /* ^W command - wish for something */
574 STATIC_PTR int
575 wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */
577 if (wizard) {
578 boolean save_verbose = flags.verbose;
580 flags.verbose = FALSE;
581 makewish();
582 flags.verbose = save_verbose;
583 (void) encumber_msg();
584 } else
585 pline("Unavailable command '%s'.",
586 visctrl((int) cmd_from_func(wiz_wish)));
587 return 0;
590 /* ^I command - reveal and optionally identify hero's inventory */
591 STATIC_PTR int
592 wiz_identify(VOID_ARGS)
594 if (wizard) {
595 iflags.override_ID = (int) cmd_from_func(wiz_identify);
596 if (display_inventory((char *) 0, TRUE) == -1)
597 identify_pack(0, FALSE);
598 iflags.override_ID = 0;
599 } else
600 pline("Unavailable command '%s'.",
601 visctrl((int) cmd_from_func(wiz_identify)));
602 return 0;
605 /* ^F command - reveal the level map and any traps on it */
606 STATIC_PTR int
607 wiz_map(VOID_ARGS)
609 if (wizard) {
610 struct trap *t;
611 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
613 HConfusion = HHallucination = 0L;
614 for (t = ftrap; t != 0; t = t->ntrap) {
615 t->tseen = 1;
616 map_trap(t, TRUE);
618 do_mapping();
619 HConfusion = save_Hconf;
620 HHallucination = save_Hhallu;
621 } else
622 pline("Unavailable command '%s'.",
623 visctrl((int) cmd_from_func(wiz_map)));
624 return 0;
627 /* ^G command - generate monster(s); a count prefix will be honored */
628 STATIC_PTR int
629 wiz_genesis(VOID_ARGS)
631 if (wizard)
632 (void) create_particular();
633 else
634 pline("Unavailable command '%s'.",
635 visctrl((int) cmd_from_func(wiz_genesis)));
636 return 0;
639 /* ^O command - display dungeon layout */
640 STATIC_PTR int
641 wiz_where(VOID_ARGS)
643 if (wizard)
644 (void) print_dungeon(FALSE, (schar *) 0, (xchar *) 0);
645 else
646 pline("Unavailable command '%s'.",
647 visctrl((int) cmd_from_func(wiz_where)));
648 return 0;
651 /* ^E command - detect unseen (secret doors, traps, hidden monsters) */
652 STATIC_PTR int
653 wiz_detect(VOID_ARGS)
655 if (wizard)
656 (void) findit();
657 else
658 pline("Unavailable command '%s'.",
659 visctrl((int) cmd_from_func(wiz_detect)));
660 return 0;
663 /* ^V command - level teleport */
664 STATIC_PTR int
665 wiz_level_tele(VOID_ARGS)
667 if (wizard)
668 level_tele();
669 else
670 pline("Unavailable command '%s'.",
671 visctrl((int) cmd_from_func(wiz_level_tele)));
672 return 0;
675 /* #monpolycontrol command - choose new form for shapechangers, polymorphees */
676 STATIC_PTR int
677 wiz_mon_polycontrol(VOID_ARGS)
679 iflags.mon_polycontrol = !iflags.mon_polycontrol;
680 pline("Monster polymorph control is %s.",
681 iflags.mon_polycontrol ? "on" : "off");
682 return 0;
685 /* #levelchange command - adjust hero's experience level */
686 STATIC_PTR int
687 wiz_level_change(VOID_ARGS)
689 char buf[BUFSZ];
690 int newlevel;
691 int ret;
693 getlin("To what experience level do you want to be set?", buf);
694 (void) mungspaces(buf);
695 if (buf[0] == '\033' || buf[0] == '\0')
696 ret = 0;
697 else
698 ret = sscanf(buf, "%d", &newlevel);
700 if (ret != 1) {
701 pline1(Never_mind);
702 return 0;
704 if (newlevel == u.ulevel) {
705 You("are already that experienced.");
706 } else if (newlevel < u.ulevel) {
707 if (u.ulevel == 1) {
708 You("are already as inexperienced as you can get.");
709 return 0;
711 if (newlevel < 1)
712 newlevel = 1;
713 while (u.ulevel > newlevel)
714 losexp("#levelchange");
715 } else {
716 if (u.ulevel >= MAXULEV) {
717 You("are already as experienced as you can get.");
718 return 0;
720 if (newlevel > MAXULEV)
721 newlevel = MAXULEV;
722 while (u.ulevel < newlevel)
723 pluslvl(FALSE);
725 u.ulevelmax = u.ulevel;
726 return 0;
729 /* #panic command - test program's panic handling */
730 STATIC_PTR int
731 wiz_panic(VOID_ARGS)
733 if (yn("Do you want to call panic() and end your game?") == 'y')
734 panic("Crash test.");
735 return 0;
738 /* #polyself command - change hero's form */
739 STATIC_PTR int
740 wiz_polyself(VOID_ARGS)
742 polyself(1);
743 return 0;
746 /* #seenv command */
747 STATIC_PTR int
748 wiz_show_seenv(VOID_ARGS)
750 winid win;
751 int x, y, v, startx, stopx, curx;
752 char row[COLNO + 1];
754 win = create_nhwindow(NHW_TEXT);
756 * Each seenv description takes up 2 characters, so center
757 * the seenv display around the hero.
759 startx = max(1, u.ux - (COLNO / 4));
760 stopx = min(startx + (COLNO / 2), COLNO);
761 /* can't have a line exactly 80 chars long */
762 if (stopx - startx == COLNO / 2)
763 startx++;
765 for (y = 0; y < ROWNO; y++) {
766 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
767 if (x == u.ux && y == u.uy) {
768 row[curx] = row[curx + 1] = '@';
769 } else {
770 v = levl[x][y].seenv & 0xff;
771 if (v == 0)
772 row[curx] = row[curx + 1] = ' ';
773 else
774 Sprintf(&row[curx], "%02x", v);
777 /* remove trailing spaces */
778 for (x = curx - 1; x >= 0; x--)
779 if (row[x] != ' ')
780 break;
781 row[x + 1] = '\0';
783 putstr(win, 0, row);
785 display_nhwindow(win, TRUE);
786 destroy_nhwindow(win);
787 return 0;
790 /* #vision command */
791 STATIC_PTR int
792 wiz_show_vision(VOID_ARGS)
794 winid win;
795 int x, y, v;
796 char row[COLNO + 1];
798 win = create_nhwindow(NHW_TEXT);
799 Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
800 COULD_SEE, IN_SIGHT, TEMP_LIT);
801 putstr(win, 0, row);
802 putstr(win, 0, "");
803 for (y = 0; y < ROWNO; y++) {
804 for (x = 1; x < COLNO; x++) {
805 if (x == u.ux && y == u.uy)
806 row[x] = '@';
807 else {
808 v = viz_array[y][x]; /* data access should be hidden */
809 if (v == 0)
810 row[x] = ' ';
811 else
812 row[x] = '0' + viz_array[y][x];
815 /* remove trailing spaces */
816 for (x = COLNO - 1; x >= 1; x--)
817 if (row[x] != ' ')
818 break;
819 row[x + 1] = '\0';
821 putstr(win, 0, &row[1]);
823 display_nhwindow(win, TRUE);
824 destroy_nhwindow(win);
825 return 0;
828 /* #wmode command */
829 STATIC_PTR int
830 wiz_show_wmodes(VOID_ARGS)
832 winid win;
833 int x, y;
834 char row[COLNO + 1];
835 struct rm *lev;
836 boolean istty = !strcmp(windowprocs.name, "tty");
838 win = create_nhwindow(NHW_TEXT);
839 if (istty)
840 putstr(win, 0, ""); /* tty only: blank top line */
841 for (y = 0; y < ROWNO; y++) {
842 for (x = 0; x < COLNO; x++) {
843 lev = &levl[x][y];
844 if (x == u.ux && y == u.uy)
845 row[x] = '@';
846 else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
847 row[x] = '0' + (lev->wall_info & WM_MASK);
848 else if (lev->typ == CORR)
849 row[x] = '#';
850 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
851 row[x] = '.';
852 else
853 row[x] = 'x';
855 row[COLNO] = '\0';
856 /* map column 0, levl[0][], is off the left edge of the screen */
857 putstr(win, 0, &row[1]);
859 display_nhwindow(win, TRUE);
860 destroy_nhwindow(win);
861 return 0;
864 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
865 STATIC_OVL void
866 wiz_map_levltyp(VOID_ARGS)
868 winid win;
869 int x, y, terrain;
870 char row[COLNO + 1];
871 boolean istty = !strcmp(windowprocs.name, "tty");
873 win = create_nhwindow(NHW_TEXT);
874 /* map row 0, levl[][0], is drawn on the second line of tty screen */
875 if (istty)
876 putstr(win, 0, ""); /* tty only: blank top line */
877 for (y = 0; y < ROWNO; y++) {
878 /* map column 0, levl[0][], is off the left edge of the screen;
879 it should always have terrain type "undiggable stone" */
880 for (x = 1; x < COLNO; x++) {
881 terrain = levl[x][y].typ;
882 /* assumes there aren't more than 10+26+26 terrain types */
883 row[x - 1] = (char) ((terrain == 0 && !may_dig(x, y))
884 ? '*'
885 : (terrain < 10)
886 ? '0' + terrain
887 : (terrain < 36)
888 ? 'a' + terrain - 10
889 : 'A' + terrain - 36);
891 if (levl[0][y].typ != 0 || may_dig(0, y))
892 row[x++] = '!';
893 row[x] = '\0';
894 putstr(win, 0, row);
898 char dsc[BUFSZ];
899 s_level *slev = Is_special(&u.uz);
901 Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel);
902 /* [dungeon branch features currently omitted] */
903 /* special level features */
904 if (slev) {
905 Sprintf(eos(dsc), " \"%s\"", slev->proto);
906 /* special level flags (note: dungeon.def doesn't set `maze'
907 or `hell' for any specific levels so those never show up) */
908 if (slev->flags.maze_like)
909 Strcat(dsc, " mazelike");
910 if (slev->flags.hellish)
911 Strcat(dsc, " hellish");
912 if (slev->flags.town)
913 Strcat(dsc, " town");
914 if (slev->flags.rogue_like)
915 Strcat(dsc, " roguelike");
916 /* alignment currently omitted to save space */
918 /* level features */
919 if (level.flags.nfountains)
920 Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym,
921 (int) level.flags.nfountains);
922 if (level.flags.nsinks)
923 Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym,
924 (int) level.flags.nsinks);
925 if (level.flags.has_vault)
926 Strcat(dsc, " vault");
927 if (level.flags.has_shop)
928 Strcat(dsc, " shop");
929 if (level.flags.has_temple)
930 Strcat(dsc, " temple");
931 if (level.flags.has_court)
932 Strcat(dsc, " throne");
933 if (level.flags.has_zoo)
934 Strcat(dsc, " zoo");
935 if (level.flags.has_morgue)
936 Strcat(dsc, " morgue");
937 if (level.flags.has_barracks)
938 Strcat(dsc, " barracks");
939 if (level.flags.has_beehive)
940 Strcat(dsc, " hive");
941 if (level.flags.has_swamp)
942 Strcat(dsc, " swamp");
943 /* level flags */
944 if (level.flags.noteleport)
945 Strcat(dsc, " noTport");
946 if (level.flags.hardfloor)
947 Strcat(dsc, " noDig");
948 if (level.flags.nommap)
949 Strcat(dsc, " noMMap");
950 if (!level.flags.hero_memory)
951 Strcat(dsc, " noMem");
952 if (level.flags.shortsighted)
953 Strcat(dsc, " shortsight");
954 if (level.flags.graveyard)
955 Strcat(dsc, " graveyard");
956 if (level.flags.is_maze_lev)
957 Strcat(dsc, " maze");
958 if (level.flags.is_cavernous_lev)
959 Strcat(dsc, " cave");
960 if (level.flags.arboreal)
961 Strcat(dsc, " tree");
962 if (Sokoban)
963 Strcat(dsc, " sokoban-rules");
964 /* non-flag info; probably should include dungeon branching
965 checks (extra stairs and magic portals) here */
966 if (Invocation_lev(&u.uz))
967 Strcat(dsc, " invoke");
968 if (On_W_tower_level(&u.uz))
969 Strcat(dsc, " tower");
970 /* append a branch identifier for completeness' sake */
971 if (u.uz.dnum == 0)
972 Strcat(dsc, " dungeon");
973 else if (u.uz.dnum == mines_dnum)
974 Strcat(dsc, " mines");
975 else if (In_sokoban(&u.uz))
976 Strcat(dsc, " sokoban");
977 else if (u.uz.dnum == quest_dnum)
978 Strcat(dsc, " quest");
979 else if (Is_knox(&u.uz))
980 Strcat(dsc, " ludios");
981 else if (u.uz.dnum == 1)
982 Strcat(dsc, " gehennom");
983 else if (u.uz.dnum == tower_dnum)
984 Strcat(dsc, " vlad");
985 else if (In_endgame(&u.uz))
986 Strcat(dsc, " endgame");
987 else {
988 /* somebody's added a dungeon branch we're not expecting */
989 const char *brname = dungeons[u.uz.dnum].dname;
991 if (!brname || !*brname)
992 brname = "unknown";
993 if (!strncmpi(brname, "the ", 4))
994 brname += 4;
995 Sprintf(eos(dsc), " %s", brname);
997 /* limit the line length to map width */
998 if (strlen(dsc) >= COLNO)
999 dsc[COLNO - 1] = '\0'; /* truncate */
1000 putstr(win, 0, dsc);
1003 display_nhwindow(win, TRUE);
1004 destroy_nhwindow(win);
1005 return;
1008 /* temporary? hack, since level type codes aren't the same as screen
1009 symbols and only the latter have easily accessible descriptions */
1010 static const char *levltyp[] = {
1011 "stone", "vertical wall", "horizontal wall", "top-left corner wall",
1012 "top-right corner wall", "bottom-left corner wall",
1013 "bottom-right corner wall", "cross wall", "tee-up wall", "tee-down wall",
1014 "tee-left wall", "tee-right wall", "drawbridge wall", "tree",
1015 "secret door", "secret corridor", "pool", "moat", "water",
1016 "drawbridge up", "lava pool", "iron bars", "door", "corridor", "room",
1017 "stairs", "ladder", "fountain", "throne", "sink", "grave", "altar", "ice",
1018 "drawbridge down", "air", "cloud",
1019 /* not a real terrain type, but used for undiggable stone
1020 by wiz_map_levltyp() */
1021 "unreachable/undiggable",
1022 /* padding in case the number of entries above is odd */
1026 /* explanation of base-36 output from wiz_map_levltyp() */
1027 STATIC_OVL void
1028 wiz_levltyp_legend(VOID_ARGS)
1030 winid win;
1031 int i, j, last, c;
1032 const char *dsc, *fmt;
1033 char buf[BUFSZ];
1035 win = create_nhwindow(NHW_TEXT);
1036 putstr(win, 0, "#terrain encodings:");
1037 putstr(win, 0, "");
1038 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
1039 *buf = '\0';
1040 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
1041 and right hand column holds [N/2],[N/2+1],...,[N-1];
1042 N ('last') will always be even, and may or may not include
1043 the empty string entry to pad out the final pair, depending
1044 upon how many other entries are present in levltyp[] */
1045 last = SIZE(levltyp) & ~1;
1046 for (i = 0; i < last / 2; ++i)
1047 for (j = i; j < last; j += last / 2) {
1048 dsc = levltyp[j];
1049 c = !*dsc ? ' '
1050 : !strncmp(dsc, "unreachable", 11) ? '*'
1051 /* same int-to-char conversion as wiz_map_levltyp() */
1052 : (j < 10) ? '0' + j
1053 : (j < 36) ? 'a' + j - 10
1054 : 'A' + j - 36;
1055 Sprintf(eos(buf), fmt, c, dsc);
1056 if (j > i) {
1057 putstr(win, 0, buf);
1058 *buf = '\0';
1061 display_nhwindow(win, TRUE);
1062 destroy_nhwindow(win);
1063 return;
1066 /* #wizsmell command - test usmellmon(). */
1067 STATIC_PTR int
1068 wiz_smell(VOID_ARGS)
1070 int ans = 0;
1071 int mndx; /* monster index */
1072 coord cc; /* screen pos of unknown glyph */
1073 int glyph; /* glyph at selected position */
1075 cc.x = u.ux;
1076 cc.y = u.uy;
1077 mndx = 0; /* gcc -Wall lint */
1078 if (!olfaction(youmonst.data)) {
1079 You("are incapable of detecting odors in your present form.");
1080 return 0;
1083 pline("You can move the cursor to a monster that you want to smell.");
1084 do {
1085 pline("Pick a monster to smell.");
1086 ans = getpos(&cc, TRUE, "a monster");
1087 if (ans < 0 || cc.x < 0) {
1088 return 0; /* done */
1090 /* Convert the glyph at the selected position to a mndxbol. */
1091 glyph = glyph_at(cc.x, cc.y);
1092 if (glyph_is_monster(glyph))
1093 mndx = glyph_to_mon(glyph);
1094 else
1095 mndx = 0;
1096 /* Is it a monster? */
1097 if (mndx) {
1098 if (!usmellmon(&mons[mndx]))
1099 pline("That monster seems to give off no smell.");
1100 } else
1101 pline("That is not a monster.");
1102 } while (TRUE);
1103 return 0;
1106 /* #wizinstrinsic command to set some intrinsics for testing */
1107 STATIC_PTR int
1108 wiz_intrinsic(VOID_ARGS)
1110 if (wizard) {
1111 winid win;
1112 anything any;
1113 int i, n, accelerator;
1114 menu_item *pick_list = (menu_item *) 0;
1116 static const char *const intrinsics[] = {
1117 "deafness",
1120 win = create_nhwindow(NHW_MENU);
1121 start_menu(win);
1122 accelerator = 0;
1124 for (i = 0; i < SIZE(intrinsics); ++i) {
1125 accelerator = intrinsics[i][0];
1126 any.a_int = i + 1;
1127 add_menu(win, NO_GLYPH, &any, accelerator, 0, ATR_NONE, intrinsics[i], FALSE);
1129 end_menu(win, "Which intrinsic?");
1130 n = select_menu(win, PICK_ONE, &pick_list);
1131 destroy_nhwindow(win);
1133 if (n >= 1) {
1134 i = pick_list[0].item.a_int-1;
1135 free((genericptr_t) pick_list);
1136 } else {
1137 return 0;
1140 if (!strcmp(intrinsics[i],"deafness")) {
1141 You("go deaf.");
1142 incr_itimeout(&HDeaf, 30);
1143 context.botl = TRUE;
1145 } else
1146 pline("Unavailable command '%s'.",
1147 visctrl((int) cmd_from_func(wiz_intrinsic)));
1148 return 0;
1151 /* #wizrumorcheck command - verify each rumor access */
1152 STATIC_PTR int
1153 wiz_rumor_check(VOID_ARGS)
1155 rumor_check();
1156 return 0;
1159 /* #terrain command -- show known map, inspired by crawl's '|' command */
1160 STATIC_PTR int
1161 doterrain(VOID_ARGS)
1163 winid men;
1164 menu_item *sel;
1165 anything any;
1166 int n;
1167 int which;
1170 * normal play: choose between known map without mons, obj, and traps
1171 * (to see underlying terrain only), or
1172 * known map without mons and objs (to see traps under mons and objs), or
1173 * known map without mons (to see objects under monsters);
1174 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1175 * wizard mode: normal and explore choices plus
1176 * a dump of the internal levl[][].typ codes w/ level flags, or
1177 * a legend for the levl[][].typ codes dump
1179 men = create_nhwindow(NHW_MENU);
1180 start_menu(men);
1181 any = zeroany;
1182 any.a_int = 1;
1183 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1184 "known map without monsters, objects, and traps",
1185 MENU_SELECTED);
1186 any.a_int = 2;
1187 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1188 "known map without monsters and objects",
1189 MENU_UNSELECTED);
1190 any.a_int = 3;
1191 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1192 "known map without monsters",
1193 MENU_UNSELECTED);
1194 if (discover || wizard) {
1195 any.a_int = 4;
1196 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1197 "full map without monsters, objects, and traps",
1198 MENU_UNSELECTED);
1199 if (wizard) {
1200 any.a_int = 5;
1201 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1202 "internal levl[][].typ codes in base-36",
1203 MENU_UNSELECTED);
1204 any.a_int = 6;
1205 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1206 "legend of base-36 levl[][].typ codes",
1207 MENU_UNSELECTED);
1210 end_menu(men, "View which?");
1212 n = select_menu(men, PICK_ONE, &sel);
1213 destroy_nhwindow(men);
1215 * n < 0: player used ESC to cancel;
1216 * n == 0: preselected entry was explicitly chosen and got toggled off;
1217 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1218 * n == 2: another entry was explicitly chosen, so skip preselected one.
1220 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1221 if (n > 1 && which == 1)
1222 which = sel[1].item.a_int;
1223 if (n > 0)
1224 free((genericptr_t) sel);
1226 switch (which) {
1227 case 1: reveal_terrain(0, 0); break; /* known map */
1228 case 2: reveal_terrain(0, 1); break; /* known map with traps */
1229 case 3: reveal_terrain(0, 1|2); break; /* known map w/ traps & objs */
1230 case 4: reveal_terrain(1, 0); break; /* full map */
1231 case 5: wiz_map_levltyp(); break; /* map internals */
1232 case 6: wiz_levltyp_legend(); break; /* internal details */
1233 default: break;
1235 return 0; /* no time elapses */
1238 /* -enlightenment and conduct- */
1239 static winid en_win = WIN_ERR;
1240 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1241 have[] = "have ", had[] = "had ", can[] = "can ",
1242 could[] = "could ";
1243 static const char have_been[] = "have been ", have_never[] = "have never ",
1244 never[] = "never ";
1246 #define enl_msg(prefix, present, past, suffix, ps) \
1247 enlght_line(prefix, final ? past : present, suffix, ps)
1248 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1249 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1250 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1251 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1252 #define you_have_never(badthing) \
1253 enl_msg(You_, have_never, never, badthing, "")
1254 #define you_have_X(something) \
1255 enl_msg(You_, have, (const char *) "", something, "")
1257 static void
1258 enlght_line(start, middle, end, ps)
1259 const char *start, *middle, *end, *ps;
1261 char buf[BUFSZ];
1263 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1264 putstr(en_win, 0, buf);
1267 /* format increased chance to hit or damage or defense (Protection) */
1268 static char *
1269 enlght_combatinc(inctyp, incamt, final, outbuf)
1270 const char *inctyp;
1271 int incamt, final;
1272 char *outbuf;
1274 const char *modif, *bonus;
1275 boolean invrt;
1276 int absamt;
1278 absamt = abs(incamt);
1279 /* Protection amount is typically larger than damage or to-hit;
1280 reduce magnitude by a third in order to stretch modifier ranges
1281 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1282 if (!strcmp(inctyp, "defense"))
1283 absamt = (absamt * 2) / 3;
1285 if (absamt <= 3)
1286 modif = "small";
1287 else if (absamt <= 6)
1288 modif = "moderate";
1289 else if (absamt <= 12)
1290 modif = "large";
1291 else
1292 modif = "huge";
1294 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1295 bonus = (incamt >= 0) ? "bonus" : "penalty";
1296 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1297 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1299 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1300 invrt ? bonus : inctyp);
1301 if (final || wizard)
1302 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1304 return outbuf;
1307 /* report half physical or half spell damage */
1308 STATIC_OVL void
1309 enlght_halfdmg(category, final)
1310 int category;
1311 int final;
1313 const char *category_name;
1314 char buf[BUFSZ];
1316 switch (category) {
1317 case HALF_PHDAM:
1318 category_name = "physical";
1319 break;
1320 case HALF_SPDAM:
1321 category_name = "spell";
1322 break;
1323 default:
1324 category_name = "unknown";
1325 break;
1327 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1328 category_name);
1329 enl_msg(You_, "take", "took", buf, from_what(category));
1332 /* is hero actively using water walking capability on water (or lava)? */
1333 STATIC_OVL boolean
1334 walking_on_water()
1336 if (u.uinwater || Levitation || Flying)
1337 return FALSE;
1338 return (boolean) (Wwalking
1339 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1342 /* check whether hero is wearing something that player definitely knows
1343 confers the target property; item must have been seen and its type
1344 discovered but it doesn't necessarily have to be fully identified */
1345 STATIC_OVL boolean
1346 cause_known(propindx)
1347 int propindx; /* index of a property which can be conveyed by worn item */
1349 register struct obj *o;
1350 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1352 /* simpler than from_what()/what_gives(); we don't attempt to
1353 handle artifacts and we deliberately ignore wielded items */
1354 for (o = invent; o; o = o->nobj) {
1355 if (!(o->owornmask & mask))
1356 continue;
1357 if ((int) objects[o->otyp].oc_oprop == propindx
1358 && objects[o->otyp].oc_name_known && o->dknown)
1359 return TRUE;
1361 return FALSE;
1364 /* format a characteristic value, accommodating Strength's strangeness */
1365 STATIC_OVL char *
1366 attrval(attrindx, attrvalue, resultbuf)
1367 int attrindx, attrvalue;
1368 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1370 if (attrindx != A_STR || attrvalue <= 18)
1371 Sprintf(resultbuf, "%d", attrvalue);
1372 else if (attrvalue > STR18(100)) /* 19 to 25 */
1373 Sprintf(resultbuf, "%d", attrvalue - 100);
1374 else /* simplify "18/ **" to be "18/100" */
1375 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1376 return resultbuf;
1379 void
1380 enlightenment(mode, final)
1381 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1382 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1384 char buf[BUFSZ], tmpbuf[BUFSZ];
1386 Strcpy(tmpbuf, plname);
1387 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1388 /* as in background_enlightenment, when poly'd we need to use the saved
1389 gender in u.mfemale rather than the current you-as-monster gender */
1390 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1391 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1392 ? urole.name.f
1393 : urole.name.m);
1395 en_win = create_nhwindow(NHW_MENU);
1396 /* title */
1397 putstr(en_win, 0, buf); /* "Conan the Archeologist's attributes:" */
1398 /* background and characteristics; ^X or end-of-game disclosure */
1399 if (mode & BASICENLIGHTENMENT) {
1400 /* role, race, alignment, deities */
1401 background_enlightenment(mode, final);
1402 /* strength, dexterity, &c */
1403 characteristics_enlightenment(mode, final);
1405 /* expanded status line information, including things which aren't
1406 included there due to space considerations--such as obvious
1407 alternative movement indicators (riding, levitation, &c), and
1408 various troubles (turning to stone, trapped, confusion, &c);
1409 shown for both basic and magic enlightenment */
1410 status_enlightenment(mode, final);
1411 /* remaining attributes; shown for potion,&c or wizard mode and
1412 explore mode ^X or end of game disclosure */
1413 if (mode & MAGICENLIGHTENMENT) {
1414 /* intrinsics and other traditional enlightenment feedback */
1415 attributes_enlightenment(mode, final);
1417 display_nhwindow(en_win, TRUE);
1418 destroy_nhwindow(en_win);
1419 en_win = WIN_ERR;
1422 /*ARGSUSED*/
1423 /* display role, race, alignment and such to en_win */
1424 STATIC_OVL void
1425 background_enlightenment(unused_mode, final)
1426 int unused_mode UNUSED;
1427 int final;
1429 const char *role_titl, *rank_titl;
1430 int innategend, difgend, difalgn;
1431 char buf[BUFSZ], tmpbuf[BUFSZ];
1433 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1434 to access hero's saved gender-as-human/elf/&c rather than current one */
1435 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1436 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1437 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1439 putstr(en_win, 0, ""); /* separator after title */
1440 putstr(en_win, 0, "Background:");
1442 /* if polymorphed, report current shape before underlying role;
1443 will be repeated as first status: "you are transformed" and also
1444 among various attributes: "you are in beast form" (after being
1445 told about lycanthropy) or "you are polymorphed into <a foo>"
1446 (with countdown timer appended for wizard mode); we really want
1447 the player to know he's not a samurai at the moment... */
1448 if (Upolyd) {
1449 struct permonst *uasmon = youmonst.data;
1451 tmpbuf[0] = '\0';
1452 /* here we always use current gender, not saved role gender */
1453 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1454 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1455 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1456 uasmon->mname);
1457 you_are(buf, "");
1460 /* report role; omit gender if it's redundant (eg, "female priestess") */
1461 tmpbuf[0] = '\0';
1462 if (!urole.name.f
1463 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1464 || innategend != flags.initgend))
1465 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1466 buf[0] = '\0';
1467 if (Upolyd)
1468 Strcpy(buf, "actually "); /* "You are actually a ..." */
1469 if (!strcmpi(rank_titl, role_titl)) {
1470 /* omit role when rank title matches it */
1471 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1472 tmpbuf, urace.noun);
1473 } else {
1474 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1475 tmpbuf, urace.adj, role_titl);
1477 you_are(buf, "");
1479 /* report alignment (bypass you_are() in order to omit ending period) */
1480 Sprintf(buf, " %s%s%s, %son a mission for %s",
1481 You_, !final ? are : were,
1482 align_str(u.ualign.type),
1483 /* helm of opposite alignment (might hide conversion) */
1484 (u.ualign.type != u.ualignbase[A_CURRENT]) ? "temporarily "
1485 /* permanent conversion */
1486 : (u.ualign.type != u.ualignbase[A_ORIGINAL]) ? "now "
1487 /* atheist (ignored in very early game) */
1488 : (!u.uconduct.gnostic && moves > 1000L) ? "nominally "
1489 /* lastly, normal case */
1490 : "",
1491 u_gname());
1492 putstr(en_win, 0, buf);
1493 /* show the rest of this game's pantheon (finishes previous sentence)
1494 [appending "also Moloch" at the end would allow for straightforward
1495 trailing "and" on all three aligned entries but looks too verbose] */
1496 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1497 if (u.ualign.type != A_LAWFUL)
1498 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1499 align_str(A_LAWFUL));
1500 if (u.ualign.type != A_NEUTRAL)
1501 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1502 align_str(A_NEUTRAL),
1503 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1504 if (u.ualign.type != A_CHAOTIC)
1505 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1506 align_str(A_CHAOTIC));
1507 Strcat(buf, "."); /* terminate sentence */
1508 putstr(en_win, 0, buf);
1510 /* show original alignment,gender,race,role if any have been changed;
1511 giving separate message for temporary alignment change bypasses need
1512 for tricky phrasing otherwise necessitated by possibility of having
1513 helm of opposite alignment mask a permanent alignment conversion */
1514 difgend = (innategend != flags.initgend);
1515 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1516 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1517 ? 2 : 0));
1518 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1519 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1520 you_are(buf, "");
1521 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1523 if (difgend || difalgn) { /* sex change or perm align change or both */
1524 Sprintf(buf, " You started out %s%s%s.",
1525 difgend ? genders[flags.initgend].adj : "",
1526 (difgend && difalgn) ? " and " : "",
1527 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1528 putstr(en_win, 0, buf);
1532 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1533 STATIC_OVL void
1534 characteristics_enlightenment(mode, final)
1535 int mode;
1536 int final;
1538 putstr(en_win, 0, ""); /* separator after background */
1539 putstr(en_win, 0,
1540 final ? "Final Characteristics:" : "Current Characteristics:");
1542 /* bottom line order */
1543 one_characteristic(mode, final, A_STR); /* strength */
1544 one_characteristic(mode, final, A_DEX); /* dexterity */
1545 one_characteristic(mode, final, A_CON); /* constitution */
1546 one_characteristic(mode, final, A_INT); /* intelligence */
1547 one_characteristic(mode, final, A_WIS); /* wisdom */
1548 one_characteristic(mode, final, A_CHA); /* charisma */
1551 /* display one attribute value for characteristics_enlightenment() */
1552 STATIC_OVL void
1553 one_characteristic(mode, final, attrindx)
1554 int mode, final, attrindx;
1556 boolean hide_innate_value = FALSE, interesting_alimit;
1557 int acurrent, abase, apeak, alimit;
1558 const char *attrname, *paren_pfx;
1559 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
1561 /* being polymorphed or wearing certain cursed items prevents
1562 hero from reliably tracking changes to characteristics so
1563 we don't show base & peak values then; when the items aren't
1564 cursed, hero could take them off to check underlying values
1565 and we show those in such case so that player doesn't need
1566 to actually resort to doing that */
1567 if (Upolyd) {
1568 hide_innate_value = TRUE;
1569 } else if (Fixed_abil) {
1570 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
1571 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
1572 hide_innate_value = TRUE;
1574 switch (attrindx) {
1575 case A_STR:
1576 attrname = "strength";
1577 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
1578 hide_innate_value = TRUE;
1579 break;
1580 case A_DEX:
1581 attrname = "dexterity";
1582 break;
1583 case A_CON:
1584 attrname = "constitution";
1585 break;
1586 case A_INT:
1587 attrname = "intelligence";
1588 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1589 hide_innate_value = TRUE;
1590 break;
1591 case A_WIS:
1592 attrname = "wisdom";
1593 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1594 hide_innate_value = TRUE;
1595 break;
1596 case A_CHA:
1597 attrname = "charisma";
1598 break;
1599 default:
1600 return; /* impossible */
1602 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1603 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
1604 hide_innate_value = FALSE;
1606 acurrent = ACURR(attrindx);
1607 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
1608 Sprintf(subjbuf, "Your %s ", attrname);
1610 if (!hide_innate_value) {
1611 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1612 (a magic bonus or penalty is in effect) or abase doesn't match
1613 amax (some points have been lost to poison or exercise abuse
1614 and are restorable) or attrmax is different from normal human
1615 (while game is in progress; trying to reduce dependency on
1616 spoilers to keep track of such stuff) or attrmax was different
1617 from abase (at end of game; this attribute wasn't maxed out) */
1618 abase = ABASE(attrindx);
1619 apeak = AMAX(attrindx);
1620 alimit = ATTRMAX(attrindx);
1621 /* criterium for whether the limit is interesting varies */
1622 interesting_alimit =
1623 final ? TRUE /* was originally `(abase != alimit)' */
1624 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
1625 paren_pfx = final ? " (" : " (current; ";
1626 if (acurrent != abase) {
1627 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
1628 attrval(attrindx, abase, valstring));
1629 paren_pfx = ", ";
1631 if (abase != apeak) {
1632 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
1633 attrval(attrindx, apeak, valstring));
1634 paren_pfx = ", ";
1636 if (interesting_alimit) {
1637 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
1638 /* more verbose if exceeding 'limit' due to magic bonus */
1639 (acurrent > alimit) ? "innate " : "",
1640 attrval(attrindx, alimit, valstring));
1641 /* paren_pfx = ", "; */
1643 if (acurrent != abase || abase != apeak || interesting_alimit)
1644 Strcat(valubuf, ")");
1646 enl_msg(subjbuf, "is ", "was ", valubuf, "");
1649 /* status: selected obvious capabilities, assorted troubles */
1650 STATIC_OVL void
1651 status_enlightenment(mode, final)
1652 int mode;
1653 int final;
1655 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
1656 int cap;
1657 char buf[BUFSZ], youtoo[BUFSZ];
1658 boolean Riding = (u.usteed
1659 /* if hero dies while dismounting, u.usteed will still
1660 be set; we want to ignore steed in that situation */
1661 && !(final == ENL_GAMEOVERDEAD
1662 && !strcmp(killer.name, "riding accident")));
1663 const char *steedname = (!Riding ? (char *) 0
1664 : x_monnam(u.usteed,
1665 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
1666 (char *) 0,
1667 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
1668 FALSE));
1671 * Status (many are abbreviated on bottom line; others are or
1672 * should be discernible to the hero hence to the player)
1674 putstr(en_win, 0, ""); /* separator after title or characteristics */
1675 putstr(en_win, 0, final ? "Final Status:" : "Current Status:");
1677 Strcpy(youtoo, You_);
1678 /* not a traditional status but inherently obvious to player; more
1679 detail given below (attributes section) for magic enlightenment */
1680 if (Upolyd)
1681 you_are("transformed", "");
1682 /* not a trouble, but we want to display riding status before maybe
1683 reporting steed as trapped or hero stuck to cursed saddle */
1684 if (Riding) {
1685 Sprintf(buf, "riding %s", steedname);
1686 you_are(buf, "");
1687 Sprintf(eos(youtoo), "and %s ", steedname);
1689 /* other movement situations that hero should always know */
1690 if (Levitation) {
1691 if (Lev_at_will && magic)
1692 you_are("levitating, at will", "");
1693 else
1694 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
1695 } else if (Flying) { /* can only fly when not levitating */
1696 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
1698 if (Underwater) {
1699 you_are("underwater", "");
1700 } else if (u.uinwater) {
1701 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
1702 } else if (walking_on_water()) {
1703 /* show active Wwalking here, potential Wwalking elsewhere */
1704 Sprintf(buf, "walking on %s",
1705 is_pool(u.ux, u.uy) ? "water"
1706 : is_lava(u.ux, u.uy) ? "lava"
1707 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
1708 you_are(buf, from_what(WWALKING));
1710 if (Upolyd && (u.uundetected || youmonst.m_ap_type != M_AP_NOTHING))
1711 youhiding(TRUE, final);
1713 /* internal troubles, mostly in the order that prayer ranks them */
1714 if (Stoned)
1715 you_are("turning to stone", "");
1716 if (Slimed)
1717 you_are("turning into slime", "");
1718 if (Strangled) {
1719 if (u.uburied) {
1720 you_are("buried", "");
1721 } else {
1722 Strcpy(buf, "being strangled");
1723 if (wizard)
1724 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
1725 you_are(buf, from_what(STRANGLED));
1728 if (Sick) {
1729 /* prayer lumps these together; botl puts Ill before FoodPois */
1730 if (u.usick_type & SICK_NONVOMITABLE)
1731 you_are("terminally sick from illness", "");
1732 if (u.usick_type & SICK_VOMITABLE)
1733 you_are("terminally sick from food poisoning", "");
1735 if (Vomiting)
1736 you_are("nauseated", "");
1737 if (Stunned)
1738 you_are("stunned", "");
1739 if (Confusion)
1740 you_are("confused", "");
1741 if (Hallucination)
1742 you_are("hallucinating", "");
1743 if (Blind) {
1744 /* from_what() (currently wizard-mode only) checks !haseyes()
1745 before u.uroleplay.blind, so we should too */
1746 Sprintf(buf, "%s blind",
1747 !haseyes(youmonst.data) ? "innately"
1748 : u.uroleplay.blind ? "permanently"
1749 /* better phrasing desperately wanted... */
1750 : Blindfolded_only ? "deliberately"
1751 : "temporarily");
1752 if (wizard && (Blinded & TIMEOUT) != 0L
1753 && !u.uroleplay.blind && haseyes(youmonst.data))
1754 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
1755 /* !haseyes: avoid "you are innately blind innately" */
1756 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
1758 if (Deaf)
1759 you_are("deaf", from_what(DEAF));
1761 /* external troubles, more or less */
1762 if (Punished) {
1763 if (uball) {
1764 Sprintf(buf, "chained to %s", ansimpleoname(uball));
1765 } else {
1766 impossible("Punished without uball?");
1767 Strcpy(buf, "punished");
1769 you_are(buf, "");
1771 if (u.utrap) {
1772 char predicament[BUFSZ];
1773 struct trap *t;
1774 boolean anchored = (u.utraptype == TT_BURIEDBALL);
1776 if (anchored) {
1777 Strcpy(predicament, "tethered to something buried");
1778 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
1779 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
1780 } else {
1781 Strcpy(predicament, "trapped");
1782 if ((t = t_at(u.ux, u.uy)) != 0)
1783 Sprintf(eos(predicament), " in %s",
1784 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
1786 if (u.usteed) { /* not `Riding' here */
1787 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
1788 *buf = highc(*buf);
1789 enl_msg(buf, (anchored ? "are " : "is "),
1790 (anchored ? "were " : "was "), predicament, "");
1791 } else
1792 you_are(predicament, "");
1793 } /* (u.utrap) */
1794 if (u.uswallow) {
1795 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
1796 if (wizard)
1797 Sprintf(eos(buf), " (%u)", u.uswldtim);
1798 you_are(buf, "");
1799 } else if (u.ustuck) {
1800 Sprintf(buf, "%s %s",
1801 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
1802 a_monnam(u.ustuck));
1803 you_are(buf, "");
1805 if (Riding) {
1806 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
1808 if (saddle && saddle->cursed) {
1809 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
1810 simpleonames(saddle));
1811 you_are(buf, "");
1814 if (Wounded_legs) {
1815 /* when mounted, Wounded_legs applies to steed rather than to
1816 hero; we only report steed's wounded legs in wizard mode */
1817 if (u.usteed) { /* not `Riding' here */
1818 if (wizard && steedname) {
1819 Strcpy(buf, steedname);
1820 *buf = highc(*buf);
1821 enl_msg(buf, " has", " had", " wounded legs", "");
1823 } else {
1824 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
1825 you_have(buf, "");
1828 if (Glib) {
1829 Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
1830 you_have(buf, "");
1832 if (Fumbling) {
1833 if (magic || cause_known(FUMBLING))
1834 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
1836 if (Sleepy) {
1837 if (magic || cause_known(SLEEPY)) {
1838 Strcpy(buf, from_what(SLEEPY));
1839 if (wizard)
1840 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
1841 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
1844 /* hunger/nutrition */
1845 if (Hunger) {
1846 if (magic || cause_known(HUNGER))
1847 enl_msg(You_, "hunger", "hungered", " rapidly",
1848 from_what(HUNGER));
1850 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
1851 mungspaces(buf); /* strip trailing spaces */
1852 if (*buf) {
1853 *buf = lowc(*buf); /* override capitalization */
1854 if (!strcmp(buf, "weak"))
1855 Strcat(buf, " from severe hunger");
1856 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
1857 Strcat(buf, " due to starvation");
1858 you_are(buf, "");
1860 /* encumbrance */
1861 if ((cap = near_capacity()) > UNENCUMBERED) {
1862 const char *adj = "?_?"; /* (should always get overridden) */
1864 Strcpy(buf, enc_stat[cap]);
1865 *buf = lowc(*buf);
1866 switch (cap) {
1867 case SLT_ENCUMBER:
1868 adj = "slightly";
1869 break; /* burdened */
1870 case MOD_ENCUMBER:
1871 adj = "moderately";
1872 break; /* stressed */
1873 case HVY_ENCUMBER:
1874 adj = "very";
1875 break; /* strained */
1876 case EXT_ENCUMBER:
1877 adj = "extremely";
1878 break; /* overtaxed */
1879 case OVERLOADED:
1880 adj = "not possible";
1881 break;
1883 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
1884 (cap < OVERLOADED) ? " slowed" : "");
1885 you_are(buf, "");
1886 } else {
1887 /* last resort entry, guarantees Status section is non-empty
1888 (no longer needed for that purpose since weapon status added;
1889 still useful though) */
1890 you_are("unencumbered", "");
1892 /* report being weaponless; distinguish whether gloves are worn */
1893 if (!uwep) {
1894 you_are(uarmg ? "empty handed" /* gloves imply hands */
1895 : humanoid(youmonst.data)
1896 /* hands but no weapon and no gloves */
1897 ? "bare handed"
1898 /* alternate phrasing for paws or lack of hands */
1899 : "not wielding anything",
1900 "");
1901 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1902 } else if (u.twoweap) {
1903 you_are("wielding two weapons at once", "");
1904 /* report most weapons by their skill class (so a katana will be
1905 described as a long sword, for instance; mattock and hook are
1906 exceptions), or wielded non-weapon item by its object class */
1907 } else {
1908 const char *what = weapon_descr(uwep);
1910 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
1911 || !strcmpi(what, "venom"))
1912 Sprintf(buf, "wielding some %s", what);
1913 else
1914 Sprintf(buf, "wielding %s",
1915 (uwep->quan == 1L) ? an(what) : makeplural(what));
1916 you_are(buf, "");
1918 /* report 'nudity' */
1919 if (!uarm && !uarmu && !uarmc && !uarmg && !uarmf && !uarmh) {
1920 if (u.uroleplay.nudist)
1921 enl_msg(You_, "do", "did", " not wear any armor", "");
1922 else
1923 you_are("not wearing any armor", "");
1927 /* attributes: intrinsics and the like, other non-obvious capabilities */
1928 void
1929 attributes_enlightenment(unused_mode, final)
1930 int unused_mode UNUSED;
1931 int final;
1933 static NEARDATA const char if_surroundings_permitted[] =
1934 " if surroundings permitted";
1935 int ltmp, armpro;
1936 char buf[BUFSZ];
1939 * Attributes
1941 putstr(en_win, 0, "");
1942 putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
1944 if (u.uevent.uhand_of_elbereth) {
1945 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
1946 "the Envoy of Balance",
1947 "the Glory of Arioch" };
1948 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
1951 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
1952 if (u.ualign.record >= 20)
1953 you_are("piously aligned", "");
1954 else if (u.ualign.record > 13)
1955 you_are("devoutly aligned", "");
1956 else if (u.ualign.record > 8)
1957 you_are("fervently aligned", "");
1958 else if (u.ualign.record > 3)
1959 you_are("stridently aligned", "");
1960 else if (u.ualign.record == 3)
1961 you_are("aligned", "");
1962 else if (u.ualign.record > 0)
1963 you_are("haltingly aligned", "");
1964 else if (u.ualign.record == 0)
1965 you_are("nominally aligned", "");
1966 else if (u.ualign.record >= -3)
1967 you_have("strayed", "");
1968 else if (u.ualign.record >= -8)
1969 you_have("sinned", "");
1970 else
1971 you_have("transgressed", "");
1972 if (wizard) {
1973 Sprintf(buf, " %d", u.ualign.record);
1974 enl_msg("Your alignment ", "is", "was", buf, "");
1977 /*** Resistances to troubles ***/
1978 if (Invulnerable)
1979 you_are("invulnerable", from_what(INVULNERABLE));
1980 if (Antimagic)
1981 you_are("magic-protected", from_what(ANTIMAGIC));
1982 if (Fire_resistance)
1983 you_are("fire resistant", from_what(FIRE_RES));
1984 if (Cold_resistance)
1985 you_are("cold resistant", from_what(COLD_RES));
1986 if (Sleep_resistance)
1987 you_are("sleep resistant", from_what(SLEEP_RES));
1988 if (Disint_resistance)
1989 you_are("disintegration-resistant", from_what(DISINT_RES));
1990 if (Shock_resistance)
1991 you_are("shock resistant", from_what(SHOCK_RES));
1992 if (Poison_resistance)
1993 you_are("poison resistant", from_what(POISON_RES));
1994 if (Acid_resistance)
1995 you_are("acid resistant", from_what(ACID_RES));
1996 if (Drain_resistance)
1997 you_are("level-drain resistant", from_what(DRAIN_RES));
1998 if (Sick_resistance)
1999 you_are("immune to sickness", from_what(SICK_RES));
2000 if (Stone_resistance)
2001 you_are("petrification resistant", from_what(STONE_RES));
2002 if (Halluc_resistance)
2003 enl_msg(You_, "resist", "resisted", " hallucinations",
2004 from_what(HALLUC_RES));
2005 if (u.uedibility)
2006 you_can("recognize detrimental food", "");
2008 /*** Vision and senses ***/
2009 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2010 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2011 if (See_invisible) {
2012 if (!Blind)
2013 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2014 else
2015 enl_msg(You_, "will see", "would have seen",
2016 " invisible when not blind", from_what(SEE_INVIS));
2018 if (Blind_telepat)
2019 you_are("telepathic", from_what(TELEPAT));
2020 if (Warning)
2021 you_are("warned", from_what(WARNING));
2022 if (Warn_of_mon && context.warntype.obj) {
2023 Sprintf(buf, "aware of the presence of %s",
2024 (context.warntype.obj & M2_ORC) ? "orcs"
2025 : (context.warntype.obj & M2_ELF) ? "elves"
2026 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2027 you_are(buf, from_what(WARN_OF_MON));
2029 if (Warn_of_mon && context.warntype.polyd) {
2030 Sprintf(buf, "aware of the presence of %s",
2031 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2032 == (M2_HUMAN | M2_ELF))
2033 ? "humans and elves"
2034 : (context.warntype.polyd & M2_HUMAN)
2035 ? "humans"
2036 : (context.warntype.polyd & M2_ELF)
2037 ? "elves"
2038 : (context.warntype.polyd & M2_ORC)
2039 ? "orcs"
2040 : (context.warntype.polyd & M2_DEMON)
2041 ? "demons"
2042 : "certain monsters");
2043 you_are(buf, "");
2045 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2046 Sprintf(buf, "aware of the presence of %s",
2047 makeplural(mons[context.warntype.speciesidx].mname));
2048 you_are(buf, from_what(WARN_OF_MON));
2050 if (Undead_warning)
2051 you_are("warned of undead", from_what(WARN_UNDEAD));
2052 if (Searching)
2053 you_have("automatic searching", from_what(SEARCHING));
2054 if (Clairvoyant)
2055 you_are("clairvoyant", from_what(CLAIRVOYANT));
2056 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2057 Strcpy(buf, from_what(-CLAIRVOYANT));
2058 if (!strncmp(buf, " because of ", 12))
2059 /* overwrite substring; strncpy doesn't add terminator */
2060 (void) strncpy(buf, " if not for ", 12);
2061 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2063 if (Infravision)
2064 you_have("infravision", from_what(INFRAVISION));
2065 if (Detect_monsters)
2066 you_are("sensing the presence of monsters", "");
2067 if (u.umconf)
2068 you_are("going to confuse monsters", "");
2070 /*** Appearance and behavior ***/
2071 if (Adornment) {
2072 int adorn = 0;
2074 if (uleft && uleft->otyp == RIN_ADORNMENT)
2075 adorn += uleft->spe;
2076 if (uright && uright->otyp == RIN_ADORNMENT)
2077 adorn += uright->spe;
2078 /* the sum might be 0 (+0 ring or two which negate each other);
2079 that yields "you are charismatic" (which isn't pointless
2080 because it potentially impacts seduction attacks) */
2081 Sprintf(buf, "%scharismatic",
2082 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2083 you_are(buf, from_what(ADORNED));
2085 if (Invisible)
2086 you_are("invisible", from_what(INVIS));
2087 else if (Invis)
2088 you_are("invisible to others", from_what(INVIS));
2089 /* ordinarily "visible" is redundant; this is a special case for
2090 the situation when invisibility would be an expected attribute */
2091 else if ((HInvis || EInvis) && BInvis)
2092 you_are("visible", from_what(-INVIS));
2093 if (Displaced)
2094 you_are("displaced", from_what(DISPLACED));
2095 if (Stealth)
2096 you_are("stealthy", from_what(STEALTH));
2097 if (Aggravate_monster)
2098 enl_msg("You aggravate", "", "d", " monsters",
2099 from_what(AGGRAVATE_MONSTER));
2100 if (Conflict)
2101 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2103 /*** Transportation ***/
2104 if (Jumping)
2105 you_can("jump", from_what(JUMPING));
2106 if (Teleportation)
2107 you_can("teleport", from_what(TELEPORT));
2108 if (Teleport_control)
2109 you_have("teleport control", from_what(TELEPORT_CONTROL));
2110 /* actively levitating handled earlier as a status condition */
2111 if (BLevitation) { /* levitation is blocked */
2112 long save_BLev = BLevitation;
2114 BLevitation = 0L;
2115 if (Levitation)
2116 enl_msg(You_, "would levitate", "would have levitated",
2117 if_surroundings_permitted, "");
2118 BLevitation = save_BLev;
2120 /* actively flying handled earlier as a status condition */
2121 if (BFlying) { /* flight is blocked */
2122 long save_BFly = BFlying;
2124 BFlying = 0L;
2125 if (Flying)
2126 enl_msg(You_, "would fly", "would have flown",
2127 Levitation
2128 ? "if you weren't levitating"
2129 : (save_BFly == FROMOUTSIDE)
2130 ? if_surroundings_permitted
2131 /* both surroundings and [latent] levitation */
2132 : " if circumstances permitted",
2133 "");
2134 BFlying = save_BFly;
2136 /* actively walking on water handled earlier as a status condition */
2137 if (Wwalking && !walking_on_water())
2138 you_can("walk on water", from_what(WWALKING));
2139 /* actively swimming (in water but not under it) handled earlier */
2140 if (Swimming && (Underwater || !u.uinwater))
2141 you_can("swim", from_what(SWIMMING));
2142 if (Breathless)
2143 you_can("survive without air", from_what(MAGICAL_BREATHING));
2144 else if (Amphibious)
2145 you_can("breathe water", from_what(MAGICAL_BREATHING));
2146 if (Passes_walls)
2147 you_can("walk through walls", from_what(PASSES_WALLS));
2149 /*** Physical attributes ***/
2150 if (Regeneration)
2151 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2152 if (Slow_digestion)
2153 you_have("slower digestion", from_what(SLOW_DIGESTION));
2154 if (u.uhitinc)
2155 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2156 if (u.udaminc)
2157 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2158 if (u.uspellprot || Protection) {
2159 int prot = 0;
2161 if (uleft && uleft->otyp == RIN_PROTECTION)
2162 prot += uleft->spe;
2163 if (uright && uright->otyp == RIN_PROTECTION)
2164 prot += uright->spe;
2165 if (HProtection & INTRINSIC)
2166 prot += u.ublessed;
2167 prot += u.uspellprot;
2168 if (prot)
2169 you_have(enlght_combatinc("defense", prot, final, buf), "");
2171 if ((armpro = magic_negation(&youmonst)) > 0) {
2172 /* magic cancellation factor, conferred by worn armor */
2173 static const char *const mc_types[] = {
2174 "" /*ordinary*/, "warded", "guarded", "protected",
2176 /* sanity check */
2177 if (armpro >= SIZE(mc_types))
2178 armpro = SIZE(mc_types) - 1;
2179 you_are(mc_types[armpro], "");
2181 if (Half_physical_damage)
2182 enlght_halfdmg(HALF_PHDAM, final);
2183 if (Half_spell_damage)
2184 enlght_halfdmg(HALF_SPDAM, final);
2185 /* polymorph and other shape change */
2186 if (Protection_from_shape_changers)
2187 you_are("protected from shape changers",
2188 from_what(PROT_FROM_SHAPE_CHANGERS));
2189 if (Unchanging) {
2190 const char *what = 0;
2192 if (!Upolyd) /* Upolyd handled below after current form */
2193 you_can("not change from your current form",
2194 from_what(UNCHANGING));
2195 /* blocked shape changes */
2196 if (Polymorph)
2197 what = !final ? "polymorph" : "have polymorphed";
2198 else if (u.ulycn >= LOW_PM)
2199 what = !final ? "change shape" : "have changed shape";
2200 if (what) {
2201 Sprintf(buf, "would %s periodically", what);
2202 /* omit from_what(UNCHANGING); too verbose */
2203 enl_msg(You_, buf, buf, " if not locked into your current form",
2204 "");
2206 } else if (Polymorph) {
2207 you_are("polymorphing periodically", from_what(POLYMORPH));
2209 if (Polymorph_control)
2210 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2211 if (Upolyd && u.umonnum != u.ulycn) {
2212 /* foreign shape (except were-form which is handled below) */
2213 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2214 if (wizard)
2215 Sprintf(eos(buf), " (%d)", u.mtimedone);
2216 you_are(buf, "");
2218 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2219 you_can("lay eggs", "");
2220 if (u.ulycn >= LOW_PM) {
2221 /* "you are a werecreature [in beast form]" */
2222 Strcpy(buf, an(mons[u.ulycn].mname));
2223 if (u.umonnum == u.ulycn) {
2224 Strcat(buf, " in beast form");
2225 if (wizard)
2226 Sprintf(eos(buf), " (%d)", u.mtimedone);
2228 you_are(buf, "");
2230 if (Unchanging && Upolyd) /* !Upolyd handled above */
2231 you_can("not change from your current form", from_what(UNCHANGING));
2232 if (Hate_silver)
2233 you_are("harmed by silver", "");
2234 /* movement and non-armor-based protection */
2235 if (Fast)
2236 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2237 if (Reflecting)
2238 you_have("reflection", from_what(REFLECTING));
2239 if (Free_action)
2240 you_have("free action", from_what(FREE_ACTION));
2241 if (Fixed_abil)
2242 you_have("fixed abilities", from_what(FIXED_ABIL));
2243 if (Lifesaved)
2244 enl_msg("Your life ", "will be", "would have been", " saved", "");
2246 /*** Miscellany ***/
2247 if (Luck) {
2248 ltmp = abs((int) Luck);
2249 Sprintf(buf, "%s%slucky",
2250 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2251 Luck < 0 ? "un" : "");
2252 if (wizard)
2253 Sprintf(eos(buf), " (%d)", Luck);
2254 you_are(buf, "");
2255 } else if (wizard)
2256 enl_msg("Your luck ", "is", "was", " zero", "");
2257 if (u.moreluck > 0)
2258 you_have("extra luck", "");
2259 else if (u.moreluck < 0)
2260 you_have("reduced luck", "");
2261 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2262 ltmp = stone_luck(0);
2263 if (ltmp <= 0)
2264 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2265 if (ltmp >= 0)
2266 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2269 if (u.ugangr) {
2270 Sprintf(buf, " %sangry with you",
2271 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2272 if (wizard)
2273 Sprintf(eos(buf), " (%d)", u.ugangr);
2274 enl_msg(u_gname(), " is", " was", buf, "");
2275 } else {
2277 * We need to suppress this when the game is over, because death
2278 * can change the value calculated by can_pray(), potentially
2279 * resulting in a false claim that you could have prayed safely.
2281 if (!final) {
2282 #if 0
2283 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2284 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2285 final ? "have " : "", final ? "ed" : "");
2286 #else
2287 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2288 #endif
2289 if (wizard)
2290 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2291 you_can(buf, "");
2295 #ifdef DEBUG
2296 /* named fruit debugging (doesn't really belong here...); to enable,
2297 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2298 if (wizard && explicitdebug("fruit")) {
2299 int fcount = 0;
2300 struct fruit *f;
2301 char buf2[BUFSZ];
2303 for (f = ffruit; f; f = f->nextf) {
2304 Sprintf(buf, "Fruit %d ", ++fcount);
2305 Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
2306 enl_msg(buf, "is ", "was ", buf2, "");
2308 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
2309 Sprintf(buf, "%d", flags.made_fruit);
2310 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
2312 #endif
2315 const char *p;
2317 buf[0] = '\0';
2318 if (final < 2) { /* still in progress, or quit/escaped/ascended */
2319 p = "survived after being killed ";
2320 switch (u.umortality) {
2321 case 0:
2322 p = !final ? (char *) 0 : "survived";
2323 break;
2324 case 1:
2325 Strcpy(buf, "once");
2326 break;
2327 case 2:
2328 Strcpy(buf, "twice");
2329 break;
2330 case 3:
2331 Strcpy(buf, "thrice");
2332 break;
2333 default:
2334 Sprintf(buf, "%d times", u.umortality);
2335 break;
2337 } else { /* game ended in character's death */
2338 p = "are dead";
2339 switch (u.umortality) {
2340 case 0:
2341 impossible("dead without dying?");
2342 case 1:
2343 break; /* just "are dead" */
2344 default:
2345 Sprintf(buf, " (%d%s time!)", u.umortality,
2346 ordin(u.umortality));
2347 break;
2350 if (p)
2351 enl_msg(You_, "have been killed ", p, buf, "");
2355 #if 0 /* no longer used */
2356 STATIC_DCL boolean NDECL(minimal_enlightenment);
2359 * Courtesy function for non-debug, non-explorer mode players
2360 * to help refresh them about who/what they are.
2361 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2363 STATIC_OVL boolean
2364 minimal_enlightenment()
2366 winid tmpwin;
2367 menu_item *selected;
2368 anything any;
2369 int genidx, n;
2370 char buf[BUFSZ], buf2[BUFSZ];
2371 static const char untabbed_fmtstr[] = "%-15s: %-12s";
2372 static const char untabbed_deity_fmtstr[] = "%-17s%s";
2373 static const char tabbed_fmtstr[] = "%s:\t%-12s";
2374 static const char tabbed_deity_fmtstr[] = "%s\t%s";
2375 static const char *fmtstr;
2376 static const char *deity_fmtstr;
2378 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
2379 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
2380 : untabbed_deity_fmtstr;
2381 any = zeroany;
2382 buf[0] = buf2[0] = '\0';
2383 tmpwin = create_nhwindow(NHW_MENU);
2384 start_menu(tmpwin);
2385 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2386 "Starting", FALSE);
2388 /* Starting name, race, role, gender */
2389 Sprintf(buf, fmtstr, "name", plname);
2390 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2391 Sprintf(buf, fmtstr, "race", urace.noun);
2392 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2393 Sprintf(buf, fmtstr, "role",
2394 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
2395 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2396 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
2397 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2399 /* Starting alignment */
2400 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
2401 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2403 /* Current name, race, role, gender */
2404 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2405 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2406 "Current", FALSE);
2407 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
2408 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2409 if (Upolyd) {
2410 Sprintf(buf, fmtstr, "role (base)",
2411 (u.mfemale && urole.name.f) ? urole.name.f
2412 : urole.name.m);
2413 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2414 } else {
2415 Sprintf(buf, fmtstr, "role",
2416 (flags.female && urole.name.f) ? urole.name.f
2417 : urole.name.m);
2418 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2420 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2421 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
2422 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
2423 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2424 if (Upolyd && (int) u.mfemale != genidx) {
2425 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
2426 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2429 /* Current alignment */
2430 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
2431 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2433 /* Deity list */
2434 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2435 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2436 "Deities", FALSE);
2437 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
2438 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2439 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
2440 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
2441 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
2442 Sprintf(buf, fmtstr, "Chaotic", buf2);
2443 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2445 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
2446 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2447 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
2448 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
2449 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
2450 Sprintf(buf, fmtstr, "Neutral", buf2);
2451 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2453 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
2454 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2455 && u.ualign.type == A_LAWFUL) ? " (s,c)"
2456 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
2457 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
2458 Sprintf(buf, fmtstr, "Lawful", buf2);
2459 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2461 end_menu(tmpwin, "Base Attributes");
2462 n = select_menu(tmpwin, PICK_NONE, &selected);
2463 destroy_nhwindow(tmpwin);
2464 return (boolean) (n != -1);
2466 #endif /*0*/
2468 /* ^X command */
2469 STATIC_PTR int
2470 doattributes(VOID_ARGS)
2472 int mode = BASICENLIGHTENMENT;
2474 /* show more--as if final disclosure--for wizard and explore modes */
2475 if (wizard || discover)
2476 mode |= MAGICENLIGHTENMENT;
2478 enlightenment(mode, ENL_GAMEINPROGRESS);
2479 return 0;
2482 void
2483 youhiding(via_enlghtmt, msgflag)
2484 boolean via_enlghtmt; /* englightment line vs topl message */
2485 int msgflag; /* for variant message phrasing */
2487 char *bp, buf[BUFSZ];
2489 Strcpy(buf, "hiding");
2490 if (youmonst.m_ap_type != M_AP_NOTHING) {
2491 /* mimic; hero is only able to mimic a strange object or gold
2492 or hallucinatory alternative to gold, so we skip the details
2493 for the hypothetical furniture and monster cases */
2494 bp = eos(strcpy(buf, "mimicking"));
2495 if (youmonst.m_ap_type == M_AP_OBJECT) {
2496 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
2497 } else if (youmonst.m_ap_type == M_AP_FURNITURE) {
2498 Strcpy(bp, " something");
2499 } else if (youmonst.m_ap_type == M_AP_MONSTER) {
2500 Strcpy(bp, " someone");
2501 } else {
2502 ; /* something unexpected; leave 'buf' as-is */
2504 } else if (u.uundetected) {
2505 bp = eos(buf); /* points past "hiding" */
2506 if (youmonst.data->mlet == S_EEL) {
2507 if (is_pool(u.ux, u.uy))
2508 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
2509 } else if (hides_under(youmonst.data)) {
2510 struct obj *o = level.objects[u.ux][u.uy];
2512 if (o)
2513 Sprintf(bp, " underneath %s", ansimpleoname(o));
2514 } else if (is_clinger(youmonst.data) || Flying) {
2515 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2516 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
2517 } else {
2518 /* on floor; is_hider() but otherwise not special: 'trapper' */
2519 if (u.utrap && u.utraptype == TT_PIT) {
2520 struct trap *t = t_at(u.ux, u.uy);
2522 Sprintf(bp, " in a %spit",
2523 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
2524 } else
2525 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
2527 } else {
2528 ; /* shouldn't happen; will result in generic "you are hiding" */
2531 if (via_enlghtmt) {
2532 int final = msgflag; /* 'final' is used by you_are() macro */
2534 you_are(buf, "");
2535 } else {
2536 /* for dohide(), when player uses '#monster' command */
2537 You("are %s %s.", msgflag ? "already" : "now", buf);
2541 /* KMH, #conduct
2542 * (shares enlightenment's tense handling)
2544 STATIC_PTR int
2545 doconduct(VOID_ARGS)
2547 show_conduct(0);
2548 return 0;
2551 void
2552 show_conduct(final)
2553 int final;
2555 char buf[BUFSZ];
2556 int ngenocided;
2558 /* Create the conduct window */
2559 en_win = create_nhwindow(NHW_MENU);
2560 putstr(en_win, 0, "Voluntary challenges:");
2562 if (u.uroleplay.blind)
2563 you_have_been("blind from birth");
2564 if (u.uroleplay.nudist)
2565 you_have_been("faithfully nudist");
2567 if (!u.uconduct.food)
2568 enl_msg(You_, "have gone", "went", " without food", "");
2569 /* But beverages are okay */
2570 else if (!u.uconduct.unvegan)
2571 you_have_X("followed a strict vegan diet");
2572 else if (!u.uconduct.unvegetarian)
2573 you_have_been("vegetarian");
2575 if (!u.uconduct.gnostic)
2576 you_have_been("an atheist");
2578 if (!u.uconduct.weaphit) {
2579 you_have_never("hit with a wielded weapon");
2580 } else if (wizard) {
2581 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
2582 plur(u.uconduct.weaphit));
2583 you_have_X(buf);
2585 if (!u.uconduct.killer)
2586 you_have_been("a pacifist");
2588 if (!u.uconduct.literate) {
2589 you_have_been("illiterate");
2590 } else if (wizard) {
2591 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
2592 plur(u.uconduct.literate));
2593 you_have_X(buf);
2596 ngenocided = num_genocides();
2597 if (ngenocided == 0) {
2598 you_have_never("genocided any monsters");
2599 } else {
2600 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
2601 plur(ngenocided), plur(ngenocided));
2602 you_have_X(buf);
2605 if (!u.uconduct.polypiles) {
2606 you_have_never("polymorphed an object");
2607 } else if (wizard) {
2608 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
2609 plur(u.uconduct.polypiles));
2610 you_have_X(buf);
2613 if (!u.uconduct.polyselfs) {
2614 you_have_never("changed form");
2615 } else if (wizard) {
2616 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
2617 plur(u.uconduct.polyselfs));
2618 you_have_X(buf);
2621 if (!u.uconduct.wishes) {
2622 you_have_X("used no wishes");
2623 } else {
2624 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
2625 (u.uconduct.wishes > 1L) ? "es" : "");
2626 you_have_X(buf);
2628 if (!u.uconduct.wisharti)
2629 enl_msg(You_, "have not wished", "did not wish",
2630 " for any artifacts", "");
2633 /* Pop up the window and wait for a key */
2634 display_nhwindow(en_win, TRUE);
2635 destroy_nhwindow(en_win);
2636 en_win = WIN_ERR;
2639 #ifndef M
2640 #ifndef NHSTDC
2641 #define M(c) (0x80 | (c))
2642 #else
2643 #define M(c) ((c) -128)
2644 #endif /* NHSTDC */
2645 #endif
2646 #ifndef C
2647 #define C(c) (0x1f & (c))
2648 #endif
2650 static const struct func_tab cmdlist[] = {
2651 { C('d'), FALSE, dokick }, /* "D" is for door!...? Msg is in dokick.c */
2652 { C('e'), TRUE, wiz_detect },
2653 { C('f'), TRUE, wiz_map },
2654 { C('g'), TRUE, wiz_genesis },
2655 { C('i'), TRUE, wiz_identify },
2656 { C('l'), TRUE, doredraw }, /* if number_pad is set */
2657 { C('n'), TRUE, donamelevel }, /* if number_pad is set */
2658 { C('o'), TRUE, dooverview_or_wiz_where }, /* depends on wizard status */
2659 { C('p'), TRUE, doprev_message },
2660 { C('r'), TRUE, doredraw },
2661 { C('t'), TRUE, dotele },
2662 { C('v'), TRUE, wiz_level_tele },
2663 { C('w'), TRUE, wiz_wish },
2664 { C('x'), TRUE, doattributes },
2665 { C('z'), TRUE, dosuspend_core },
2666 { 'a', FALSE, doapply },
2667 { 'A', FALSE, doddoremarm },
2668 { M('a'), TRUE, doorganize },
2669 { M('A'), TRUE, donamelevel }, /* #annotate */
2670 /* 'b', 'B' : go sw */
2671 { 'c', FALSE, doclose },
2672 { 'C', TRUE, docallcmd },
2673 { M('c'), TRUE, dotalk },
2674 { M('C'), TRUE, doconduct }, /* #conduct */
2675 { 'd', FALSE, dodrop },
2676 { 'D', FALSE, doddrop },
2677 { M('d'), FALSE, dodip },
2678 { 'e', FALSE, doeat },
2679 { 'E', FALSE, doengrave },
2680 { M('e'), TRUE, enhance_weapon_skill },
2681 { 'f', FALSE, dofire },
2682 /* 'F' : fight (one time) */
2683 { M('f'), FALSE, doforce },
2684 /* 'g', 'G' : multiple go */
2685 /* 'h', 'H' : go west */
2686 { 'h', TRUE, dohelp }, /* if number_pad is set */
2687 { 'i', TRUE, ddoinv },
2688 { 'I', TRUE, dotypeinv }, /* Robert Viduya */
2689 { M('i'), TRUE, doinvoke },
2690 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2691 { 'j', FALSE, dojump }, /* if number_pad is on */
2692 { M('j'), FALSE, dojump },
2693 { 'k', FALSE, dokick }, /* if number_pad is on */
2694 { 'l', FALSE, doloot }, /* if number_pad is on */
2695 { M('l'), FALSE, doloot },
2696 /* 'n' prefixes a count if number_pad is on */
2697 { M('m'), TRUE, domonability },
2698 { 'N', TRUE, docallcmd }, /* if number_pad is on */
2699 { M('n'), TRUE, docallcmd },
2700 { M('N'), TRUE, docallcmd },
2701 { 'o', FALSE, doopen },
2702 { 'O', TRUE, doset },
2703 { M('o'), FALSE, dosacrifice },
2704 { M('O'), TRUE, dooverview }, /* #overview */
2705 { 'p', FALSE, dopay },
2706 { 'P', FALSE, doputon },
2707 { M('p'), TRUE, dopray },
2708 { 'q', FALSE, dodrink },
2709 { 'Q', FALSE, dowieldquiver },
2710 { M('q'), TRUE, done2 },
2711 { 'r', FALSE, doread },
2712 { 'R', FALSE, doremring },
2713 { M('r'), FALSE, dorub },
2714 { M('R'), FALSE, doride }, /* #ride */
2715 { 's', TRUE, dosearch, "searching" },
2716 { 'S', TRUE, dosave },
2717 { M('s'), FALSE, dosit },
2718 { 't', FALSE, dothrow },
2719 { 'T', FALSE, dotakeoff },
2720 { M('t'), TRUE, doturn },
2721 { M('T'), FALSE, dotip }, /* #tip */
2722 /* 'u', 'U' : go ne */
2723 { 'u', FALSE, dountrap }, /* if number_pad is on */
2724 { M('u'), FALSE, dountrap },
2725 { 'v', TRUE, doversion },
2726 { 'V', TRUE, dohistory },
2727 { M('v'), TRUE, doextversion },
2728 { 'w', FALSE, dowield },
2729 { 'W', FALSE, dowear },
2730 { M('w'), FALSE, dowipe },
2731 { 'x', FALSE, doswapweapon },
2732 { 'X', FALSE, dotwoweapon },
2733 /* 'y', 'Y' : go nw */
2734 { 'z', FALSE, dozap },
2735 { 'Z', TRUE, docast },
2736 { '<', FALSE, doup },
2737 { '>', FALSE, dodown },
2738 { '/', TRUE, dowhatis },
2739 { '&', TRUE, dowhatdoes },
2740 { '?', TRUE, dohelp },
2741 { M('?'), TRUE, doextlist },
2742 #ifdef SHELL
2743 { '!', TRUE, dosh },
2744 #endif
2745 { '.', TRUE, donull, "waiting" },
2746 { ' ', TRUE, donull, "waiting" },
2747 { ',', FALSE, dopickup },
2748 { ':', TRUE, dolook },
2749 { ';', TRUE, doquickwhatis },
2750 { '^', TRUE, doidtrap },
2751 { '\\', TRUE, dodiscovered }, /* Robert Viduya */
2752 { '`', TRUE, doclassdisco },
2753 { '@', TRUE, dotogglepickup },
2754 { M('2'), FALSE, dotwoweapon },
2755 { WEAPON_SYM, TRUE, doprwep },
2756 { ARMOR_SYM, TRUE, doprarm },
2757 { RING_SYM, TRUE, doprring },
2758 { AMULET_SYM, TRUE, dopramulet },
2759 { TOOL_SYM, TRUE, doprtool },
2760 { '*', TRUE, doprinuse }, /* inventory of all equipment in use */
2761 { GOLD_SYM, TRUE, doprgold },
2762 { SPBOOK_SYM, TRUE, dovspell }, /* Mike Stephenson */
2763 { '#', TRUE, doextcmd },
2764 { '_', TRUE, dotravel },
2765 { 0, 0, 0, 0 }
2768 struct ext_func_tab extcmdlist[] = {
2769 { "adjust", "adjust inventory letters", doorganize, TRUE },
2770 { "annotate", "name current level", donamelevel, TRUE },
2771 { "chat", "talk to someone", dotalk, TRUE }, /* converse? */
2772 { "conduct", "list voluntary challenges you have maintained", doconduct,
2773 TRUE },
2774 { "dip", "dip an object into something", dodip, FALSE },
2775 { "enhance", "advance or check weapon and spell skills",
2776 enhance_weapon_skill, TRUE },
2777 { "exploremode", "enter explore mode", enter_explore_mode, TRUE },
2778 { "force", "force a lock", doforce, FALSE },
2779 { "invoke", "invoke an object's powers", doinvoke, TRUE },
2780 { "jump", "jump to a location", dojump, FALSE },
2781 { "kick", "kick something", dokick, FALSE },
2782 { "loot", "loot a box on the floor", doloot, FALSE },
2783 { "monster", "use a monster's special ability", domonability, TRUE },
2784 { "name", "name a monster or an object", docallcmd, TRUE },
2785 { "offer", "offer a sacrifice to the gods", dosacrifice, FALSE },
2786 { "overview", "show an overview of the dungeon", dooverview, TRUE },
2787 { "pray", "pray to the gods for help", dopray, TRUE },
2788 { "quit", "exit without saving current game", done2, TRUE },
2789 { "ride", "ride (or stop riding) a monster", doride, FALSE },
2790 { "rub", "rub a lamp or a stone", dorub, FALSE },
2791 { "sit", "sit down", dosit, FALSE },
2792 { "terrain", "show map without obstructions", doterrain, TRUE },
2793 { "tip", "empty a container", dotip, FALSE },
2794 { "turn", "turn undead", doturn, TRUE },
2795 { "twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE },
2796 { "untrap", "untrap something", dountrap, FALSE },
2797 { "version", "list compile time options for this version of NetHack",
2798 doextversion, TRUE },
2799 { "wipe", "wipe off your face", dowipe, FALSE },
2800 { "?", "get this list of extended commands", doextlist, TRUE },
2802 * There must be a blank entry here for every entry in the table
2803 * below.
2805 { (char *) 0, (char *) 0, donull, TRUE }, /* levelchange */
2806 { (char *) 0, (char *) 0, donull, TRUE }, /* lightsources */
2807 #ifdef DEBUG_MIGRATING_MONS
2808 { (char *) 0, (char *) 0, donull, TRUE }, /* migratemons */
2809 #endif
2810 { (char *) 0, (char *) 0, donull, TRUE }, /* monpolycontrol */
2811 { (char *) 0, (char *) 0, donull, TRUE }, /* panic */
2812 { (char *) 0, (char *) 0, donull, TRUE }, /* polyself */
2813 #ifdef PORT_DEBUG
2814 { (char *) 0, (char *) 0, donull, TRUE }, /* portdebug */
2815 #endif
2816 { (char *) 0, (char *) 0, donull, TRUE }, /* seenv */
2817 { (char *) 0, (char *) 0, donull, TRUE }, /* stats */
2818 { (char *) 0, (char *) 0, donull, TRUE }, /* timeout */
2819 { (char *) 0, (char *) 0, donull, TRUE }, /* vanquished */
2820 { (char *) 0, (char *) 0, donull, TRUE }, /* vision */
2821 { (char *) 0, (char *) 0, donull, TRUE }, /* wizsmell */
2822 { (char *) 0, (char *) 0, donull, TRUE }, /* wizintrinsic */
2823 #ifdef DEBUG
2824 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_traveldisplay */
2825 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_bury */
2826 #endif
2827 { (char *) 0, (char *) 0, donull, TRUE }, /* wizrumorcheck */
2828 { (char *) 0, (char *) 0, donull, TRUE }, /* wmode */
2829 { (char *) 0, (char *) 0, donull, TRUE } /* sentinel */
2832 /* there must be a placeholder in the table above for every entry here */
2833 static const struct ext_func_tab debug_extcmdlist[] = {
2834 { "levelchange", "change experience level", wiz_level_change, TRUE },
2835 { "lightsources", "show mobile light sources", wiz_light_sources, TRUE },
2836 #ifdef DEBUG_MIGRATING_MONS
2837 { "migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE },
2838 #endif
2839 { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol,
2840 TRUE },
2841 { "panic", "test panic routine (fatal to game)", wiz_panic, TRUE },
2842 { "polyself", "polymorph self", wiz_polyself, TRUE },
2843 #ifdef PORT_DEBUG
2844 { "portdebug", "wizard port debug command", wiz_port_debug, TRUE },
2845 #endif
2846 { "seenv", "show seen vectors", wiz_show_seenv, TRUE },
2847 { "stats", "show memory statistics", wiz_show_stats, TRUE },
2848 { "timeout", "look at timeout queue", wiz_timeout_queue, TRUE },
2849 { "vanquished", "list vanquished monsters", dovanquished, TRUE },
2850 { "vision", "show vision array", wiz_show_vision, TRUE },
2851 { "wizsmell", "smell monster", wiz_smell, TRUE },
2852 { "wizintrinsic", "set intrinsic", wiz_intrinsic, TRUE },
2853 #ifdef DEBUG
2854 { "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2855 wiz_debug_cmd_traveldisplay, TRUE },
2856 { "wizdebug_bury", "wizard debug: bury objs under and around you",
2857 wiz_debug_cmd_bury, TRUE },
2858 #endif
2859 { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, TRUE },
2860 { "wmode", "show wall modes", wiz_show_wmodes, TRUE },
2861 { (char *) 0, (char *) 0, donull, TRUE }
2865 * Insert debug commands into the extended command list. This function
2866 * assumes that the last entry will be the help entry.
2868 * You must add entries in ext_func_tab every time you add one to the
2869 * debug_extcmdlist().
2871 void
2872 add_debug_extended_commands()
2874 int i, j, k, n;
2876 /* count the # of help entries */
2877 for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++)
2880 for (i = 0; debug_extcmdlist[i].ef_txt; i++) {
2881 /* need enough room for "?" entry plus terminator */
2882 if (n + 2 >= SIZE(extcmdlist))
2883 panic("Too many debugging commands!");
2884 for (j = 0; j < n; j++)
2885 if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0)
2886 break;
2888 /* insert i'th debug entry into extcmdlist[j], pushing down */
2889 for (k = n; k >= j; --k)
2890 extcmdlist[k + 1] = extcmdlist[k];
2891 extcmdlist[j] = debug_extcmdlist[i];
2892 n++; /* now an extra entry */
2896 STATIC_OVL char
2897 cmd_from_func(fn)
2898 int NDECL((*fn));
2900 int i;
2901 for (i = 0; i < SIZE(cmdlist); ++i)
2902 if (cmdlist[i].f_funct == fn)
2903 return cmdlist[i].f_char;
2904 return 0;
2907 static const char template[] = "%-18s %4ld %6ld";
2908 static const char count_str[] = " count bytes";
2909 static const char separator[] = "------------------ ----- ------";
2911 STATIC_OVL int
2912 size_obj(otmp)
2913 struct obj *otmp;
2915 int sz = (int) sizeof(struct obj);
2917 if (otmp->oextra) {
2918 sz += (int) sizeof(struct oextra);
2919 if (ONAME(otmp))
2920 sz += (int) strlen(ONAME(otmp)) + 1;
2921 if (OMONST(otmp))
2922 sz += (int) sizeof(struct monst);
2923 if (OMID(otmp))
2924 sz += (int) sizeof(unsigned);
2925 if (OLONG(otmp))
2926 sz += (int) sizeof(long);
2927 if (OMAILCMD(otmp))
2928 sz += (int) strlen(OMAILCMD(otmp)) + 1;
2930 return sz;
2933 STATIC_OVL void
2934 count_obj(chain, total_count, total_size, top, recurse)
2935 struct obj *chain;
2936 long *total_count;
2937 long *total_size;
2938 boolean top;
2939 boolean recurse;
2941 long count, size;
2942 struct obj *obj;
2944 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
2945 if (top) {
2946 count++;
2947 size += size_obj(obj);
2949 if (recurse && obj->cobj)
2950 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
2952 *total_count += count;
2953 *total_size += size;
2956 STATIC_OVL void
2957 obj_chain(win, src, chain, total_count, total_size)
2958 winid win;
2959 const char *src;
2960 struct obj *chain;
2961 long *total_count;
2962 long *total_size;
2964 char buf[BUFSZ];
2965 long count = 0, size = 0;
2967 count_obj(chain, &count, &size, TRUE, FALSE);
2968 *total_count += count;
2969 *total_size += size;
2970 Sprintf(buf, template, src, count, size);
2971 putstr(win, 0, buf);
2974 STATIC_OVL void
2975 mon_invent_chain(win, src, chain, total_count, total_size)
2976 winid win;
2977 const char *src;
2978 struct monst *chain;
2979 long *total_count;
2980 long *total_size;
2982 char buf[BUFSZ];
2983 long count = 0, size = 0;
2984 struct monst *mon;
2986 for (mon = chain; mon; mon = mon->nmon)
2987 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
2988 *total_count += count;
2989 *total_size += size;
2990 Sprintf(buf, template, src, count, size);
2991 putstr(win, 0, buf);
2994 STATIC_OVL void
2995 contained(win, src, total_count, total_size)
2996 winid win;
2997 const char *src;
2998 long *total_count;
2999 long *total_size;
3001 char buf[BUFSZ];
3002 long count = 0, size = 0;
3003 struct monst *mon;
3005 count_obj(invent, &count, &size, FALSE, TRUE);
3006 count_obj(fobj, &count, &size, FALSE, TRUE);
3007 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3008 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3009 /* DEADMONSTER check not required in this loop since they have no
3010 * inventory */
3011 for (mon = fmon; mon; mon = mon->nmon)
3012 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3013 for (mon = migrating_mons; mon; mon = mon->nmon)
3014 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3016 *total_count += count;
3017 *total_size += size;
3019 Sprintf(buf, template, src, count, size);
3020 putstr(win, 0, buf);
3023 STATIC_OVL int
3024 size_monst(mtmp)
3025 struct monst *mtmp;
3027 int sz = (int) sizeof(struct monst);
3029 if (mtmp->mextra) {
3030 sz += (int) sizeof(struct mextra);
3031 if (MNAME(mtmp))
3032 sz += (int) strlen(MNAME(mtmp)) + 1;
3033 if (EGD(mtmp))
3034 sz += (int) sizeof(struct egd);
3035 if (EPRI(mtmp))
3036 sz += (int) sizeof(struct epri);
3037 if (ESHK(mtmp))
3038 sz += (int) sizeof(struct eshk);
3039 if (EMIN(mtmp))
3040 sz += (int) sizeof(struct emin);
3041 if (EDOG(mtmp))
3042 sz += (int) sizeof(struct edog);
3043 /* mextra->mcorpsenm doesn't point to more memory */
3045 return sz;
3048 STATIC_OVL void
3049 mon_chain(win, src, chain, total_count, total_size)
3050 winid win;
3051 const char *src;
3052 struct monst *chain;
3053 long *total_count;
3054 long *total_size;
3056 char buf[BUFSZ];
3057 long count, size;
3058 struct monst *mon;
3060 for (count = size = 0, mon = chain; mon; mon = mon->nmon) {
3061 count++;
3062 size += size_monst(mon);
3064 *total_count += count;
3065 *total_size += size;
3066 Sprintf(buf, template, src, count, size);
3067 putstr(win, 0, buf);
3071 * Display memory usage of all monsters and objects on the level.
3073 static int
3074 wiz_show_stats()
3076 char buf[BUFSZ];
3077 winid win;
3078 long total_obj_size = 0, total_obj_count = 0;
3079 long total_mon_size = 0, total_mon_count = 0;
3081 win = create_nhwindow(NHW_TEXT);
3082 putstr(win, 0, "Current memory statistics:");
3083 putstr(win, 0, "");
3084 Sprintf(buf, "Objects, size %d", (int) sizeof(struct obj));
3085 putstr(win, 0, buf);
3086 putstr(win, 0, "");
3087 putstr(win, 0, count_str);
3089 obj_chain(win, "invent", invent, &total_obj_count, &total_obj_size);
3090 obj_chain(win, "fobj", fobj, &total_obj_count, &total_obj_size);
3091 obj_chain(win, "buried", level.buriedobjlist, &total_obj_count,
3092 &total_obj_size);
3093 obj_chain(win, "migrating obj", migrating_objs, &total_obj_count,
3094 &total_obj_size);
3095 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3096 mon_invent_chain(win, "migrating minvent", migrating_mons,
3097 &total_obj_count, &total_obj_size);
3099 contained(win, "contained", &total_obj_count, &total_obj_size);
3101 putstr(win, 0, separator);
3102 Sprintf(buf, template, "Total", total_obj_count, total_obj_size);
3103 putstr(win, 0, buf);
3105 putstr(win, 0, "");
3106 putstr(win, 0, "");
3107 Sprintf(buf, "Monsters, size %d", (int) sizeof(struct monst));
3108 putstr(win, 0, buf);
3109 putstr(win, 0, "");
3111 mon_chain(win, "fmon", fmon, &total_mon_count, &total_mon_size);
3112 mon_chain(win, "migrating", migrating_mons, &total_mon_count,
3113 &total_mon_size);
3115 putstr(win, 0, separator);
3116 Sprintf(buf, template, "Total", total_mon_count, total_mon_size);
3117 putstr(win, 0, buf);
3119 #if defined(__BORLANDC__) && !defined(_WIN32)
3120 show_borlandc_stats(win);
3121 #endif
3123 display_nhwindow(win, FALSE);
3124 destroy_nhwindow(win);
3125 return 0;
3128 void
3129 sanity_check()
3131 obj_sanity_check();
3132 timer_sanity_check();
3133 mon_sanity_check();
3134 light_sources_sanity_check();
3137 #ifdef DEBUG_MIGRATING_MONS
3138 static int
3139 wiz_migrate_mons()
3141 int mcount = 0;
3142 char inbuf[BUFSZ];
3143 struct permonst *ptr;
3144 struct monst *mtmp;
3145 d_level tolevel;
3147 getlin("How many random monsters to migrate? [0]", inbuf);
3148 if (*inbuf == '\033')
3149 return 0;
3150 mcount = atoi(inbuf);
3151 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3152 return 0;
3153 while (mcount > 0) {
3154 if (Is_stronghold(&u.uz))
3155 assign_level(&tolevel, &valley_level);
3156 else
3157 get_level(&tolevel, depth(&u.uz) + 1);
3158 ptr = rndmonst();
3159 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3160 if (mtmp)
3161 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3162 (coord *) 0);
3163 mcount--;
3165 return 0;
3167 #endif
3169 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3170 #define unmeta(c) (0x7f & (c))
3172 /* called at startup and after number_pad is twiddled */
3173 void
3174 reset_commands(initial)
3175 boolean initial;
3177 static const char sdir[] = "hykulnjb><",
3178 sdir_swap_yz[] = "hzkulnjb><",
3179 ndir[] = "47896321><",
3180 ndir_phone_layout[] = "41236987><";
3181 static const int ylist[] = {
3182 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3184 const struct func_tab *cmdtmp;
3185 boolean flagtemp;
3186 int c, i, updated = 0;
3188 if (initial) {
3189 updated = 1;
3190 for (i = 0; i < SIZE(cmdlist); i++) {
3191 c = cmdlist[i].f_char & 0xff;
3192 Cmd.commands[c] = &cmdlist[i];
3194 Cmd.num_pad = FALSE;
3195 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3196 } else {
3197 /* basic num_pad */
3198 flagtemp = iflags.num_pad;
3199 if (flagtemp != Cmd.num_pad) {
3200 Cmd.num_pad = flagtemp;
3201 ++updated;
3203 /* swap_yz mode (only applicable for !num_pad) */
3204 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3205 if (flagtemp != Cmd.swap_yz) {
3206 Cmd.swap_yz = flagtemp;
3207 ++updated;
3208 /* Cmd.swap_yz has been toggled;
3209 perform the swap (or reverse previous one) */
3210 for (i = 0; i < SIZE(ylist); i++) {
3211 c = ylist[i] & 0xff;
3212 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3213 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3214 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3217 /* MSDOS compatibility mode (only applicable for num_pad) */
3218 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3219 if (flagtemp != Cmd.pcHack_compat) {
3220 Cmd.pcHack_compat = flagtemp;
3221 ++updated;
3222 /* pcHack_compat has been toggled */
3223 c = M('5') & 0xff;
3224 cmdtmp = Cmd.commands['5'];
3225 Cmd.commands['5'] = Cmd.commands[c];
3226 Cmd.commands[c] = cmdtmp;
3227 c = M('0') & 0xff;
3228 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3230 /* phone keypad layout (only applicable for num_pad) */
3231 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3232 if (flagtemp != Cmd.phone_layout) {
3233 Cmd.phone_layout = flagtemp;
3234 ++updated;
3235 /* phone_layout has been toggled */
3236 for (i = 0; i < 3; i++) {
3237 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3238 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3239 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3240 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3241 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3242 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3243 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3244 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3247 } /*?initial*/
3249 if (updated)
3250 Cmd.serialno++;
3251 Cmd.dirchars = !Cmd.num_pad
3252 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3253 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3254 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3256 Cmd.move_W = Cmd.dirchars[0];
3257 Cmd.move_NW = Cmd.dirchars[1];
3258 Cmd.move_N = Cmd.dirchars[2];
3259 Cmd.move_NE = Cmd.dirchars[3];
3260 Cmd.move_E = Cmd.dirchars[4];
3261 Cmd.move_SE = Cmd.dirchars[5];
3262 Cmd.move_S = Cmd.dirchars[6];
3263 Cmd.move_SW = Cmd.dirchars[7];
3266 STATIC_OVL boolean
3267 accept_menu_prefix(cmd_func)
3268 int NDECL((*cmd_func));
3270 if (cmd_func == dopickup || cmd_func == dotip
3271 || cmd_func == doextcmd || cmd_func == doextlist)
3272 return TRUE;
3273 return FALSE;
3276 void
3277 rhack(cmd)
3278 register char *cmd;
3280 boolean do_walk, do_rush, prefix_seen, bad_command,
3281 firsttime = (cmd == 0);
3283 iflags.menu_requested = FALSE;
3284 #ifdef SAFERHANGUP
3285 if (program_state.done_hup)
3286 end_of_input();
3287 #endif
3288 if (firsttime) {
3289 context.nopick = 0;
3290 cmd = parse();
3292 if (*cmd == '\033') {
3293 context.move = FALSE;
3294 return;
3296 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3297 in_doagain = TRUE;
3298 stail = 0;
3299 rhack((char *) 0); /* read and execute command */
3300 in_doagain = FALSE;
3301 return;
3303 /* Special case of *cmd == ' ' handled better below */
3304 if (!*cmd || *cmd == (char) 0377) {
3305 nhbell();
3306 context.move = FALSE;
3307 return; /* probably we just had an interrupt */
3310 /* handle most movement commands */
3311 do_walk = do_rush = prefix_seen = FALSE;
3312 context.travel = context.travel1 = 0;
3313 switch (*cmd) {
3314 case 'g':
3315 if (movecmd(cmd[1])) {
3316 context.run = 2;
3317 do_rush = TRUE;
3318 } else
3319 prefix_seen = TRUE;
3320 break;
3321 case '5':
3322 if (!Cmd.num_pad)
3323 break; /* else FALLTHRU */
3324 case 'G':
3325 if (movecmd(lowc(cmd[1]))) {
3326 context.run = 3;
3327 do_rush = TRUE;
3328 } else
3329 prefix_seen = TRUE;
3330 break;
3331 case '-':
3332 if (!Cmd.num_pad)
3333 break; /* else FALLTHRU */
3334 /* Effects of movement commands and invisible monsters:
3335 * m: always move onto space (even if 'I' remembered)
3336 * F: always attack space (even if 'I' not remembered)
3337 * normal movement: attack if 'I', move otherwise.
3339 case 'F':
3340 if (movecmd(cmd[1])) {
3341 context.forcefight = 1;
3342 do_walk = TRUE;
3343 } else
3344 prefix_seen = TRUE;
3345 break;
3346 case 'm':
3347 if (movecmd(cmd[1]) || u.dz) {
3348 context.run = 0;
3349 context.nopick = 1;
3350 if (!u.dz)
3351 do_walk = TRUE;
3352 else
3353 cmd[0] = cmd[1]; /* "m<" or "m>" */
3354 } else
3355 prefix_seen = TRUE;
3356 break;
3357 case 'M':
3358 if (movecmd(lowc(cmd[1]))) {
3359 context.run = 1;
3360 context.nopick = 1;
3361 do_rush = TRUE;
3362 } else
3363 prefix_seen = TRUE;
3364 break;
3365 case '0':
3366 if (!Cmd.num_pad)
3367 break;
3368 (void) ddoinv(); /* a convenience borrowed from the PC */
3369 context.move = FALSE;
3370 multi = 0;
3371 return;
3372 case CMD_CLICKLOOK:
3373 if (iflags.clicklook) {
3374 context.move = FALSE;
3375 do_look(2, &clicklook_cc);
3377 return;
3378 case CMD_TRAVEL:
3379 if (flags.travelcmd) {
3380 context.travel = 1;
3381 context.travel1 = 1;
3382 context.run = 8;
3383 context.nopick = 1;
3384 do_rush = TRUE;
3385 break;
3387 /*FALLTHRU*/
3388 default:
3389 if (movecmd(*cmd)) { /* ordinary movement */
3390 context.run = 0; /* only matters here if it was 8 */
3391 do_walk = TRUE;
3392 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
3393 context.run = 1;
3394 do_rush = TRUE;
3395 } else if (movecmd(unctrl(*cmd))) {
3396 context.run = 3;
3397 do_rush = TRUE;
3399 break;
3402 /* some special prefix handling */
3403 /* overload 'm' prefix to mean "request a menu" */
3404 if (prefix_seen && cmd[0] == 'm') {
3405 /* (for func_tab cast, see below) */
3406 const struct func_tab *ft = Cmd.commands[cmd[1] & 0xff];
3407 int NDECL((*func)) = ft ? ((struct func_tab *) ft)->f_funct : 0;
3409 if (func && accept_menu_prefix(func)) {
3410 iflags.menu_requested = TRUE;
3411 ++cmd;
3415 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
3416 /* trying to move diagonally as a grid bug;
3417 this used to be treated by movecmd() as not being
3418 a movement attempt, but that didn't provide for any
3419 feedback and led to strangeness if the key pressed
3420 ('u' in particular) was overloaded for num_pad use */
3421 You_cant("get there from here...");
3422 context.run = 0;
3423 context.nopick = context.forcefight = FALSE;
3424 context.move = context.mv = FALSE;
3425 multi = 0;
3426 return;
3429 if (do_walk) {
3430 if (multi)
3431 context.mv = TRUE;
3432 domove();
3433 context.forcefight = 0;
3434 return;
3435 } else if (do_rush) {
3436 if (firsttime) {
3437 if (!multi)
3438 multi = max(COLNO, ROWNO);
3439 u.last_str_turn = 0;
3441 context.mv = TRUE;
3442 domove();
3443 return;
3444 } else if (prefix_seen && cmd[1] == '\033') { /* <prefix><escape> */
3445 /* don't report "unknown command" for change of heart... */
3446 bad_command = FALSE;
3447 } else if (*cmd == ' ' && !flags.rest_on_space) {
3448 bad_command = TRUE; /* skip cmdlist[] loop */
3450 /* handle all other commands */
3451 } else {
3452 register const struct func_tab *tlist;
3453 int res, NDECL((*func));
3455 /* current - use *cmd to directly index cmdlist array */
3456 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
3457 if (u.uburied && !tlist->can_if_buried) {
3458 You_cant("do that while you are buried!");
3459 res = 0;
3460 } else {
3461 /* we discard 'const' because some compilers seem to have
3462 trouble with the pointer passed to set_occupation() */
3463 func = ((struct func_tab *) tlist)->f_funct;
3464 if (tlist->f_text && !occupation && multi)
3465 set_occupation(func, tlist->f_text, multi);
3466 res = (*func)(); /* perform the command */
3468 if (!res) {
3469 context.move = FALSE;
3470 multi = 0;
3472 return;
3474 /* if we reach here, cmd wasn't found in cmdlist[] */
3475 bad_command = TRUE;
3478 if (bad_command) {
3479 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3480 register char c;
3482 expcmd[0] = '\0';
3483 while ((c = *cmd++) != '\0')
3484 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
3486 if (!prefix_seen || !iflags.cmdassist
3487 || !help_dir(0, "Invalid direction key!"))
3488 Norep("Unknown command '%s'.", expcmd);
3490 /* didn't move */
3491 context.move = FALSE;
3492 multi = 0;
3493 return;
3496 /* convert an x,y pair into a direction code */
3498 xytod(x, y)
3499 schar x, y;
3501 register int dd;
3503 for (dd = 0; dd < 8; dd++)
3504 if (x == xdir[dd] && y == ydir[dd])
3505 return dd;
3506 return -1;
3509 /* convert a direction code into an x,y pair */
3510 void
3511 dtoxy(cc, dd)
3512 coord *cc;
3513 register int dd;
3515 cc->x = xdir[dd];
3516 cc->y = ydir[dd];
3517 return;
3520 /* also sets u.dz, but returns false for <> */
3522 movecmd(sym)
3523 char sym;
3525 register const char *dp = index(Cmd.dirchars, sym);
3527 u.dz = 0;
3528 if (!dp || !*dp)
3529 return 0;
3530 u.dx = xdir[dp - Cmd.dirchars];
3531 u.dy = ydir[dp - Cmd.dirchars];
3532 u.dz = zdir[dp - Cmd.dirchars];
3533 #if 0 /* now handled elsewhere */
3534 if (u.dx && u.dy && NODIAG(u.umonnum)) {
3535 u.dx = u.dy = 0;
3536 return 0;
3538 #endif
3539 return !u.dz;
3542 /* grid bug handling which used to be in movecmd() */
3544 dxdy_moveok()
3546 if (u.dx && u.dy && NODIAG(u.umonnum))
3547 u.dx = u.dy = 0;
3548 return u.dx || u.dy;
3551 /* decide whether a character (user input keystroke) requests screen repaint */
3552 boolean
3553 redraw_cmd(c)
3554 char c;
3556 return (boolean) (c == C('r') || (Cmd.num_pad && c == C('l')));
3560 * uses getdir() but unlike getdir() it specifically
3561 * produces coordinates using the direction from getdir()
3562 * and verifies that those coordinates are ok.
3564 * If the call to getdir() returns 0, Never_mind is displayed.
3565 * If the resulting coordinates are not okay, emsg is displayed.
3567 * Returns non-zero if coordinates in cc are valid.
3570 get_adjacent_loc(prompt, emsg, x, y, cc)
3571 const char *prompt, *emsg;
3572 xchar x, y;
3573 coord *cc;
3575 xchar new_x, new_y;
3576 if (!getdir(prompt)) {
3577 pline1(Never_mind);
3578 return 0;
3580 new_x = x + u.dx;
3581 new_y = y + u.dy;
3582 if (cc && isok(new_x, new_y)) {
3583 cc->x = new_x;
3584 cc->y = new_y;
3585 } else {
3586 if (emsg)
3587 pline1(emsg);
3588 return 0;
3590 return 1;
3594 getdir(s)
3595 const char *s;
3597 char dirsym;
3598 int is_mov;
3600 retry:
3601 if (in_doagain || *readchar_queue)
3602 dirsym = readchar();
3603 else
3604 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
3605 (char *) 0, '\0');
3606 /* remove the prompt string so caller won't have to */
3607 clear_nhwindow(WIN_MESSAGE);
3609 if (redraw_cmd(dirsym)) { /* ^R */
3610 docrt(); /* redraw */
3611 goto retry;
3613 savech(dirsym);
3615 if (dirsym == '.' || dirsym == 's') {
3616 u.dx = u.dy = u.dz = 0;
3617 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
3618 boolean did_help = FALSE, help_requested;
3620 if (!index(quitchars, dirsym)) {
3621 help_requested = (dirsym == '?');
3622 if (help_requested || iflags.cmdassist) {
3623 did_help = help_dir((s && *s == '^') ? dirsym : 0,
3624 help_requested ? (const char *) 0
3625 : "Invalid direction key!");
3626 if (help_requested)
3627 goto retry;
3629 if (!did_help)
3630 pline("What a strange direction!");
3632 return 0;
3633 } else if (is_mov && !dxdy_moveok()) {
3634 You_cant("orient yourself that direction.");
3635 return 0;
3637 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
3638 confdir();
3639 return 1;
3642 STATIC_OVL boolean
3643 help_dir(sym, msg)
3644 char sym;
3645 const char *msg;
3647 char ctrl;
3648 winid win;
3649 static const char wiz_only_list[] = "EFGIOVW";
3650 char buf[BUFSZ], buf2[BUFSZ], *explain;
3652 win = create_nhwindow(NHW_TEXT);
3653 if (!win)
3654 return FALSE;
3655 if (msg) {
3656 Sprintf(buf, "cmdassist: %s", msg);
3657 putstr(win, 0, buf);
3658 putstr(win, 0, "");
3660 if (letter(sym)) {
3661 sym = highc(sym);
3662 ctrl = (sym - 'A') + 1;
3663 if ((explain = dowhatdoes_core(ctrl, buf2))
3664 && (!index(wiz_only_list, sym) || wizard)) {
3665 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
3666 index(wiz_only_list, sym)
3667 ? ""
3668 : " as specified in the Guidebook");
3669 putstr(win, 0, buf);
3670 putstr(win, 0, "");
3671 putstr(win, 0, explain);
3672 putstr(win, 0, "");
3673 putstr(win, 0, "To use that command, you press");
3674 Sprintf(buf, "the <Ctrl> key, and the <%c> key at the same time.",
3675 sym);
3676 putstr(win, 0, buf);
3677 putstr(win, 0, "");
3680 if (NODIAG(u.umonnum)) {
3681 putstr(win, 0, "Valid direction keys in your current form are:");
3682 Sprintf(buf, " %c ", Cmd.move_N);
3683 putstr(win, 0, buf);
3684 putstr(win, 0, " | ");
3685 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3686 putstr(win, 0, buf);
3687 putstr(win, 0, " | ");
3688 Sprintf(buf, " %c ", Cmd.move_S);
3689 putstr(win, 0, buf);
3690 } else {
3691 putstr(win, 0, "Valid direction keys are:");
3692 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
3693 Cmd.move_NE);
3694 putstr(win, 0, buf);
3695 putstr(win, 0, " \\ | / ");
3696 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3697 putstr(win, 0, buf);
3698 putstr(win, 0, " / | \\ ");
3699 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
3700 Cmd.move_SE);
3701 putstr(win, 0, buf);
3703 putstr(win, 0, "");
3704 putstr(win, 0, " < up");
3705 putstr(win, 0, " > down");
3706 putstr(win, 0, " . direct at yourself");
3707 if (msg) {
3708 /* non-null msg means that this wasn't an explicit user request */
3709 putstr(win, 0, "");
3710 putstr(win, 0,
3711 "(Suppress this message with !cmdassist in config file.)");
3713 display_nhwindow(win, FALSE);
3714 destroy_nhwindow(win);
3715 return TRUE;
3718 void
3719 confdir()
3721 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
3723 u.dx = xdir[x];
3724 u.dy = ydir[x];
3725 return;
3728 const char *
3729 directionname(dir)
3730 int dir;
3732 static NEARDATA const char *const dirnames[] = {
3733 "west", "northwest", "north", "northeast", "east",
3734 "southeast", "south", "southwest", "down", "up",
3737 if (dir < 0 || dir >= SIZE(dirnames))
3738 return "invalid";
3739 return dirnames[dir];
3743 isok(x, y)
3744 register int x, y;
3746 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
3747 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
3750 static NEARDATA int last_multi;
3753 * convert a MAP window position into a movecmd
3755 const char *
3756 click_to_cmd(x, y, mod)
3757 int x, y, mod;
3759 int dir;
3760 static char cmd[4];
3761 cmd[1] = 0;
3763 if (iflags.clicklook && mod == CLICK_2) {
3764 clicklook_cc.x = x;
3765 clicklook_cc.y = y;
3766 cmd[0] = CMD_CLICKLOOK;
3767 return cmd;
3770 x -= u.ux;
3771 y -= u.uy;
3773 if (flags.travelcmd) {
3774 if (abs(x) <= 1 && abs(y) <= 1) {
3775 x = sgn(x), y = sgn(y);
3776 } else {
3777 u.tx = u.ux + x;
3778 u.ty = u.uy + y;
3779 cmd[0] = CMD_TRAVEL;
3780 return cmd;
3783 if (x == 0 && y == 0) {
3784 /* here */
3785 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
3786 || IS_SINK(levl[u.ux][u.uy].typ)) {
3787 cmd[0] = mod == CLICK_1 ? 'q' : M('d');
3788 return cmd;
3789 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
3790 cmd[0] = M('s');
3791 return cmd;
3792 } else if ((u.ux == xupstair && u.uy == yupstair)
3793 || (u.ux == sstairs.sx && u.uy == sstairs.sy
3794 && sstairs.up)
3795 || (u.ux == xupladder && u.uy == yupladder)) {
3796 return "<";
3797 } else if ((u.ux == xdnstair && u.uy == ydnstair)
3798 || (u.ux == sstairs.sx && u.uy == sstairs.sy
3799 && !sstairs.up)
3800 || (u.ux == xdnladder && u.uy == ydnladder)) {
3801 return ">";
3802 } else if (OBJ_AT(u.ux, u.uy)) {
3803 cmd[0] =
3804 Is_container(level.objects[u.ux][u.uy]) ? M('l') : ',';
3805 return cmd;
3806 } else {
3807 return "."; /* just rest */
3811 /* directional commands */
3813 dir = xytod(x, y);
3815 if (!m_at(u.ux + x, u.uy + y)
3816 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
3817 cmd[1] = Cmd.dirchars[dir];
3818 cmd[2] = '\0';
3819 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
3820 /* slight assistance to the player: choose kick/open for them
3822 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
3823 cmd[0] = C('d');
3824 return cmd;
3826 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
3827 cmd[0] = 'o';
3828 return cmd;
3831 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
3832 cmd[0] = 's';
3833 cmd[1] = 0;
3834 return cmd;
3837 } else {
3838 /* convert without using floating point, allowing sloppy clicking */
3839 if (x > 2 * abs(y))
3840 x = 1, y = 0;
3841 else if (y > 2 * abs(x))
3842 x = 0, y = 1;
3843 else if (x < -2 * abs(y))
3844 x = -1, y = 0;
3845 else if (y < -2 * abs(x))
3846 x = 0, y = -1;
3847 else
3848 x = sgn(x), y = sgn(y);
3850 if (x == 0 && y == 0) /* map click on player to "rest" command */
3851 return ".";
3853 dir = xytod(x, y);
3856 /* move, attack, etc. */
3857 cmd[1] = 0;
3858 if (mod == CLICK_1) {
3859 cmd[0] = Cmd.dirchars[dir];
3860 } else {
3861 cmd[0] = (Cmd.num_pad
3862 ? M(Cmd.dirchars[dir])
3863 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
3866 return cmd;
3869 char
3870 get_count(allowchars, inkey, maxcount, count)
3871 char *allowchars;
3872 char inkey;
3873 long maxcount;
3874 long *count;
3876 char qbuf[QBUFSZ];
3877 int key;
3878 long cnt = 0L;
3879 boolean backspaced = FALSE;
3880 /* this should be done in port code so that we have erase_char
3881 and kill_char available; we can at least fake erase_char */
3882 #define STANDBY_erase_char '\177'
3884 for (;;) {
3885 if (inkey) {
3886 key = inkey;
3887 inkey = '\0';
3888 } else
3889 key = readchar();
3891 if (digit(key)) {
3892 cnt = 10L * cnt + (long) (key - '0');
3893 if (cnt < 0)
3894 cnt = 0;
3895 else if (maxcount > 0 && cnt > maxcount)
3896 cnt = maxcount;
3897 } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) {
3898 cnt = cnt / 10;
3899 backspaced = TRUE;
3900 } else if (key == '\033') {
3901 break;
3902 } else if (!allowchars || index(allowchars, key)) {
3903 *count = cnt;
3904 break;
3907 if (cnt > 9 || backspaced) {
3908 clear_nhwindow(WIN_MESSAGE);
3909 if (backspaced && !cnt) {
3910 Sprintf(qbuf, "Count: ");
3911 } else {
3912 Sprintf(qbuf, "Count: %ld", cnt);
3913 backspaced = FALSE;
3915 pline1(qbuf);
3916 mark_synch();
3919 return key;
3923 STATIC_OVL char *
3924 parse()
3926 #ifdef LINT /* static char in_line[COLNO]; */
3927 char in_line[COLNO];
3928 #else
3929 static char in_line[COLNO];
3930 #endif
3931 register int foo;
3932 boolean prezero = FALSE;
3934 multi = 0;
3935 context.move = 1;
3936 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
3938 #ifdef ALTMETA
3939 alt_esc = iflags.altmeta; /* readchar() hack */
3940 #endif
3941 if (!Cmd.num_pad || (foo = readchar()) == 'n') {
3942 long tmpmulti = multi;
3944 foo = get_count(NULL, '\0', LARGEST_INT, &tmpmulti);
3945 last_multi = multi = tmpmulti;
3947 #ifdef ALTMETA
3948 alt_esc = FALSE; /* readchar() reset */
3949 #endif
3951 if (foo == '\033') { /* esc cancels count (TH) */
3952 clear_nhwindow(WIN_MESSAGE);
3953 multi = last_multi = 0;
3954 } else if (foo == DOAGAIN || in_doagain) {
3955 multi = last_multi;
3956 } else {
3957 last_multi = multi;
3958 savech(0); /* reset input queue */
3959 savech((char) foo);
3962 if (multi) {
3963 multi--;
3964 save_cm = in_line;
3965 } else {
3966 save_cm = (char *) 0;
3968 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
3969 if (Cmd.pcHack_compat) {
3970 /* This handles very old inconsistent DOS/Windows behaviour
3971 in a different way: earlier, the keyboard handler mapped
3972 these, which caused counts to be strange when entered
3973 from the number pad. Now do not map them until here. */
3974 switch (foo) {
3975 case '5':
3976 foo = 'g';
3977 break;
3978 case M('5'):
3979 foo = 'G';
3980 break;
3981 case M('0'):
3982 foo = 'I';
3983 break;
3984 default:
3985 break; /* as is */
3989 in_line[0] = foo;
3990 in_line[1] = '\0';
3991 if (foo == 'g' || foo == 'G' || foo == 'm' || foo == 'M' || foo == 'F'
3992 || (Cmd.num_pad && (foo == '5' || foo == '-'))) {
3993 foo = readchar();
3994 savech((char) foo);
3995 in_line[1] = foo;
3996 in_line[2] = 0;
3998 clear_nhwindow(WIN_MESSAGE);
3999 if (prezero)
4000 in_line[0] = '\033';
4001 return in_line;
4004 #ifdef HANGUPHANDLING
4005 /* some very old systems, or descendents of such systems, expect signal
4006 handlers to have return type `int', but they don't actually inspect
4007 the return value so we should be safe using `void' unconditionally */
4008 /*ARGUSED*/
4009 void
4010 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4011 int sig_unused UNUSED;
4013 if (program_state.exiting)
4014 program_state.in_moveloop = 0;
4015 nhwindows_hangup();
4016 #ifdef SAFERHANGUP
4017 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4018 and a couple of other places; actual hangup handling occurs then.
4019 This is 'safer' because it disallows certain cheats and also
4020 protects against losing objects in the process of being thrown,
4021 but also potentially riskier because the disconnected program
4022 must continue running longer before attempting a hangup save. */
4023 program_state.done_hup++;
4024 /* defer hangup iff game appears to be in progress */
4025 if (program_state.in_moveloop && program_state.something_worth_saving)
4026 return;
4027 #endif /* SAFERHANGUP */
4028 end_of_input();
4031 void
4032 end_of_input()
4034 #ifdef NOSAVEONHANGUP
4035 #ifdef INSURANCE
4036 if (flags.ins_chkpt && program_state.something_worth_saving)
4037 program_statue.preserve_locks = 1; /* keep files for recovery */
4038 #endif
4039 program_state.something_worth_saving = 0; /* don't save */
4040 #endif
4042 #ifndef SAFERHANGUP
4043 if (!program_state.done_hup++)
4044 #endif
4045 if (program_state.something_worth_saving)
4046 (void) dosave0();
4047 if (iflags.window_inited)
4048 exit_nhwindows((char *) 0);
4049 clearlocks();
4050 terminate(EXIT_SUCCESS);
4051 /*NOTREACHED*/ /* not necessarily true for vms... */
4052 return;
4054 #endif /* HANGUPHANDLING */
4056 char
4057 readchar()
4059 register int sym;
4060 int x = u.ux, y = u.uy, mod = 0;
4062 if (*readchar_queue)
4063 sym = *readchar_queue++;
4064 else
4065 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4067 #ifdef NR_OF_EOFS
4068 if (sym == EOF) {
4069 register int cnt = NR_OF_EOFS;
4071 * Some SYSV systems seem to return EOFs for various reasons
4072 * (?like when one hits break or for interrupted systemcalls?),
4073 * and we must see several before we quit.
4075 do {
4076 clearerr(stdin); /* omit if clearerr is undefined */
4077 sym = pgetchar();
4078 } while (--cnt && sym == EOF);
4080 #endif /* NR_OF_EOFS */
4082 if (sym == EOF) {
4083 #ifdef HANGUPHANDLING
4084 hangup(0); /* call end_of_input() or set program_state.done_hup */
4085 #endif
4086 sym = '\033';
4087 #ifdef ALTMETA
4088 } else if (sym == '\033' && alt_esc) {
4089 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4090 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4091 if (sym == EOF || sym == 0)
4092 sym = '\033';
4093 else if (sym != '\033')
4094 sym |= 0200; /* force 8th bit on */
4095 #endif /*ALTMETA*/
4096 } else if (sym == 0) {
4097 /* click event */
4098 readchar_queue = click_to_cmd(x, y, mod);
4099 sym = *readchar_queue++;
4101 return (char) sym;
4104 STATIC_PTR int
4105 dotravel(VOID_ARGS)
4107 /* Keyboard travel command */
4108 static char cmd[2];
4109 coord cc;
4111 if (!flags.travelcmd)
4112 return 0;
4113 cmd[1] = 0;
4114 cc.x = iflags.travelcc.x;
4115 cc.y = iflags.travelcc.y;
4116 if (cc.x == -1 && cc.y == -1) {
4117 /* No cached destination, start attempt from current position */
4118 cc.x = u.ux;
4119 cc.y = u.uy;
4121 pline("Where do you want to travel to?");
4122 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4123 /* user pressed ESC */
4124 return 0;
4126 iflags.travelcc.x = u.tx = cc.x;
4127 iflags.travelcc.y = u.ty = cc.y;
4128 cmd[0] = CMD_TRAVEL;
4129 readchar_queue = cmd;
4130 return 0;
4133 #ifdef PORT_DEBUG
4134 extern void NDECL(win32con_debug_keystrokes);
4135 extern void NDECL(win32con_handler_info);
4138 wiz_port_debug()
4140 int n, k;
4141 winid win;
4142 anything any;
4143 int item = 'a';
4144 int num_menu_selections;
4145 struct menu_selection_struct {
4146 char *menutext;
4147 void NDECL((*fn));
4148 } menu_selections[] = {
4149 #ifdef WIN32
4150 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4151 { "show keystroke handler information (tty only)",
4152 win32con_handler_info },
4153 #endif
4154 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4157 num_menu_selections = SIZE(menu_selections) - 1;
4158 if (num_menu_selections > 0) {
4159 menu_item *pick_list;
4160 win = create_nhwindow(NHW_MENU);
4161 start_menu(win);
4162 for (k = 0; k < num_menu_selections; ++k) {
4163 any.a_int = k + 1;
4164 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4165 menu_selections[k].menutext, MENU_UNSELECTED);
4167 end_menu(win, "Which port debugging feature?");
4168 n = select_menu(win, PICK_ONE, &pick_list);
4169 destroy_nhwindow(win);
4170 if (n > 0) {
4171 n = pick_list[0].item.a_int - 1;
4172 free((genericptr_t) pick_list);
4173 /* execute the function */
4174 (*menu_selections[n].fn)();
4176 } else
4177 pline("No port-specific debug capability defined.");
4178 return 0;
4180 #endif /*PORT_DEBUG*/
4183 * Parameter validator for generic yes/no function to prevent
4184 * the core from sending too long a prompt string to the
4185 * window port causing a buffer overflow there.
4187 char
4188 yn_function(query, resp, def)
4189 const char *query, *resp;
4190 char def;
4192 char qbuf[QBUFSZ];
4194 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4196 /* maximum acceptable length is QBUFSZ-1 */
4197 if (strlen(query) >= QBUFSZ) {
4198 /* caller shouldn't have passed anything this long */
4199 paniclog("Query truncated: ", query);
4200 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4201 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4202 query = qbuf;
4204 return (*windowprocs.win_yn_function)(query, resp, def);
4207 /* for paranoid_confirm:quit,die,attack prompting */
4208 boolean
4209 paranoid_query(be_paranoid, prompt)
4210 boolean be_paranoid;
4211 const char *prompt;
4213 boolean confirmed_ok;
4215 /* when paranoid, player must respond with "yes" rather than just 'y'
4216 to give the go-ahead for this query; default is "no" unless the
4217 ParanoidConfirm flag is set in which case there's no default */
4218 if (be_paranoid) {
4219 char qbuf[QBUFSZ], ans[BUFSZ];
4220 const char *promptprefix = "", *responsetype = ParanoidConfirm
4221 ? "(yes|no)"
4222 : "(yes) [no]";
4223 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4225 /* in addition to being paranoid about this particular
4226 query, we might be even more paranoid about all paranoia
4227 responses (ie, ParanoidConfirm is set) in which case we
4228 require "no" to reject in addition to "yes" to confirm
4229 (except we won't loop if response is ESC; it means no) */
4230 do {
4231 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4232 getlin(qbuf, ans);
4233 (void) mungspaces(ans);
4234 confirmed_ok = !strcmpi(ans, "yes");
4235 if (confirmed_ok || *ans == '\033')
4236 break;
4237 promptprefix = "\"Yes\" or \"No\": ";
4238 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4239 } else
4240 confirmed_ok = (yn(prompt) == 'y');
4242 return confirmed_ok;
4246 dosuspend_core()
4248 #ifdef SUSPEND
4249 /* Does current window system support suspend? */
4250 if ((*windowprocs.win_can_suspend)()) {
4251 /* NB: SYSCF SHELLERS handled in port code. */
4252 dosuspend();
4253 } else
4254 #endif
4255 Norep("Suspend command not available.");
4256 return 0;
4259 /*cmd.c*/