Use define for iron ball weight increment
[aNetHack.git] / src / cmd.c
blob693666050e6a5e77050aca769c13c41256c09433
1 /* NetHack 3.6 cmd.c $NHDT-Date: 1451082253 2015/12/25 22:24:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.212 $ */
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 #ifdef TTY_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);
1144 } else
1145 pline("Unavailable command '%s'.",
1146 visctrl((int) cmd_from_func(wiz_intrinsic)));
1147 return 0;
1150 /* #wizrumorcheck command - verify each rumor access */
1151 STATIC_PTR int
1152 wiz_rumor_check(VOID_ARGS)
1154 rumor_check();
1155 return 0;
1158 /* #terrain command -- show known map, inspired by crawl's '|' command */
1159 STATIC_PTR int
1160 doterrain(VOID_ARGS)
1162 winid men;
1163 menu_item *sel;
1164 anything any;
1165 int n;
1166 int which;
1169 * normal play: choose between known map without mons, obj, and traps
1170 * (to see underlying terrain only), or
1171 * known map without mons and objs (to see traps under mons and objs), or
1172 * known map without mons (to see objects under monsters);
1173 * explore mode: normal choices plus full map (w/o mons, objs, traps);
1174 * wizard mode: normal and explore choices plus
1175 * a dump of the internal levl[][].typ codes w/ level flags, or
1176 * a legend for the levl[][].typ codes dump
1178 men = create_nhwindow(NHW_MENU);
1179 start_menu(men);
1180 any = zeroany;
1181 any.a_int = 1;
1182 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1183 "known map without monsters, objects, and traps",
1184 MENU_SELECTED);
1185 any.a_int = 2;
1186 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1187 "known map without monsters and objects",
1188 MENU_UNSELECTED);
1189 any.a_int = 3;
1190 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1191 "known map without monsters",
1192 MENU_UNSELECTED);
1193 if (discover || wizard) {
1194 any.a_int = 4;
1195 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1196 "full map without monsters, objects, and traps",
1197 MENU_UNSELECTED);
1198 if (wizard) {
1199 any.a_int = 5;
1200 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1201 "internal levl[][].typ codes in base-36",
1202 MENU_UNSELECTED);
1203 any.a_int = 6;
1204 add_menu(men, NO_GLYPH, &any, 0, 0, ATR_NONE,
1205 "legend of base-36 levl[][].typ codes",
1206 MENU_UNSELECTED);
1209 end_menu(men, "View which?");
1211 n = select_menu(men, PICK_ONE, &sel);
1212 destroy_nhwindow(men);
1214 * n < 0: player used ESC to cancel;
1215 * n == 0: preselected entry was explicitly chosen and got toggled off;
1216 * n == 1: preselected entry was implicitly chosen via <space>|<enter>;
1217 * n == 2: another entry was explicitly chosen, so skip preselected one.
1219 which = (n < 0) ? -1 : (n == 0) ? 1 : sel[0].item.a_int;
1220 if (n > 1 && which == 1)
1221 which = sel[1].item.a_int;
1222 if (n > 0)
1223 free((genericptr_t) sel);
1225 switch (which) {
1226 case 1: reveal_terrain(0, 0); break; /* known map */
1227 case 2: reveal_terrain(0, 1); break; /* known map with traps */
1228 case 3: reveal_terrain(0, 1|2); break; /* known map w/ traps & objs */
1229 case 4: reveal_terrain(1, 0); break; /* full map */
1230 case 5: wiz_map_levltyp(); break; /* map internals */
1231 case 6: wiz_levltyp_legend(); break; /* internal details */
1232 default: break;
1234 return 0; /* no time elapses */
1237 /* -enlightenment and conduct- */
1238 static winid en_win = WIN_ERR;
1239 static const char You_[] = "You ", are[] = "are ", were[] = "were ",
1240 have[] = "have ", had[] = "had ", can[] = "can ",
1241 could[] = "could ";
1242 static const char have_been[] = "have been ", have_never[] = "have never ",
1243 never[] = "never ";
1245 #define enl_msg(prefix, present, past, suffix, ps) \
1246 enlght_line(prefix, final ? past : present, suffix, ps)
1247 #define you_are(attr, ps) enl_msg(You_, are, were, attr, ps)
1248 #define you_have(attr, ps) enl_msg(You_, have, had, attr, ps)
1249 #define you_can(attr, ps) enl_msg(You_, can, could, attr, ps)
1250 #define you_have_been(goodthing) enl_msg(You_, have_been, were, goodthing, "")
1251 #define you_have_never(badthing) \
1252 enl_msg(You_, have_never, never, badthing, "")
1253 #define you_have_X(something) \
1254 enl_msg(You_, have, (const char *) "", something, "")
1256 static void
1257 enlght_line(start, middle, end, ps)
1258 const char *start, *middle, *end, *ps;
1260 char buf[BUFSZ];
1262 Sprintf(buf, " %s%s%s%s.", start, middle, end, ps);
1263 putstr(en_win, 0, buf);
1266 /* format increased chance to hit or damage or defense (Protection) */
1267 static char *
1268 enlght_combatinc(inctyp, incamt, final, outbuf)
1269 const char *inctyp;
1270 int incamt, final;
1271 char *outbuf;
1273 const char *modif, *bonus;
1274 boolean invrt;
1275 int absamt;
1277 absamt = abs(incamt);
1278 /* Protection amount is typically larger than damage or to-hit;
1279 reduce magnitude by a third in order to stretch modifier ranges
1280 (small:1..5, moderate:6..10, large:11..19, huge:20+) */
1281 if (!strcmp(inctyp, "defense"))
1282 absamt = (absamt * 2) / 3;
1284 if (absamt <= 3)
1285 modif = "small";
1286 else if (absamt <= 6)
1287 modif = "moderate";
1288 else if (absamt <= 12)
1289 modif = "large";
1290 else
1291 modif = "huge";
1293 modif = !incamt ? "no" : an(modif); /* ("no" case shouldn't happen) */
1294 bonus = (incamt >= 0) ? "bonus" : "penalty";
1295 /* "bonus <foo>" (to hit) vs "<bar> bonus" (damage, defense) */
1296 invrt = strcmp(inctyp, "to hit") ? TRUE : FALSE;
1298 Sprintf(outbuf, "%s %s %s", modif, invrt ? inctyp : bonus,
1299 invrt ? bonus : inctyp);
1300 if (final || wizard)
1301 Sprintf(eos(outbuf), " (%s%d)", (incamt > 0) ? "+" : "", incamt);
1303 return outbuf;
1306 /* report half physical or half spell damage */
1307 STATIC_OVL void
1308 enlght_halfdmg(category, final)
1309 int category;
1310 int final;
1312 const char *category_name;
1313 char buf[BUFSZ];
1315 switch (category) {
1316 case HALF_PHDAM:
1317 category_name = "physical";
1318 break;
1319 case HALF_SPDAM:
1320 category_name = "spell";
1321 break;
1322 default:
1323 category_name = "unknown";
1324 break;
1326 Sprintf(buf, " %s %s damage", (final || wizard) ? "half" : "reduced",
1327 category_name);
1328 enl_msg(You_, "take", "took", buf, from_what(category));
1331 /* is hero actively using water walking capability on water (or lava)? */
1332 STATIC_OVL boolean
1333 walking_on_water()
1335 if (u.uinwater || Levitation || Flying)
1336 return FALSE;
1337 return (boolean) (Wwalking
1338 && (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)));
1341 /* check whether hero is wearing something that player definitely knows
1342 confers the target property; item must have been seen and its type
1343 discovered but it doesn't necessarily have to be fully identified */
1344 STATIC_OVL boolean
1345 cause_known(propindx)
1346 int propindx; /* index of a property which can be conveyed by worn item */
1348 register struct obj *o;
1349 long mask = W_ARMOR | W_AMUL | W_RING | W_TOOL;
1351 /* simpler than from_what()/what_gives(); we don't attempt to
1352 handle artifacts and we deliberately ignore wielded items */
1353 for (o = invent; o; o = o->nobj) {
1354 if (!(o->owornmask & mask))
1355 continue;
1356 if ((int) objects[o->otyp].oc_oprop == propindx
1357 && objects[o->otyp].oc_name_known && o->dknown)
1358 return TRUE;
1360 return FALSE;
1363 /* format a characteristic value, accommodating Strength's strangeness */
1364 STATIC_OVL char *
1365 attrval(attrindx, attrvalue, resultbuf)
1366 int attrindx, attrvalue;
1367 char resultbuf[]; /* should be at least [7] to hold "18/100\0" */
1369 if (attrindx != A_STR || attrvalue <= 18)
1370 Sprintf(resultbuf, "%d", attrvalue);
1371 else if (attrvalue > STR18(100)) /* 19 to 25 */
1372 Sprintf(resultbuf, "%d", attrvalue - 100);
1373 else /* simplify "18/ **" to be "18/100" */
1374 Sprintf(resultbuf, "18/%02d", attrvalue - 18);
1375 return resultbuf;
1378 void
1379 enlightenment(mode, final)
1380 int mode; /* BASICENLIGHTENMENT | MAGICENLIGHTENMENT (| both) */
1381 int final; /* ENL_GAMEINPROGRESS:0, ENL_GAMEOVERALIVE, ENL_GAMEOVERDEAD */
1383 char buf[BUFSZ], tmpbuf[BUFSZ];
1385 Strcpy(tmpbuf, plname);
1386 *tmpbuf = highc(*tmpbuf); /* same adjustment as bottom line */
1387 /* as in background_enlightenment, when poly'd we need to use the saved
1388 gender in u.mfemale rather than the current you-as-monster gender */
1389 Sprintf(buf, "%s the %s's attributes:", tmpbuf,
1390 ((Upolyd ? u.mfemale : flags.female) && urole.name.f)
1391 ? urole.name.f
1392 : urole.name.m);
1394 en_win = create_nhwindow(NHW_MENU);
1395 /* title */
1396 putstr(en_win, 0, buf); /* "Conan the Archeologist's attributes:" */
1397 /* background and characteristics; ^X or end-of-game disclosure */
1398 if (mode & BASICENLIGHTENMENT) {
1399 /* role, race, alignment, deities */
1400 background_enlightenment(mode, final);
1401 /* strength, dexterity, &c */
1402 characteristics_enlightenment(mode, final);
1404 /* expanded status line information, including things which aren't
1405 included there due to space considerations--such as obvious
1406 alternative movement indicators (riding, levitation, &c), and
1407 various troubles (turning to stone, trapped, confusion, &c);
1408 shown for both basic and magic enlightenment */
1409 status_enlightenment(mode, final);
1410 /* remaining attributes; shown for potion,&c or wizard mode and
1411 explore mode ^X or end of game disclosure */
1412 if (mode & MAGICENLIGHTENMENT) {
1413 /* intrinsics and other traditional enlightenment feedback */
1414 attributes_enlightenment(mode, final);
1416 display_nhwindow(en_win, TRUE);
1417 destroy_nhwindow(en_win);
1418 en_win = WIN_ERR;
1421 /*ARGSUSED*/
1422 /* display role, race, alignment and such to en_win */
1423 STATIC_OVL void
1424 background_enlightenment(unused_mode, final)
1425 int unused_mode UNUSED;
1426 int final;
1428 const char *role_titl, *rank_titl;
1429 int innategend, difgend, difalgn;
1430 char buf[BUFSZ], tmpbuf[BUFSZ];
1432 /* note that if poly'd, we need to use u.mfemale instead of flags.female
1433 to access hero's saved gender-as-human/elf/&c rather than current one */
1434 innategend = (Upolyd ? u.mfemale : flags.female) ? 1 : 0;
1435 role_titl = (innategend && urole.name.f) ? urole.name.f : urole.name.m;
1436 rank_titl = rank_of(u.ulevel, Role_switch, innategend);
1438 putstr(en_win, 0, ""); /* separator after title */
1439 putstr(en_win, 0, "Background:");
1441 /* if polymorphed, report current shape before underlying role;
1442 will be repeated as first status: "you are transformed" and also
1443 among various attributes: "you are in beast form" (after being
1444 told about lycanthropy) or "you are polymorphed into <a foo>"
1445 (with countdown timer appended for wizard mode); we really want
1446 the player to know he's not a samurai at the moment... */
1447 if (Upolyd) {
1448 struct permonst *uasmon = youmonst.data;
1450 tmpbuf[0] = '\0';
1451 /* here we always use current gender, not saved role gender */
1452 if (!is_male(uasmon) && !is_female(uasmon) && !is_neuter(uasmon))
1453 Sprintf(tmpbuf, "%s ", genders[flags.female ? 1 : 0].adj);
1454 Sprintf(buf, "%sin %s%s form", !final ? "currently " : "", tmpbuf,
1455 uasmon->mname);
1456 you_are(buf, "");
1459 /* report role; omit gender if it's redundant (eg, "female priestess") */
1460 tmpbuf[0] = '\0';
1461 if (!urole.name.f
1462 && ((urole.allow & ROLE_GENDMASK) == (ROLE_MALE | ROLE_FEMALE)
1463 || innategend != flags.initgend))
1464 Sprintf(tmpbuf, "%s ", genders[innategend].adj);
1465 buf[0] = '\0';
1466 if (Upolyd)
1467 Strcpy(buf, "actually "); /* "You are actually a ..." */
1468 if (!strcmpi(rank_titl, role_titl)) {
1469 /* omit role when rank title matches it */
1470 Sprintf(eos(buf), "%s, level %d %s%s", an(rank_titl), u.ulevel,
1471 tmpbuf, urace.noun);
1472 } else {
1473 Sprintf(eos(buf), "%s, a level %d %s%s %s", an(rank_titl), u.ulevel,
1474 tmpbuf, urace.adj, role_titl);
1476 you_are(buf, "");
1478 /* report alignment (bypass you_are() in order to omit ending period) */
1479 Sprintf(buf, " %s%s%s, %son a mission for %s",
1480 You_, !final ? are : were,
1481 align_str(u.ualign.type),
1482 /* helm of opposite alignment (might hide conversion) */
1483 (u.ualign.type != u.ualignbase[A_CURRENT]) ? "temporarily "
1484 /* permanent conversion */
1485 : (u.ualign.type != u.ualignbase[A_ORIGINAL]) ? "now "
1486 /* atheist (ignored in very early game) */
1487 : (!u.uconduct.gnostic && moves > 1000L) ? "nominally "
1488 /* lastly, normal case */
1489 : "",
1490 u_gname());
1491 putstr(en_win, 0, buf);
1492 /* show the rest of this game's pantheon (finishes previous sentence)
1493 [appending "also Moloch" at the end would allow for straightforward
1494 trailing "and" on all three aligned entries but looks too verbose] */
1495 Sprintf(buf, " who %s opposed by", !final ? "is" : "was");
1496 if (u.ualign.type != A_LAWFUL)
1497 Sprintf(eos(buf), " %s (%s) and", align_gname(A_LAWFUL),
1498 align_str(A_LAWFUL));
1499 if (u.ualign.type != A_NEUTRAL)
1500 Sprintf(eos(buf), " %s (%s)%s", align_gname(A_NEUTRAL),
1501 align_str(A_NEUTRAL),
1502 (u.ualign.type != A_CHAOTIC) ? " and" : "");
1503 if (u.ualign.type != A_CHAOTIC)
1504 Sprintf(eos(buf), " %s (%s)", align_gname(A_CHAOTIC),
1505 align_str(A_CHAOTIC));
1506 Strcat(buf, "."); /* terminate sentence */
1507 putstr(en_win, 0, buf);
1509 /* show original alignment,gender,race,role if any have been changed;
1510 giving separate message for temporary alignment change bypasses need
1511 for tricky phrasing otherwise necessitated by possibility of having
1512 helm of opposite alignment mask a permanent alignment conversion */
1513 difgend = (innategend != flags.initgend);
1514 difalgn = (((u.ualign.type != u.ualignbase[A_CURRENT]) ? 1 : 0)
1515 + ((u.ualignbase[A_CURRENT] != u.ualignbase[A_ORIGINAL])
1516 ? 2 : 0));
1517 if (difalgn & 1) { /* have temporary alignment so report permanent one */
1518 Sprintf(buf, "actually %s", align_str(u.ualignbase[A_CURRENT]));
1519 you_are(buf, "");
1520 difalgn &= ~1; /* suppress helm from "started out <foo>" message */
1522 if (difgend || difalgn) { /* sex change or perm align change or both */
1523 Sprintf(buf, " You started out %s%s%s.",
1524 difgend ? genders[flags.initgend].adj : "",
1525 (difgend && difalgn) ? " and " : "",
1526 difalgn ? align_str(u.ualignbase[A_ORIGINAL]) : "");
1527 putstr(en_win, 0, buf);
1531 /* characteristics: expanded version of bottom line strength, dexterity, &c */
1532 STATIC_OVL void
1533 characteristics_enlightenment(mode, final)
1534 int mode;
1535 int final;
1537 putstr(en_win, 0, ""); /* separator after background */
1538 putstr(en_win, 0,
1539 final ? "Final Characteristics:" : "Current Characteristics:");
1541 /* bottom line order */
1542 one_characteristic(mode, final, A_STR); /* strength */
1543 one_characteristic(mode, final, A_DEX); /* dexterity */
1544 one_characteristic(mode, final, A_CON); /* constitution */
1545 one_characteristic(mode, final, A_INT); /* intelligence */
1546 one_characteristic(mode, final, A_WIS); /* wisdom */
1547 one_characteristic(mode, final, A_CHA); /* charisma */
1550 /* display one attribute value for characteristics_enlightenment() */
1551 STATIC_OVL void
1552 one_characteristic(mode, final, attrindx)
1553 int mode, final, attrindx;
1555 boolean hide_innate_value = FALSE, interesting_alimit;
1556 int acurrent, abase, apeak, alimit;
1557 const char *attrname, *paren_pfx;
1558 char subjbuf[BUFSZ], valubuf[BUFSZ], valstring[32];
1560 /* being polymorphed or wearing certain cursed items prevents
1561 hero from reliably tracking changes to characteristics so
1562 we don't show base & peak values then; when the items aren't
1563 cursed, hero could take them off to check underlying values
1564 and we show those in such case so that player doesn't need
1565 to actually resort to doing that */
1566 if (Upolyd) {
1567 hide_innate_value = TRUE;
1568 } else if (Fixed_abil) {
1569 if (stuck_ring(uleft, RIN_SUSTAIN_ABILITY)
1570 || stuck_ring(uright, RIN_SUSTAIN_ABILITY))
1571 hide_innate_value = TRUE;
1573 switch (attrindx) {
1574 case A_STR:
1575 attrname = "strength";
1576 if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER && uarmg->cursed)
1577 hide_innate_value = TRUE;
1578 break;
1579 case A_DEX:
1580 attrname = "dexterity";
1581 break;
1582 case A_CON:
1583 attrname = "constitution";
1584 break;
1585 case A_INT:
1586 attrname = "intelligence";
1587 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1588 hide_innate_value = TRUE;
1589 break;
1590 case A_WIS:
1591 attrname = "wisdom";
1592 if (uarmh && uarmh->otyp == DUNCE_CAP && uarmh->cursed)
1593 hide_innate_value = TRUE;
1594 break;
1595 case A_CHA:
1596 attrname = "charisma";
1597 break;
1598 default:
1599 return; /* impossible */
1601 /* note: final disclosure includes MAGICENLIGHTENTMENT */
1602 if ((mode & MAGICENLIGHTENMENT) && !Upolyd)
1603 hide_innate_value = FALSE;
1605 acurrent = ACURR(attrindx);
1606 (void) attrval(attrindx, acurrent, valubuf); /* Sprintf(valubuf,"%d",) */
1607 Sprintf(subjbuf, "Your %s ", attrname);
1609 if (!hide_innate_value) {
1610 /* show abase, amax, and/or attrmax if acurr doesn't match abase
1611 (a magic bonus or penalty is in effect) or abase doesn't match
1612 amax (some points have been lost to poison or exercise abuse
1613 and are restorable) or attrmax is different from normal human
1614 (while game is in progress; trying to reduce dependency on
1615 spoilers to keep track of such stuff) or attrmax was different
1616 from abase (at end of game; this attribute wasn't maxed out) */
1617 abase = ABASE(attrindx);
1618 apeak = AMAX(attrindx);
1619 alimit = ATTRMAX(attrindx);
1620 /* criterium for whether the limit is interesting varies */
1621 interesting_alimit =
1622 final ? TRUE /* was originally `(abase != alimit)' */
1623 : (alimit != (attrindx != A_STR ? 18 : STR18(100)));
1624 paren_pfx = final ? " (" : " (current; ";
1625 if (acurrent != abase) {
1626 Sprintf(eos(valubuf), "%sbase:%s", paren_pfx,
1627 attrval(attrindx, abase, valstring));
1628 paren_pfx = ", ";
1630 if (abase != apeak) {
1631 Sprintf(eos(valubuf), "%speak:%s", paren_pfx,
1632 attrval(attrindx, apeak, valstring));
1633 paren_pfx = ", ";
1635 if (interesting_alimit) {
1636 Sprintf(eos(valubuf), "%s%slimit:%s", paren_pfx,
1637 /* more verbose if exceeding 'limit' due to magic bonus */
1638 (acurrent > alimit) ? "innate " : "",
1639 attrval(attrindx, alimit, valstring));
1640 /* paren_pfx = ", "; */
1642 if (acurrent != abase || abase != apeak || interesting_alimit)
1643 Strcat(valubuf, ")");
1645 enl_msg(subjbuf, "is ", "was ", valubuf, "");
1648 /* status: selected obvious capabilities, assorted troubles */
1649 STATIC_OVL void
1650 status_enlightenment(mode, final)
1651 int mode;
1652 int final;
1654 boolean magic = (mode & MAGICENLIGHTENMENT) ? TRUE : FALSE;
1655 int cap;
1656 char buf[BUFSZ], youtoo[BUFSZ];
1657 boolean Riding = (u.usteed
1658 /* if hero dies while dismounting, u.usteed will still
1659 be set; we want to ignore steed in that situation */
1660 && !(final == ENL_GAMEOVERDEAD
1661 && !strcmp(killer.name, "riding accident")));
1662 const char *steedname = (!Riding ? (char *) 0
1663 : x_monnam(u.usteed,
1664 u.usteed->mtame ? ARTICLE_YOUR : ARTICLE_THE,
1665 (char *) 0,
1666 (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION),
1667 FALSE));
1670 * Status (many are abbreviated on bottom line; others are or
1671 * should be discernible to the hero hence to the player)
1673 putstr(en_win, 0, ""); /* separator after title or characteristics */
1674 putstr(en_win, 0, final ? "Final Status:" : "Current Status:");
1676 Strcpy(youtoo, You_);
1677 /* not a traditional status but inherently obvious to player; more
1678 detail given below (attributes section) for magic enlightenment */
1679 if (Upolyd)
1680 you_are("transformed", "");
1681 /* not a trouble, but we want to display riding status before maybe
1682 reporting steed as trapped or hero stuck to cursed saddle */
1683 if (Riding) {
1684 Sprintf(buf, "riding %s", steedname);
1685 you_are(buf, "");
1686 Sprintf(eos(youtoo), "and %s ", steedname);
1688 /* other movement situations that hero should always know */
1689 if (Levitation) {
1690 if (Lev_at_will && magic)
1691 you_are("levitating, at will", "");
1692 else
1693 enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
1694 } else if (Flying) { /* can only fly when not levitating */
1695 enl_msg(youtoo, are, were, "flying", from_what(FLYING));
1697 if (Underwater) {
1698 you_are("underwater", "");
1699 } else if (u.uinwater) {
1700 you_are(Swimming ? "swimming" : "in water", from_what(SWIMMING));
1701 } else if (walking_on_water()) {
1702 /* show active Wwalking here, potential Wwalking elsewhere */
1703 Sprintf(buf, "walking on %s",
1704 is_pool(u.ux, u.uy) ? "water"
1705 : is_lava(u.ux, u.uy) ? "lava"
1706 : surface(u.ux, u.uy)); /* catchall; shouldn't happen */
1707 you_are(buf, from_what(WWALKING));
1709 if (Upolyd && (u.uundetected || youmonst.m_ap_type != M_AP_NOTHING))
1710 youhiding(TRUE, final);
1712 /* internal troubles, mostly in the order that prayer ranks them */
1713 if (Stoned)
1714 you_are("turning to stone", "");
1715 if (Slimed)
1716 you_are("turning into slime", "");
1717 if (Strangled) {
1718 if (u.uburied) {
1719 you_are("buried", "");
1720 } else {
1721 Strcpy(buf, "being strangled");
1722 if (wizard)
1723 Sprintf(eos(buf), " (%ld)", (Strangled & TIMEOUT));
1724 you_are(buf, from_what(STRANGLED));
1727 if (Sick) {
1728 /* prayer lumps these together; botl puts Ill before FoodPois */
1729 if (u.usick_type & SICK_NONVOMITABLE)
1730 you_are("terminally sick from illness", "");
1731 if (u.usick_type & SICK_VOMITABLE)
1732 you_are("terminally sick from food poisoning", "");
1734 if (Vomiting)
1735 you_are("nauseated", "");
1736 if (Stunned)
1737 you_are("stunned", "");
1738 if (Confusion)
1739 you_are("confused", "");
1740 if (Hallucination)
1741 you_are("hallucinating", "");
1742 if (Blind) {
1743 /* from_what() (currently wizard-mode only) checks !haseyes()
1744 before u.uroleplay.blind, so we should too */
1745 Sprintf(buf, "%s blind",
1746 !haseyes(youmonst.data) ? "innately"
1747 : u.uroleplay.blind ? "permanently"
1748 /* better phrasing desperately wanted... */
1749 : Blindfolded_only ? "deliberately"
1750 : "temporarily");
1751 if (wizard && (Blinded & TIMEOUT) != 0L
1752 && !u.uroleplay.blind && haseyes(youmonst.data))
1753 Sprintf(eos(buf), " (%ld)", (Blinded & TIMEOUT));
1754 /* !haseyes: avoid "you are innately blind innately" */
1755 you_are(buf, !haseyes(youmonst.data) ? "" : from_what(BLINDED));
1757 if (Deaf)
1758 you_are("deaf", from_what(DEAF));
1760 /* external troubles, more or less */
1761 if (Punished) {
1762 if (uball) {
1763 Sprintf(buf, "chained to %s", ansimpleoname(uball));
1764 } else {
1765 impossible("Punished without uball?");
1766 Strcpy(buf, "punished");
1768 you_are(buf, "");
1770 if (u.utrap) {
1771 char predicament[BUFSZ];
1772 struct trap *t;
1773 boolean anchored = (u.utraptype == TT_BURIEDBALL);
1775 if (anchored) {
1776 Strcpy(predicament, "tethered to something buried");
1777 } else if (u.utraptype == TT_INFLOOR || u.utraptype == TT_LAVA) {
1778 Sprintf(predicament, "stuck in %s", the(surface(u.ux, u.uy)));
1779 } else {
1780 Strcpy(predicament, "trapped");
1781 if ((t = t_at(u.ux, u.uy)) != 0)
1782 Sprintf(eos(predicament), " in %s",
1783 an(defsyms[trap_to_defsym(t->ttyp)].explanation));
1785 if (u.usteed) { /* not `Riding' here */
1786 Sprintf(buf, "%s%s ", anchored ? "you and " : "", steedname);
1787 *buf = highc(*buf);
1788 enl_msg(buf, (anchored ? "are " : "is "),
1789 (anchored ? "were " : "was "), predicament, "");
1790 } else
1791 you_are(predicament, "");
1792 } /* (u.utrap) */
1793 if (u.uswallow) {
1794 Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck));
1795 if (wizard)
1796 Sprintf(eos(buf), " (%u)", u.uswldtim);
1797 you_are(buf, "");
1798 } else if (u.ustuck) {
1799 Sprintf(buf, "%s %s",
1800 (Upolyd && sticks(youmonst.data)) ? "holding" : "held by",
1801 a_monnam(u.ustuck));
1802 you_are(buf, "");
1804 if (Riding) {
1805 struct obj *saddle = which_armor(u.usteed, W_SADDLE);
1807 if (saddle && saddle->cursed) {
1808 Sprintf(buf, "stuck to %s %s", s_suffix(steedname),
1809 simpleonames(saddle));
1810 you_are(buf, "");
1813 if (Wounded_legs) {
1814 /* when mounted, Wounded_legs applies to steed rather than to
1815 hero; we only report steed's wounded legs in wizard mode */
1816 if (u.usteed) { /* not `Riding' here */
1817 if (wizard && steedname) {
1818 Strcpy(buf, steedname);
1819 *buf = highc(*buf);
1820 enl_msg(buf, " has", " had", " wounded legs", "");
1822 } else {
1823 Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
1824 you_have(buf, "");
1827 if (Glib) {
1828 Sprintf(buf, "slippery %s", makeplural(body_part(FINGER)));
1829 you_have(buf, "");
1831 if (Fumbling) {
1832 if (magic || cause_known(FUMBLING))
1833 enl_msg(You_, "fumble", "fumbled", "", from_what(FUMBLING));
1835 if (Sleepy) {
1836 if (magic || cause_known(SLEEPY)) {
1837 Strcpy(buf, from_what(SLEEPY));
1838 if (wizard)
1839 Sprintf(eos(buf), " (%ld)", (HSleepy & TIMEOUT));
1840 enl_msg("You ", "fall", "fell", " asleep uncontrollably", buf);
1843 /* hunger/nutrition */
1844 if (Hunger) {
1845 if (magic || cause_known(HUNGER))
1846 enl_msg(You_, "hunger", "hungered", " rapidly",
1847 from_what(HUNGER));
1849 Strcpy(buf, hu_stat[u.uhs]); /* hunger status; omitted if "normal" */
1850 mungspaces(buf); /* strip trailing spaces */
1851 if (*buf) {
1852 *buf = lowc(*buf); /* override capitalization */
1853 if (!strcmp(buf, "weak"))
1854 Strcat(buf, " from severe hunger");
1855 else if (!strncmp(buf, "faint", 5)) /* fainting, fainted */
1856 Strcat(buf, " due to starvation");
1857 you_are(buf, "");
1859 /* encumbrance */
1860 if ((cap = near_capacity()) > UNENCUMBERED) {
1861 const char *adj = "?_?"; /* (should always get overridden) */
1863 Strcpy(buf, enc_stat[cap]);
1864 *buf = lowc(*buf);
1865 switch (cap) {
1866 case SLT_ENCUMBER:
1867 adj = "slightly";
1868 break; /* burdened */
1869 case MOD_ENCUMBER:
1870 adj = "moderately";
1871 break; /* stressed */
1872 case HVY_ENCUMBER:
1873 adj = "very";
1874 break; /* strained */
1875 case EXT_ENCUMBER:
1876 adj = "extremely";
1877 break; /* overtaxed */
1878 case OVERLOADED:
1879 adj = "not possible";
1880 break;
1882 Sprintf(eos(buf), "; movement %s %s%s", !final ? "is" : "was", adj,
1883 (cap < OVERLOADED) ? " slowed" : "");
1884 you_are(buf, "");
1885 } else {
1886 /* last resort entry, guarantees Status section is non-empty
1887 (no longer needed for that purpose since weapon status added;
1888 still useful though) */
1889 you_are("unencumbered", "");
1891 /* report being weaponless; distinguish whether gloves are worn */
1892 if (!uwep) {
1893 you_are(uarmg ? "empty handed" /* gloves imply hands */
1894 : humanoid(youmonst.data)
1895 /* hands but no weapon and no gloves */
1896 ? "bare handed"
1897 /* alternate phrasing for paws or lack of hands */
1898 : "not wielding anything",
1899 "");
1900 /* two-weaponing implies a weapon (not other odd stuff) in each hand */
1901 } else if (u.twoweap) {
1902 you_are("wielding two weapons at once", "");
1903 /* report most weapons by their skill class (so a katana will be
1904 described as a long sword, for instance; mattock and hook are
1905 exceptions), or wielded non-weapon item by its object class */
1906 } else {
1907 const char *what = weapon_descr(uwep);
1909 if (!strcmpi(what, "armor") || !strcmpi(what, "food")
1910 || !strcmpi(what, "venom"))
1911 Sprintf(buf, "wielding some %s", what);
1912 else
1913 Sprintf(buf, "wielding %s",
1914 (uwep->quan == 1L) ? an(what) : makeplural(what));
1915 you_are(buf, "");
1917 /* report 'nudity' */
1918 if (!uarm && !uarmu && !uarmc && !uarmg && !uarmf && !uarmh) {
1919 if (u.uroleplay.nudist)
1920 enl_msg(You_, "do", "did", " not wear any armor", "");
1921 else
1922 you_are("not wearing any armor", "");
1926 /* attributes: intrinsics and the like, other non-obvious capabilities */
1927 void
1928 attributes_enlightenment(unused_mode, final)
1929 int unused_mode UNUSED;
1930 int final;
1932 static NEARDATA const char if_surroundings_permitted[] =
1933 " if surroundings permitted";
1934 int ltmp, armpro;
1935 char buf[BUFSZ];
1938 * Attributes
1940 putstr(en_win, 0, "");
1941 putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:");
1943 if (u.uevent.uhand_of_elbereth) {
1944 static const char *const hofe_titles[3] = { "the Hand of Elbereth",
1945 "the Envoy of Balance",
1946 "the Glory of Arioch" };
1947 you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1], "");
1950 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
1951 if (u.ualign.record >= 20)
1952 you_are("piously aligned", "");
1953 else if (u.ualign.record > 13)
1954 you_are("devoutly aligned", "");
1955 else if (u.ualign.record > 8)
1956 you_are("fervently aligned", "");
1957 else if (u.ualign.record > 3)
1958 you_are("stridently aligned", "");
1959 else if (u.ualign.record == 3)
1960 you_are("aligned", "");
1961 else if (u.ualign.record > 0)
1962 you_are("haltingly aligned", "");
1963 else if (u.ualign.record == 0)
1964 you_are("nominally aligned", "");
1965 else if (u.ualign.record >= -3)
1966 you_have("strayed", "");
1967 else if (u.ualign.record >= -8)
1968 you_have("sinned", "");
1969 else
1970 you_have("transgressed", "");
1971 if (wizard) {
1972 Sprintf(buf, " %d", u.ualign.record);
1973 enl_msg("Your alignment ", "is", "was", buf, "");
1976 /*** Resistances to troubles ***/
1977 if (Invulnerable)
1978 you_are("invulnerable", from_what(INVULNERABLE));
1979 if (Antimagic)
1980 you_are("magic-protected", from_what(ANTIMAGIC));
1981 if (Fire_resistance)
1982 you_are("fire resistant", from_what(FIRE_RES));
1983 if (Cold_resistance)
1984 you_are("cold resistant", from_what(COLD_RES));
1985 if (Sleep_resistance)
1986 you_are("sleep resistant", from_what(SLEEP_RES));
1987 if (Disint_resistance)
1988 you_are("disintegration-resistant", from_what(DISINT_RES));
1989 if (Shock_resistance)
1990 you_are("shock resistant", from_what(SHOCK_RES));
1991 if (Poison_resistance)
1992 you_are("poison resistant", from_what(POISON_RES));
1993 if (Acid_resistance)
1994 you_are("acid resistant", from_what(ACID_RES));
1995 if (Drain_resistance)
1996 you_are("level-drain resistant", from_what(DRAIN_RES));
1997 if (Sick_resistance)
1998 you_are("immune to sickness", from_what(SICK_RES));
1999 if (Stone_resistance)
2000 you_are("petrification resistant", from_what(STONE_RES));
2001 if (Halluc_resistance)
2002 enl_msg(You_, "resist", "resisted", " hallucinations",
2003 from_what(HALLUC_RES));
2004 if (u.uedibility)
2005 you_can("recognize detrimental food", "");
2007 /*** Vision and senses ***/
2008 if (!Blind && (Blinded || !haseyes(youmonst.data)))
2009 you_can("see", from_what(-BLINDED)); /* Eyes of the Overworld */
2010 if (See_invisible) {
2011 if (!Blind)
2012 enl_msg(You_, "see", "saw", " invisible", from_what(SEE_INVIS));
2013 else
2014 enl_msg(You_, "will see", "would have seen",
2015 " invisible when not blind", from_what(SEE_INVIS));
2017 if (Blind_telepat)
2018 you_are("telepathic", from_what(TELEPAT));
2019 if (Warning)
2020 you_are("warned", from_what(WARNING));
2021 if (Warn_of_mon && context.warntype.obj) {
2022 Sprintf(buf, "aware of the presence of %s",
2023 (context.warntype.obj & M2_ORC) ? "orcs"
2024 : (context.warntype.obj & M2_ELF) ? "elves"
2025 : (context.warntype.obj & M2_DEMON) ? "demons" : something);
2026 you_are(buf, from_what(WARN_OF_MON));
2028 if (Warn_of_mon && context.warntype.polyd) {
2029 Sprintf(buf, "aware of the presence of %s",
2030 ((context.warntype.polyd & (M2_HUMAN | M2_ELF))
2031 == (M2_HUMAN | M2_ELF))
2032 ? "humans and elves"
2033 : (context.warntype.polyd & M2_HUMAN)
2034 ? "humans"
2035 : (context.warntype.polyd & M2_ELF)
2036 ? "elves"
2037 : (context.warntype.polyd & M2_ORC)
2038 ? "orcs"
2039 : (context.warntype.polyd & M2_DEMON)
2040 ? "demons"
2041 : "certain monsters");
2042 you_are(buf, "");
2044 if (Warn_of_mon && context.warntype.speciesidx >= LOW_PM) {
2045 Sprintf(buf, "aware of the presence of %s",
2046 makeplural(mons[context.warntype.speciesidx].mname));
2047 you_are(buf, from_what(WARN_OF_MON));
2049 if (Undead_warning)
2050 you_are("warned of undead", from_what(WARN_UNDEAD));
2051 if (Searching)
2052 you_have("automatic searching", from_what(SEARCHING));
2053 if (Clairvoyant)
2054 you_are("clairvoyant", from_what(CLAIRVOYANT));
2055 else if ((HClairvoyant || EClairvoyant) && BClairvoyant) {
2056 Strcpy(buf, from_what(-CLAIRVOYANT));
2057 if (!strncmp(buf, " because of ", 12))
2058 /* overwrite substring; strncpy doesn't add terminator */
2059 (void) strncpy(buf, " if not for ", 12);
2060 enl_msg(You_, "could be", "could have been", " clairvoyant", buf);
2062 if (Infravision)
2063 you_have("infravision", from_what(INFRAVISION));
2064 if (Detect_monsters)
2065 you_are("sensing the presence of monsters", "");
2066 if (u.umconf)
2067 you_are("going to confuse monsters", "");
2069 /*** Appearance and behavior ***/
2070 if (Adornment) {
2071 int adorn = 0;
2073 if (uleft && uleft->otyp == RIN_ADORNMENT)
2074 adorn += uleft->spe;
2075 if (uright && uright->otyp == RIN_ADORNMENT)
2076 adorn += uright->spe;
2077 /* the sum might be 0 (+0 ring or two which negate each other);
2078 that yields "you are charismatic" (which isn't pointless
2079 because it potentially impacts seduction attacks) */
2080 Sprintf(buf, "%scharismatic",
2081 (adorn > 0) ? "more " : (adorn < 0) ? "less " : "");
2082 you_are(buf, from_what(ADORNED));
2084 if (Invisible)
2085 you_are("invisible", from_what(INVIS));
2086 else if (Invis)
2087 you_are("invisible to others", from_what(INVIS));
2088 /* ordinarily "visible" is redundant; this is a special case for
2089 the situation when invisibility would be an expected attribute */
2090 else if ((HInvis || EInvis) && BInvis)
2091 you_are("visible", from_what(-INVIS));
2092 if (Displaced)
2093 you_are("displaced", from_what(DISPLACED));
2094 if (Stealth)
2095 you_are("stealthy", from_what(STEALTH));
2096 if (Aggravate_monster)
2097 enl_msg("You aggravate", "", "d", " monsters",
2098 from_what(AGGRAVATE_MONSTER));
2099 if (Conflict)
2100 enl_msg("You cause", "", "d", " conflict", from_what(CONFLICT));
2102 /*** Transportation ***/
2103 if (Jumping)
2104 you_can("jump", from_what(JUMPING));
2105 if (Teleportation)
2106 you_can("teleport", from_what(TELEPORT));
2107 if (Teleport_control)
2108 you_have("teleport control", from_what(TELEPORT_CONTROL));
2109 /* actively levitating handled earlier as a status condition */
2110 if (BLevitation) { /* levitation is blocked */
2111 long save_BLev = BLevitation;
2113 BLevitation = 0L;
2114 if (Levitation)
2115 enl_msg(You_, "would levitate", "would have levitated",
2116 if_surroundings_permitted, "");
2117 BLevitation = save_BLev;
2119 /* actively flying handled earlier as a status condition */
2120 if (BFlying) { /* flight is blocked */
2121 long save_BFly = BFlying;
2123 BFlying = 0L;
2124 if (Flying)
2125 enl_msg(You_, "would fly", "would have flown",
2126 Levitation
2127 ? "if you weren't levitating"
2128 : (save_BFly == FROMOUTSIDE)
2129 ? if_surroundings_permitted
2130 /* both surroundings and [latent] levitation */
2131 : " if circumstances permitted",
2132 "");
2133 BFlying = save_BFly;
2135 /* actively walking on water handled earlier as a status condition */
2136 if (Wwalking && !walking_on_water())
2137 you_can("walk on water", from_what(WWALKING));
2138 /* actively swimming (in water but not under it) handled earlier */
2139 if (Swimming && (Underwater || !u.uinwater))
2140 you_can("swim", from_what(SWIMMING));
2141 if (Breathless)
2142 you_can("survive without air", from_what(MAGICAL_BREATHING));
2143 else if (Amphibious)
2144 you_can("breathe water", from_what(MAGICAL_BREATHING));
2145 if (Passes_walls)
2146 you_can("walk through walls", from_what(PASSES_WALLS));
2148 /*** Physical attributes ***/
2149 if (Regeneration)
2150 enl_msg("You regenerate", "", "d", "", from_what(REGENERATION));
2151 if (Slow_digestion)
2152 you_have("slower digestion", from_what(SLOW_DIGESTION));
2153 if (u.uhitinc)
2154 you_have(enlght_combatinc("to hit", u.uhitinc, final, buf), "");
2155 if (u.udaminc)
2156 you_have(enlght_combatinc("damage", u.udaminc, final, buf), "");
2157 if (u.uspellprot || Protection) {
2158 int prot = 0;
2160 if (uleft && uleft->otyp == RIN_PROTECTION)
2161 prot += uleft->spe;
2162 if (uright && uright->otyp == RIN_PROTECTION)
2163 prot += uright->spe;
2164 if (HProtection & INTRINSIC)
2165 prot += u.ublessed;
2166 prot += u.uspellprot;
2167 if (prot)
2168 you_have(enlght_combatinc("defense", prot, final, buf), "");
2170 if ((armpro = magic_negation(&youmonst)) > 0) {
2171 /* magic cancellation factor, conferred by worn armor */
2172 static const char *const mc_types[] = {
2173 "" /*ordinary*/, "warded", "guarded", "protected",
2175 /* sanity check */
2176 if (armpro >= SIZE(mc_types))
2177 armpro = SIZE(mc_types) - 1;
2178 you_are(mc_types[armpro], "");
2180 if (Half_physical_damage)
2181 enlght_halfdmg(HALF_PHDAM, final);
2182 if (Half_spell_damage)
2183 enlght_halfdmg(HALF_SPDAM, final);
2184 /* polymorph and other shape change */
2185 if (Protection_from_shape_changers)
2186 you_are("protected from shape changers",
2187 from_what(PROT_FROM_SHAPE_CHANGERS));
2188 if (Unchanging) {
2189 const char *what = 0;
2191 if (!Upolyd) /* Upolyd handled below after current form */
2192 you_can("not change from your current form",
2193 from_what(UNCHANGING));
2194 /* blocked shape changes */
2195 if (Polymorph)
2196 what = !final ? "polymorph" : "have polymorphed";
2197 else if (u.ulycn >= LOW_PM)
2198 what = !final ? "change shape" : "have changed shape";
2199 if (what) {
2200 Sprintf(buf, "would %s periodically", what);
2201 /* omit from_what(UNCHANGING); too verbose */
2202 enl_msg(You_, buf, buf, " if not locked into your current form",
2203 "");
2205 } else if (Polymorph) {
2206 you_are("polymorphing periodically", from_what(POLYMORPH));
2208 if (Polymorph_control)
2209 you_have("polymorph control", from_what(POLYMORPH_CONTROL));
2210 if (Upolyd && u.umonnum != u.ulycn) {
2211 /* foreign shape (except were-form which is handled below) */
2212 Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
2213 if (wizard)
2214 Sprintf(eos(buf), " (%d)", u.mtimedone);
2215 you_are(buf, "");
2217 if (lays_eggs(youmonst.data) && flags.female) /* Upolyd */
2218 you_can("lay eggs", "");
2219 if (u.ulycn >= LOW_PM) {
2220 /* "you are a werecreature [in beast form]" */
2221 Strcpy(buf, an(mons[u.ulycn].mname));
2222 if (u.umonnum == u.ulycn) {
2223 Strcat(buf, " in beast form");
2224 if (wizard)
2225 Sprintf(eos(buf), " (%d)", u.mtimedone);
2227 you_are(buf, "");
2229 if (Unchanging && Upolyd) /* !Upolyd handled above */
2230 you_can("not change from your current form", from_what(UNCHANGING));
2231 if (Hate_silver)
2232 you_are("harmed by silver", "");
2233 /* movement and non-armor-based protection */
2234 if (Fast)
2235 you_are(Very_fast ? "very fast" : "fast", from_what(FAST));
2236 if (Reflecting)
2237 you_have("reflection", from_what(REFLECTING));
2238 if (Free_action)
2239 you_have("free action", from_what(FREE_ACTION));
2240 if (Fixed_abil)
2241 you_have("fixed abilities", from_what(FIXED_ABIL));
2242 if (Lifesaved)
2243 enl_msg("Your life ", "will be", "would have been", " saved", "");
2245 /*** Miscellany ***/
2246 if (Luck) {
2247 ltmp = abs((int) Luck);
2248 Sprintf(buf, "%s%slucky",
2249 ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "",
2250 Luck < 0 ? "un" : "");
2251 if (wizard)
2252 Sprintf(eos(buf), " (%d)", Luck);
2253 you_are(buf, "");
2254 } else if (wizard)
2255 enl_msg("Your luck ", "is", "was", " zero", "");
2256 if (u.moreluck > 0)
2257 you_have("extra luck", "");
2258 else if (u.moreluck < 0)
2259 you_have("reduced luck", "");
2260 if (carrying(LUCKSTONE) || stone_luck(TRUE)) {
2261 ltmp = stone_luck(0);
2262 if (ltmp <= 0)
2263 enl_msg("Bad luck ", "does", "did", " not time out for you", "");
2264 if (ltmp >= 0)
2265 enl_msg("Good luck ", "does", "did", " not time out for you", "");
2268 if (u.ugangr) {
2269 Sprintf(buf, " %sangry with you",
2270 u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : "");
2271 if (wizard)
2272 Sprintf(eos(buf), " (%d)", u.ugangr);
2273 enl_msg(u_gname(), " is", " was", buf, "");
2274 } else {
2276 * We need to suppress this when the game is over, because death
2277 * can change the value calculated by can_pray(), potentially
2278 * resulting in a false claim that you could have prayed safely.
2280 if (!final) {
2281 #if 0
2282 /* "can [not] safely pray" vs "could [not] have safely prayed" */
2283 Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ",
2284 final ? "have " : "", final ? "ed" : "");
2285 #else
2286 Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not ");
2287 #endif
2288 if (wizard)
2289 Sprintf(eos(buf), " (%d)", u.ublesscnt);
2290 you_can(buf, "");
2294 #ifdef DEBUG
2295 /* named fruit debugging (doesn't really belong here...); to enable,
2296 include 'fruit' in DEBUGFILES list (even though it isn't a file...) */
2297 if (wizard && explicitdebug("fruit")) {
2298 int fcount = 0;
2299 struct fruit *f;
2300 char buf2[BUFSZ];
2302 for (f = ffruit; f; f = f->nextf) {
2303 Sprintf(buf, "Fruit %d ", ++fcount);
2304 Sprintf(buf2, "%s (id %d)", f->fname, f->fid);
2305 enl_msg(buf, "is ", "was ", buf2, "");
2307 enl_msg("The current fruit ", "is ", "was ", pl_fruit, "");
2308 Sprintf(buf, "%d", flags.made_fruit);
2309 enl_msg("The made fruit flag ", "is ", "was ", buf, "");
2311 #endif
2314 const char *p;
2316 buf[0] = '\0';
2317 if (final < 2) { /* still in progress, or quit/escaped/ascended */
2318 p = "survived after being killed ";
2319 switch (u.umortality) {
2320 case 0:
2321 p = !final ? (char *) 0 : "survived";
2322 break;
2323 case 1:
2324 Strcpy(buf, "once");
2325 break;
2326 case 2:
2327 Strcpy(buf, "twice");
2328 break;
2329 case 3:
2330 Strcpy(buf, "thrice");
2331 break;
2332 default:
2333 Sprintf(buf, "%d times", u.umortality);
2334 break;
2336 } else { /* game ended in character's death */
2337 p = "are dead";
2338 switch (u.umortality) {
2339 case 0:
2340 impossible("dead without dying?");
2341 case 1:
2342 break; /* just "are dead" */
2343 default:
2344 Sprintf(buf, " (%d%s time!)", u.umortality,
2345 ordin(u.umortality));
2346 break;
2349 if (p)
2350 enl_msg(You_, "have been killed ", p, buf, "");
2354 #if 0 /* no longer used */
2355 STATIC_DCL boolean NDECL(minimal_enlightenment);
2358 * Courtesy function for non-debug, non-explorer mode players
2359 * to help refresh them about who/what they are.
2360 * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise.
2362 STATIC_OVL boolean
2363 minimal_enlightenment()
2365 winid tmpwin;
2366 menu_item *selected;
2367 anything any;
2368 int genidx, n;
2369 char buf[BUFSZ], buf2[BUFSZ];
2370 static const char untabbed_fmtstr[] = "%-15s: %-12s";
2371 static const char untabbed_deity_fmtstr[] = "%-17s%s";
2372 static const char tabbed_fmtstr[] = "%s:\t%-12s";
2373 static const char tabbed_deity_fmtstr[] = "%s\t%s";
2374 static const char *fmtstr;
2375 static const char *deity_fmtstr;
2377 fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr;
2378 deity_fmtstr = iflags.menu_tab_sep ? tabbed_deity_fmtstr
2379 : untabbed_deity_fmtstr;
2380 any = zeroany;
2381 buf[0] = buf2[0] = '\0';
2382 tmpwin = create_nhwindow(NHW_MENU);
2383 start_menu(tmpwin);
2384 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2385 "Starting", FALSE);
2387 /* Starting name, race, role, gender */
2388 Sprintf(buf, fmtstr, "name", plname);
2389 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2390 Sprintf(buf, fmtstr, "race", urace.noun);
2391 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2392 Sprintf(buf, fmtstr, "role",
2393 (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m);
2394 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2395 Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj);
2396 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2398 /* Starting alignment */
2399 Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL]));
2400 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2402 /* Current name, race, role, gender */
2403 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2404 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2405 "Current", FALSE);
2406 Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
2407 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2408 if (Upolyd) {
2409 Sprintf(buf, fmtstr, "role (base)",
2410 (u.mfemale && urole.name.f) ? urole.name.f
2411 : urole.name.m);
2412 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2413 } else {
2414 Sprintf(buf, fmtstr, "role",
2415 (flags.female && urole.name.f) ? urole.name.f
2416 : urole.name.m);
2417 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2419 /* don't want poly_gender() here; it forces `2' for non-humanoids */
2420 genidx = is_neuter(youmonst.data) ? 2 : flags.female;
2421 Sprintf(buf, fmtstr, "gender", genders[genidx].adj);
2422 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2423 if (Upolyd && (int) u.mfemale != genidx) {
2424 Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj);
2425 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2428 /* Current alignment */
2429 Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type));
2430 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2432 /* Deity list */
2433 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
2434 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2435 "Deities", FALSE);
2436 Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
2437 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2438 && u.ualign.type == A_CHAOTIC) ? " (s,c)"
2439 : (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)"
2440 : (u.ualign.type == A_CHAOTIC) ? " (c)" : "");
2441 Sprintf(buf, fmtstr, "Chaotic", buf2);
2442 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2444 Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
2445 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2446 && u.ualign.type == A_NEUTRAL) ? " (s,c)"
2447 : (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)"
2448 : (u.ualign.type == A_NEUTRAL) ? " (c)" : "");
2449 Sprintf(buf, fmtstr, "Neutral", buf2);
2450 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2452 Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
2453 (u.ualignbase[A_ORIGINAL] == u.ualign.type
2454 && u.ualign.type == A_LAWFUL) ? " (s,c)"
2455 : (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)"
2456 : (u.ualign.type == A_LAWFUL) ? " (c)" : "");
2457 Sprintf(buf, fmtstr, "Lawful", buf2);
2458 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
2460 end_menu(tmpwin, "Base Attributes");
2461 n = select_menu(tmpwin, PICK_NONE, &selected);
2462 destroy_nhwindow(tmpwin);
2463 return (boolean) (n != -1);
2465 #endif /*0*/
2467 /* ^X command */
2468 STATIC_PTR int
2469 doattributes(VOID_ARGS)
2471 int mode = BASICENLIGHTENMENT;
2473 /* show more--as if final disclosure--for wizard and explore modes */
2474 if (wizard || discover)
2475 mode |= MAGICENLIGHTENMENT;
2477 enlightenment(mode, ENL_GAMEINPROGRESS);
2478 return 0;
2481 void
2482 youhiding(via_enlghtmt, msgflag)
2483 boolean via_enlghtmt; /* englightment line vs topl message */
2484 int msgflag; /* for variant message phrasing */
2486 char *bp, buf[BUFSZ];
2488 Strcpy(buf, "hiding");
2489 if (youmonst.m_ap_type != M_AP_NOTHING) {
2490 /* mimic; hero is only able to mimic a strange object or gold
2491 or hallucinatory alternative to gold, so we skip the details
2492 for the hypothetical furniture and monster cases */
2493 bp = eos(strcpy(buf, "mimicking"));
2494 if (youmonst.m_ap_type == M_AP_OBJECT) {
2495 Sprintf(bp, " %s", an(simple_typename(youmonst.mappearance)));
2496 } else if (youmonst.m_ap_type == M_AP_FURNITURE) {
2497 Strcpy(bp, " something");
2498 } else if (youmonst.m_ap_type == M_AP_MONSTER) {
2499 Strcpy(bp, " someone");
2500 } else {
2501 ; /* something unexpected; leave 'buf' as-is */
2503 } else if (u.uundetected) {
2504 bp = eos(buf); /* points past "hiding" */
2505 if (youmonst.data->mlet == S_EEL) {
2506 if (is_pool(u.ux, u.uy))
2507 Sprintf(bp, " in the %s", waterbody_name(u.ux, u.uy));
2508 } else if (hides_under(youmonst.data)) {
2509 struct obj *o = level.objects[u.ux][u.uy];
2511 if (o)
2512 Sprintf(bp, " underneath %s", ansimpleoname(o));
2513 } else if (is_clinger(youmonst.data) || Flying) {
2514 /* Flying: 'lurker above' hides on ceiling but doesn't cling */
2515 Sprintf(bp, " on the %s", ceiling(u.ux, u.uy));
2516 } else {
2517 /* on floor; is_hider() but otherwise not special: 'trapper' */
2518 if (u.utrap && u.utraptype == TT_PIT) {
2519 struct trap *t = t_at(u.ux, u.uy);
2521 Sprintf(bp, " in a %spit",
2522 (t && t->ttyp == SPIKED_PIT) ? "spiked " : "");
2523 } else
2524 Sprintf(bp, " on the %s", surface(u.ux, u.uy));
2526 } else {
2527 ; /* shouldn't happen; will result in generic "you are hiding" */
2530 if (via_enlghtmt) {
2531 int final = msgflag; /* 'final' is used by you_are() macro */
2533 you_are(buf, "");
2534 } else {
2535 /* for dohide(), when player uses '#monster' command */
2536 You("are %s %s.", msgflag ? "already" : "now", buf);
2540 /* KMH, #conduct
2541 * (shares enlightenment's tense handling)
2543 STATIC_PTR int
2544 doconduct(VOID_ARGS)
2546 show_conduct(0);
2547 return 0;
2550 void
2551 show_conduct(final)
2552 int final;
2554 char buf[BUFSZ];
2555 int ngenocided;
2557 /* Create the conduct window */
2558 en_win = create_nhwindow(NHW_MENU);
2559 putstr(en_win, 0, "Voluntary challenges:");
2561 if (u.uroleplay.blind)
2562 you_have_been("blind from birth");
2563 if (u.uroleplay.nudist)
2564 you_have_been("faithfully nudist");
2566 if (!u.uconduct.food)
2567 enl_msg(You_, "have gone", "went", " without food", "");
2568 /* But beverages are okay */
2569 else if (!u.uconduct.unvegan)
2570 you_have_X("followed a strict vegan diet");
2571 else if (!u.uconduct.unvegetarian)
2572 you_have_been("vegetarian");
2574 if (!u.uconduct.gnostic)
2575 you_have_been("an atheist");
2577 if (!u.uconduct.weaphit) {
2578 you_have_never("hit with a wielded weapon");
2579 } else if (wizard) {
2580 Sprintf(buf, "used a wielded weapon %ld time%s", u.uconduct.weaphit,
2581 plur(u.uconduct.weaphit));
2582 you_have_X(buf);
2584 if (!u.uconduct.killer)
2585 you_have_been("a pacifist");
2587 if (!u.uconduct.literate) {
2588 you_have_been("illiterate");
2589 } else if (wizard) {
2590 Sprintf(buf, "read items or engraved %ld time%s", u.uconduct.literate,
2591 plur(u.uconduct.literate));
2592 you_have_X(buf);
2595 ngenocided = num_genocides();
2596 if (ngenocided == 0) {
2597 you_have_never("genocided any monsters");
2598 } else {
2599 Sprintf(buf, "genocided %d type%s of monster%s", ngenocided,
2600 plur(ngenocided), plur(ngenocided));
2601 you_have_X(buf);
2604 if (!u.uconduct.polypiles) {
2605 you_have_never("polymorphed an object");
2606 } else if (wizard) {
2607 Sprintf(buf, "polymorphed %ld item%s", u.uconduct.polypiles,
2608 plur(u.uconduct.polypiles));
2609 you_have_X(buf);
2612 if (!u.uconduct.polyselfs) {
2613 you_have_never("changed form");
2614 } else if (wizard) {
2615 Sprintf(buf, "changed form %ld time%s", u.uconduct.polyselfs,
2616 plur(u.uconduct.polyselfs));
2617 you_have_X(buf);
2620 if (!u.uconduct.wishes) {
2621 you_have_X("used no wishes");
2622 } else {
2623 Sprintf(buf, "used %ld wish%s", u.uconduct.wishes,
2624 (u.uconduct.wishes > 1L) ? "es" : "");
2625 you_have_X(buf);
2627 if (!u.uconduct.wisharti)
2628 enl_msg(You_, "have not wished", "did not wish",
2629 " for any artifacts", "");
2632 /* Pop up the window and wait for a key */
2633 display_nhwindow(en_win, TRUE);
2634 destroy_nhwindow(en_win);
2635 en_win = WIN_ERR;
2638 #ifndef M
2639 #ifndef NHSTDC
2640 #define M(c) (0x80 | (c))
2641 #else
2642 #define M(c) ((c) -128)
2643 #endif /* NHSTDC */
2644 #endif
2645 #ifndef C
2646 #define C(c) (0x1f & (c))
2647 #endif
2649 static const struct func_tab cmdlist[] = {
2650 { C('d'), FALSE, dokick }, /* "D" is for door!...? Msg is in dokick.c */
2651 { C('e'), TRUE, wiz_detect },
2652 { C('f'), TRUE, wiz_map },
2653 { C('g'), TRUE, wiz_genesis },
2654 { C('i'), TRUE, wiz_identify },
2655 { C('l'), TRUE, doredraw }, /* if number_pad is set */
2656 { C('n'), TRUE, donamelevel }, /* if number_pad is set */
2657 { C('o'), TRUE, dooverview_or_wiz_where }, /* depends on wizard status */
2658 { C('p'), TRUE, doprev_message },
2659 { C('r'), TRUE, doredraw },
2660 { C('t'), TRUE, dotele },
2661 { C('v'), TRUE, wiz_level_tele },
2662 { C('w'), TRUE, wiz_wish },
2663 { C('x'), TRUE, doattributes },
2664 { C('z'), TRUE, dosuspend_core },
2665 { 'a', FALSE, doapply },
2666 { 'A', FALSE, doddoremarm },
2667 { M('a'), TRUE, doorganize },
2668 { M('A'), TRUE, donamelevel }, /* #annotate */
2669 /* 'b', 'B' : go sw */
2670 { 'c', FALSE, doclose },
2671 { 'C', TRUE, docallcmd },
2672 { M('c'), TRUE, dotalk },
2673 { M('C'), TRUE, doconduct }, /* #conduct */
2674 { 'd', FALSE, dodrop },
2675 { 'D', FALSE, doddrop },
2676 { M('d'), FALSE, dodip },
2677 { 'e', FALSE, doeat },
2678 { 'E', FALSE, doengrave },
2679 { M('e'), TRUE, enhance_weapon_skill },
2680 { 'f', FALSE, dofire },
2681 /* 'F' : fight (one time) */
2682 { M('f'), FALSE, doforce },
2683 /* 'g', 'G' : multiple go */
2684 /* 'h', 'H' : go west */
2685 { 'h', TRUE, dohelp }, /* if number_pad is set */
2686 { 'i', TRUE, ddoinv },
2687 { 'I', TRUE, dotypeinv }, /* Robert Viduya */
2688 { M('i'), TRUE, doinvoke },
2689 /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
2690 { 'j', FALSE, dojump }, /* if number_pad is on */
2691 { M('j'), FALSE, dojump },
2692 { 'k', FALSE, dokick }, /* if number_pad is on */
2693 { 'l', FALSE, doloot }, /* if number_pad is on */
2694 { M('l'), FALSE, doloot },
2695 /* 'n' prefixes a count if number_pad is on */
2696 { M('m'), TRUE, domonability },
2697 { 'N', TRUE, docallcmd }, /* if number_pad is on */
2698 { M('n'), TRUE, docallcmd },
2699 { M('N'), TRUE, docallcmd },
2700 { 'o', FALSE, doopen },
2701 { 'O', TRUE, doset },
2702 { M('o'), FALSE, dosacrifice },
2703 { M('O'), TRUE, dooverview }, /* #overview */
2704 { 'p', FALSE, dopay },
2705 { 'P', FALSE, doputon },
2706 { M('p'), TRUE, dopray },
2707 { 'q', FALSE, dodrink },
2708 { 'Q', FALSE, dowieldquiver },
2709 { M('q'), TRUE, done2 },
2710 { 'r', FALSE, doread },
2711 { 'R', FALSE, doremring },
2712 { M('r'), FALSE, dorub },
2713 { M('R'), FALSE, doride }, /* #ride */
2714 { 's', TRUE, dosearch, "searching" },
2715 { 'S', TRUE, dosave },
2716 { M('s'), FALSE, dosit },
2717 { 't', FALSE, dothrow },
2718 { 'T', FALSE, dotakeoff },
2719 { M('t'), TRUE, doturn },
2720 { M('T'), FALSE, dotip }, /* #tip */
2721 /* 'u', 'U' : go ne */
2722 { 'u', FALSE, dountrap }, /* if number_pad is on */
2723 { M('u'), FALSE, dountrap },
2724 { 'v', TRUE, doversion },
2725 { 'V', TRUE, dohistory },
2726 { M('v'), TRUE, doextversion },
2727 { 'w', FALSE, dowield },
2728 { 'W', FALSE, dowear },
2729 { M('w'), FALSE, dowipe },
2730 { 'x', FALSE, doswapweapon },
2731 { 'X', FALSE, dotwoweapon },
2732 /* 'y', 'Y' : go nw */
2733 { 'z', FALSE, dozap },
2734 { 'Z', TRUE, docast },
2735 { '<', FALSE, doup },
2736 { '>', FALSE, dodown },
2737 { '/', TRUE, dowhatis },
2738 { '&', TRUE, dowhatdoes },
2739 { '?', TRUE, dohelp },
2740 { M('?'), TRUE, doextlist },
2741 #ifdef SHELL
2742 { '!', TRUE, dosh },
2743 #endif
2744 { '.', TRUE, donull, "waiting" },
2745 { ' ', TRUE, donull, "waiting" },
2746 { ',', FALSE, dopickup },
2747 { ':', TRUE, dolook },
2748 { ';', TRUE, doquickwhatis },
2749 { '^', TRUE, doidtrap },
2750 { '\\', TRUE, dodiscovered }, /* Robert Viduya */
2751 { '`', TRUE, doclassdisco },
2752 { '@', TRUE, dotogglepickup },
2753 { M('2'), FALSE, dotwoweapon },
2754 { WEAPON_SYM, TRUE, doprwep },
2755 { ARMOR_SYM, TRUE, doprarm },
2756 { RING_SYM, TRUE, doprring },
2757 { AMULET_SYM, TRUE, dopramulet },
2758 { TOOL_SYM, TRUE, doprtool },
2759 { '*', TRUE, doprinuse }, /* inventory of all equipment in use */
2760 { GOLD_SYM, TRUE, doprgold },
2761 { SPBOOK_SYM, TRUE, dovspell }, /* Mike Stephenson */
2762 { '#', TRUE, doextcmd },
2763 { '_', TRUE, dotravel },
2764 { 0, 0, 0, 0 }
2767 struct ext_func_tab extcmdlist[] = {
2768 { "adjust", "adjust inventory letters", doorganize, TRUE },
2769 { "annotate", "name current level", donamelevel, TRUE },
2770 { "chat", "talk to someone", dotalk, TRUE }, /* converse? */
2771 { "conduct", "list voluntary challenges you have maintained", doconduct,
2772 TRUE },
2773 { "dip", "dip an object into something", dodip, FALSE },
2774 { "enhance", "advance or check weapon and spell skills",
2775 enhance_weapon_skill, TRUE },
2776 { "exploremode", "enter explore mode", enter_explore_mode, TRUE },
2777 { "force", "force a lock", doforce, FALSE },
2778 { "invoke", "invoke an object's powers", doinvoke, TRUE },
2779 { "jump", "jump to a location", dojump, FALSE },
2780 { "loot", "loot a box on the floor", doloot, FALSE },
2781 { "monster", "use a monster's special ability", domonability, TRUE },
2782 { "name", "name a monster or an object", docallcmd, TRUE },
2783 { "offer", "offer a sacrifice to the gods", dosacrifice, FALSE },
2784 { "overview", "show an overview of the dungeon", dooverview, TRUE },
2785 { "pray", "pray to the gods for help", dopray, TRUE },
2786 { "quit", "exit without saving current game", done2, TRUE },
2787 { "ride", "ride (or stop riding) a monster", doride, FALSE },
2788 { "rub", "rub a lamp or a stone", dorub, FALSE },
2789 { "sit", "sit down", dosit, FALSE },
2790 { "terrain", "show map without obstructions", doterrain, TRUE },
2791 { "tip", "empty a container", dotip, FALSE },
2792 { "turn", "turn undead", doturn, TRUE },
2793 { "twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE },
2794 { "untrap", "untrap something", dountrap, FALSE },
2795 { "version", "list compile time options for this version of NetHack",
2796 doextversion, TRUE },
2797 { "wipe", "wipe off your face", dowipe, FALSE },
2798 { "?", "get this list of extended commands", doextlist, TRUE },
2800 * There must be a blank entry here for every entry in the table
2801 * below.
2803 { (char *) 0, (char *) 0, donull, TRUE }, /* levelchange */
2804 { (char *) 0, (char *) 0, donull, TRUE }, /* lightsources */
2805 #ifdef DEBUG_MIGRATING_MONS
2806 { (char *) 0, (char *) 0, donull, TRUE }, /* migratemons */
2807 #endif
2808 { (char *) 0, (char *) 0, donull, TRUE }, /* monpolycontrol */
2809 { (char *) 0, (char *) 0, donull, TRUE }, /* panic */
2810 { (char *) 0, (char *) 0, donull, TRUE }, /* polyself */
2811 #ifdef PORT_DEBUG
2812 { (char *) 0, (char *) 0, donull, TRUE }, /* portdebug */
2813 #endif
2814 { (char *) 0, (char *) 0, donull, TRUE }, /* seenv */
2815 { (char *) 0, (char *) 0, donull, TRUE }, /* stats */
2816 { (char *) 0, (char *) 0, donull, TRUE }, /* timeout */
2817 { (char *) 0, (char *) 0, donull, TRUE }, /* vanquished */
2818 { (char *) 0, (char *) 0, donull, TRUE }, /* vision */
2819 { (char *) 0, (char *) 0, donull, TRUE }, /* wizsmell */
2820 { (char *) 0, (char *) 0, donull, TRUE }, /* wizintrinsic */
2821 #ifdef DEBUG
2822 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_traveldisplay */
2823 { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_bury */
2824 #endif
2825 { (char *) 0, (char *) 0, donull, TRUE }, /* wizrumorcheck */
2826 { (char *) 0, (char *) 0, donull, TRUE }, /* wmode */
2827 { (char *) 0, (char *) 0, donull, TRUE } /* sentinel */
2830 /* there must be a placeholder in the table above for every entry here */
2831 static const struct ext_func_tab debug_extcmdlist[] = {
2832 { "levelchange", "change experience level", wiz_level_change, TRUE },
2833 { "lightsources", "show mobile light sources", wiz_light_sources, TRUE },
2834 #ifdef DEBUG_MIGRATING_MONS
2835 { "migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE },
2836 #endif
2837 { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol,
2838 TRUE },
2839 { "panic", "test panic routine (fatal to game)", wiz_panic, TRUE },
2840 { "polyself", "polymorph self", wiz_polyself, TRUE },
2841 #ifdef PORT_DEBUG
2842 { "portdebug", "wizard port debug command", wiz_port_debug, TRUE },
2843 #endif
2844 { "seenv", "show seen vectors", wiz_show_seenv, TRUE },
2845 { "stats", "show memory statistics", wiz_show_stats, TRUE },
2846 { "timeout", "look at timeout queue", wiz_timeout_queue, TRUE },
2847 { "vanquished", "list vanquished monsters", dovanquished, TRUE },
2848 { "vision", "show vision array", wiz_show_vision, TRUE },
2849 { "wizsmell", "smell monster", wiz_smell, TRUE },
2850 { "wizintrinsic", "set intrinsic", wiz_intrinsic, TRUE },
2851 #ifdef DEBUG
2852 { "wizdebug_traveldisplay", "wizard debug: toggle travel display",
2853 wiz_debug_cmd_traveldisplay, TRUE },
2854 { "wizdebug_bury", "wizard debug: bury objs under and around you",
2855 wiz_debug_cmd_bury, TRUE },
2856 #endif
2857 { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, TRUE },
2858 { "wmode", "show wall modes", wiz_show_wmodes, TRUE },
2859 { (char *) 0, (char *) 0, donull, TRUE }
2863 * Insert debug commands into the extended command list. This function
2864 * assumes that the last entry will be the help entry.
2866 * You must add entries in ext_func_tab every time you add one to the
2867 * debug_extcmdlist().
2869 void
2870 add_debug_extended_commands()
2872 int i, j, k, n;
2874 /* count the # of help entries */
2875 for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++)
2878 for (i = 0; debug_extcmdlist[i].ef_txt; i++) {
2879 /* need enough room for "?" entry plus terminator */
2880 if (n + 2 >= SIZE(extcmdlist))
2881 panic("Too many debugging commands!");
2882 for (j = 0; j < n; j++)
2883 if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0)
2884 break;
2886 /* insert i'th debug entry into extcmdlist[j], pushing down */
2887 for (k = n; k >= j; --k)
2888 extcmdlist[k + 1] = extcmdlist[k];
2889 extcmdlist[j] = debug_extcmdlist[i];
2890 n++; /* now an extra entry */
2894 STATIC_OVL char
2895 cmd_from_func(fn)
2896 int NDECL((*fn));
2898 int i;
2899 for (i = 0; i < SIZE(cmdlist); ++i)
2900 if (cmdlist[i].f_funct == fn)
2901 return cmdlist[i].f_char;
2902 return 0;
2905 static const char template[] = "%-18s %4ld %6ld";
2906 static const char count_str[] = " count bytes";
2907 static const char separator[] = "------------------ ----- ------";
2909 STATIC_OVL int
2910 size_obj(otmp)
2911 struct obj *otmp;
2913 int sz = (int) sizeof(struct obj);
2915 if (otmp->oextra) {
2916 sz += (int) sizeof(struct oextra);
2917 if (ONAME(otmp))
2918 sz += (int) strlen(ONAME(otmp)) + 1;
2919 if (OMONST(otmp))
2920 sz += (int) sizeof(struct monst);
2921 if (OMID(otmp))
2922 sz += (int) sizeof(unsigned);
2923 if (OLONG(otmp))
2924 sz += (int) sizeof(long);
2925 if (OMAILCMD(otmp))
2926 sz += (int) strlen(OMAILCMD(otmp)) + 1;
2928 return sz;
2931 STATIC_OVL void
2932 count_obj(chain, total_count, total_size, top, recurse)
2933 struct obj *chain;
2934 long *total_count;
2935 long *total_size;
2936 boolean top;
2937 boolean recurse;
2939 long count, size;
2940 struct obj *obj;
2942 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
2943 if (top) {
2944 count++;
2945 size += size_obj(obj);
2947 if (recurse && obj->cobj)
2948 count_obj(obj->cobj, total_count, total_size, TRUE, TRUE);
2950 *total_count += count;
2951 *total_size += size;
2954 STATIC_OVL void
2955 obj_chain(win, src, chain, total_count, total_size)
2956 winid win;
2957 const char *src;
2958 struct obj *chain;
2959 long *total_count;
2960 long *total_size;
2962 char buf[BUFSZ];
2963 long count = 0, size = 0;
2965 count_obj(chain, &count, &size, TRUE, FALSE);
2966 *total_count += count;
2967 *total_size += size;
2968 Sprintf(buf, template, src, count, size);
2969 putstr(win, 0, buf);
2972 STATIC_OVL void
2973 mon_invent_chain(win, src, chain, total_count, total_size)
2974 winid win;
2975 const char *src;
2976 struct monst *chain;
2977 long *total_count;
2978 long *total_size;
2980 char buf[BUFSZ];
2981 long count = 0, size = 0;
2982 struct monst *mon;
2984 for (mon = chain; mon; mon = mon->nmon)
2985 count_obj(mon->minvent, &count, &size, TRUE, FALSE);
2986 *total_count += count;
2987 *total_size += size;
2988 Sprintf(buf, template, src, count, size);
2989 putstr(win, 0, buf);
2992 STATIC_OVL void
2993 contained(win, src, total_count, total_size)
2994 winid win;
2995 const char *src;
2996 long *total_count;
2997 long *total_size;
2999 char buf[BUFSZ];
3000 long count = 0, size = 0;
3001 struct monst *mon;
3003 count_obj(invent, &count, &size, FALSE, TRUE);
3004 count_obj(fobj, &count, &size, FALSE, TRUE);
3005 count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE);
3006 count_obj(migrating_objs, &count, &size, FALSE, TRUE);
3007 /* DEADMONSTER check not required in this loop since they have no
3008 * inventory */
3009 for (mon = fmon; mon; mon = mon->nmon)
3010 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3011 for (mon = migrating_mons; mon; mon = mon->nmon)
3012 count_obj(mon->minvent, &count, &size, FALSE, TRUE);
3014 *total_count += count;
3015 *total_size += size;
3017 Sprintf(buf, template, src, count, size);
3018 putstr(win, 0, buf);
3021 STATIC_OVL int
3022 size_monst(mtmp)
3023 struct monst *mtmp;
3025 int sz = (int) sizeof(struct monst);
3027 if (mtmp->mextra) {
3028 sz += (int) sizeof(struct mextra);
3029 if (MNAME(mtmp))
3030 sz += (int) strlen(MNAME(mtmp)) + 1;
3031 if (EGD(mtmp))
3032 sz += (int) sizeof(struct egd);
3033 if (EPRI(mtmp))
3034 sz += (int) sizeof(struct epri);
3035 if (ESHK(mtmp))
3036 sz += (int) sizeof(struct eshk);
3037 if (EMIN(mtmp))
3038 sz += (int) sizeof(struct emin);
3039 if (EDOG(mtmp))
3040 sz += (int) sizeof(struct edog);
3041 /* mextra->mcorpsenm doesn't point to more memory */
3043 return sz;
3046 STATIC_OVL void
3047 mon_chain(win, src, chain, total_count, total_size)
3048 winid win;
3049 const char *src;
3050 struct monst *chain;
3051 long *total_count;
3052 long *total_size;
3054 char buf[BUFSZ];
3055 long count, size;
3056 struct monst *mon;
3058 for (count = size = 0, mon = chain; mon; mon = mon->nmon) {
3059 count++;
3060 size += size_monst(mon);
3062 *total_count += count;
3063 *total_size += size;
3064 Sprintf(buf, template, src, count, size);
3065 putstr(win, 0, buf);
3069 * Display memory usage of all monsters and objects on the level.
3071 static int
3072 wiz_show_stats()
3074 char buf[BUFSZ];
3075 winid win;
3076 long total_obj_size = 0, total_obj_count = 0;
3077 long total_mon_size = 0, total_mon_count = 0;
3079 win = create_nhwindow(NHW_TEXT);
3080 putstr(win, 0, "Current memory statistics:");
3081 putstr(win, 0, "");
3082 Sprintf(buf, "Objects, size %d", (int) sizeof(struct obj));
3083 putstr(win, 0, buf);
3084 putstr(win, 0, "");
3085 putstr(win, 0, count_str);
3087 obj_chain(win, "invent", invent, &total_obj_count, &total_obj_size);
3088 obj_chain(win, "fobj", fobj, &total_obj_count, &total_obj_size);
3089 obj_chain(win, "buried", level.buriedobjlist, &total_obj_count,
3090 &total_obj_size);
3091 obj_chain(win, "migrating obj", migrating_objs, &total_obj_count,
3092 &total_obj_size);
3093 mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size);
3094 mon_invent_chain(win, "migrating minvent", migrating_mons,
3095 &total_obj_count, &total_obj_size);
3097 contained(win, "contained", &total_obj_count, &total_obj_size);
3099 putstr(win, 0, separator);
3100 Sprintf(buf, template, "Total", total_obj_count, total_obj_size);
3101 putstr(win, 0, buf);
3103 putstr(win, 0, "");
3104 putstr(win, 0, "");
3105 Sprintf(buf, "Monsters, size %d", (int) sizeof(struct monst));
3106 putstr(win, 0, buf);
3107 putstr(win, 0, "");
3109 mon_chain(win, "fmon", fmon, &total_mon_count, &total_mon_size);
3110 mon_chain(win, "migrating", migrating_mons, &total_mon_count,
3111 &total_mon_size);
3113 putstr(win, 0, separator);
3114 Sprintf(buf, template, "Total", total_mon_count, total_mon_size);
3115 putstr(win, 0, buf);
3117 #if defined(__BORLANDC__) && !defined(_WIN32)
3118 show_borlandc_stats(win);
3119 #endif
3121 display_nhwindow(win, FALSE);
3122 destroy_nhwindow(win);
3123 return 0;
3126 void
3127 sanity_check()
3129 obj_sanity_check();
3130 timer_sanity_check();
3131 mon_sanity_check();
3132 light_sources_sanity_check();
3135 #ifdef DEBUG_MIGRATING_MONS
3136 static int
3137 wiz_migrate_mons()
3139 int mcount = 0;
3140 char inbuf[BUFSZ];
3141 struct permonst *ptr;
3142 struct monst *mtmp;
3143 d_level tolevel;
3145 getlin("How many random monsters to migrate? [0]", inbuf);
3146 if (*inbuf == '\033')
3147 return 0;
3148 mcount = atoi(inbuf);
3149 if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz))
3150 return 0;
3151 while (mcount > 0) {
3152 if (Is_stronghold(&u.uz))
3153 assign_level(&tolevel, &valley_level);
3154 else
3155 get_level(&tolevel, depth(&u.uz) + 1);
3156 ptr = rndmonst();
3157 mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS);
3158 if (mtmp)
3159 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
3160 (coord *) 0);
3161 mcount--;
3163 return 0;
3165 #endif
3167 #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c))
3168 #define unmeta(c) (0x7f & (c))
3170 /* called at startup and after number_pad is twiddled */
3171 void
3172 reset_commands(initial)
3173 boolean initial;
3175 static const char sdir[] = "hykulnjb><",
3176 sdir_swap_yz[] = "hzkulnjb><",
3177 ndir[] = "47896321><",
3178 ndir_phone_layout[] = "41236987><";
3179 static const int ylist[] = {
3180 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y'))
3182 const struct func_tab *cmdtmp;
3183 boolean flagtemp;
3184 int c, i, updated = 0;
3186 if (initial) {
3187 updated = 1;
3188 for (i = 0; i < SIZE(cmdlist); i++) {
3189 c = cmdlist[i].f_char & 0xff;
3190 Cmd.commands[c] = &cmdlist[i];
3192 Cmd.num_pad = FALSE;
3193 Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE;
3194 } else {
3195 /* basic num_pad */
3196 flagtemp = iflags.num_pad;
3197 if (flagtemp != Cmd.num_pad) {
3198 Cmd.num_pad = flagtemp;
3199 ++updated;
3201 /* swap_yz mode (only applicable for !num_pad) */
3202 flagtemp = (iflags.num_pad_mode & 1) ? !Cmd.num_pad : FALSE;
3203 if (flagtemp != Cmd.swap_yz) {
3204 Cmd.swap_yz = flagtemp;
3205 ++updated;
3206 /* Cmd.swap_yz has been toggled;
3207 perform the swap (or reverse previous one) */
3208 for (i = 0; i < SIZE(ylist); i++) {
3209 c = ylist[i] & 0xff;
3210 cmdtmp = Cmd.commands[c]; /* tmp = [y] */
3211 Cmd.commands[c] = Cmd.commands[c + 1]; /* [y] = [z] */
3212 Cmd.commands[c + 1] = cmdtmp; /* [z] = tmp */
3215 /* MSDOS compatibility mode (only applicable for num_pad) */
3216 flagtemp = (iflags.num_pad_mode & 1) ? Cmd.num_pad : FALSE;
3217 if (flagtemp != Cmd.pcHack_compat) {
3218 Cmd.pcHack_compat = flagtemp;
3219 ++updated;
3220 /* pcHack_compat has been toggled */
3221 c = M('5') & 0xff;
3222 cmdtmp = Cmd.commands['5'];
3223 Cmd.commands['5'] = Cmd.commands[c];
3224 Cmd.commands[c] = cmdtmp;
3225 c = M('0') & 0xff;
3226 Cmd.commands[c] = Cmd.pcHack_compat ? Cmd.commands['I'] : 0;
3228 /* phone keypad layout (only applicable for num_pad) */
3229 flagtemp = (iflags.num_pad_mode & 2) ? Cmd.num_pad : FALSE;
3230 if (flagtemp != Cmd.phone_layout) {
3231 Cmd.phone_layout = flagtemp;
3232 ++updated;
3233 /* phone_layout has been toggled */
3234 for (i = 0; i < 3; i++) {
3235 c = '1' + i; /* 1,2,3 <-> 7,8,9 */
3236 cmdtmp = Cmd.commands[c]; /* tmp = [1] */
3237 Cmd.commands[c] = Cmd.commands[c + 6]; /* [1] = [7] */
3238 Cmd.commands[c + 6] = cmdtmp; /* [7] = tmp */
3239 c = (M('1') & 0xff) + i; /* M-1,M-2,M-3 <-> M-7,M-8,M-9 */
3240 cmdtmp = Cmd.commands[c]; /* tmp = [M-1] */
3241 Cmd.commands[c] = Cmd.commands[c + 6]; /* [M-1] = [M-7] */
3242 Cmd.commands[c + 6] = cmdtmp; /* [M-7] = tmp */
3245 } /*?initial*/
3247 if (updated)
3248 Cmd.serialno++;
3249 Cmd.dirchars = !Cmd.num_pad
3250 ? (!Cmd.swap_yz ? sdir : sdir_swap_yz)
3251 : (!Cmd.phone_layout ? ndir : ndir_phone_layout);
3252 Cmd.alphadirchars = !Cmd.num_pad ? Cmd.dirchars : sdir;
3254 Cmd.move_W = Cmd.dirchars[0];
3255 Cmd.move_NW = Cmd.dirchars[1];
3256 Cmd.move_N = Cmd.dirchars[2];
3257 Cmd.move_NE = Cmd.dirchars[3];
3258 Cmd.move_E = Cmd.dirchars[4];
3259 Cmd.move_SE = Cmd.dirchars[5];
3260 Cmd.move_S = Cmd.dirchars[6];
3261 Cmd.move_SW = Cmd.dirchars[7];
3264 STATIC_OVL boolean
3265 accept_menu_prefix(cmd_func)
3266 int NDECL((*cmd_func));
3268 if (cmd_func == dopickup || cmd_func == dotip
3269 || cmd_func == doextcmd || cmd_func == doextlist)
3270 return TRUE;
3271 return FALSE;
3274 void
3275 rhack(cmd)
3276 register char *cmd;
3278 boolean do_walk, do_rush, prefix_seen, bad_command,
3279 firsttime = (cmd == 0);
3281 iflags.menu_requested = FALSE;
3282 #ifdef SAFERHANGUP
3283 if (program_state.done_hup)
3284 end_of_input();
3285 #endif
3286 if (firsttime) {
3287 context.nopick = 0;
3288 cmd = parse();
3290 if (*cmd == '\033') {
3291 context.move = FALSE;
3292 return;
3294 if (*cmd == DOAGAIN && !in_doagain && saveq[0]) {
3295 in_doagain = TRUE;
3296 stail = 0;
3297 rhack((char *) 0); /* read and execute command */
3298 in_doagain = FALSE;
3299 return;
3301 /* Special case of *cmd == ' ' handled better below */
3302 if (!*cmd || *cmd == (char) 0377) {
3303 nhbell();
3304 context.move = FALSE;
3305 return; /* probably we just had an interrupt */
3308 /* handle most movement commands */
3309 do_walk = do_rush = prefix_seen = FALSE;
3310 context.travel = context.travel1 = 0;
3311 switch (*cmd) {
3312 case 'g':
3313 if (movecmd(cmd[1])) {
3314 context.run = 2;
3315 do_rush = TRUE;
3316 } else
3317 prefix_seen = TRUE;
3318 break;
3319 case '5':
3320 if (!Cmd.num_pad)
3321 break; /* else FALLTHRU */
3322 case 'G':
3323 if (movecmd(lowc(cmd[1]))) {
3324 context.run = 3;
3325 do_rush = TRUE;
3326 } else
3327 prefix_seen = TRUE;
3328 break;
3329 case '-':
3330 if (!Cmd.num_pad)
3331 break; /* else FALLTHRU */
3332 /* Effects of movement commands and invisible monsters:
3333 * m: always move onto space (even if 'I' remembered)
3334 * F: always attack space (even if 'I' not remembered)
3335 * normal movement: attack if 'I', move otherwise.
3337 case 'F':
3338 if (movecmd(cmd[1])) {
3339 context.forcefight = 1;
3340 do_walk = TRUE;
3341 } else
3342 prefix_seen = TRUE;
3343 break;
3344 case 'm':
3345 if (movecmd(cmd[1]) || u.dz) {
3346 context.run = 0;
3347 context.nopick = 1;
3348 if (!u.dz)
3349 do_walk = TRUE;
3350 else
3351 cmd[0] = cmd[1]; /* "m<" or "m>" */
3352 } else
3353 prefix_seen = TRUE;
3354 break;
3355 case 'M':
3356 if (movecmd(lowc(cmd[1]))) {
3357 context.run = 1;
3358 context.nopick = 1;
3359 do_rush = TRUE;
3360 } else
3361 prefix_seen = TRUE;
3362 break;
3363 case '0':
3364 if (!Cmd.num_pad)
3365 break;
3366 (void) ddoinv(); /* a convenience borrowed from the PC */
3367 context.move = FALSE;
3368 multi = 0;
3369 return;
3370 case CMD_CLICKLOOK:
3371 if (iflags.clicklook) {
3372 context.move = FALSE;
3373 do_look(2, &clicklook_cc);
3375 return;
3376 case CMD_TRAVEL:
3377 if (flags.travelcmd) {
3378 context.travel = 1;
3379 context.travel1 = 1;
3380 context.run = 8;
3381 context.nopick = 1;
3382 do_rush = TRUE;
3383 break;
3385 /*FALLTHRU*/
3386 default:
3387 if (movecmd(*cmd)) { /* ordinary movement */
3388 context.run = 0; /* only matters here if it was 8 */
3389 do_walk = TRUE;
3390 } else if (movecmd(Cmd.num_pad ? unmeta(*cmd) : lowc(*cmd))) {
3391 context.run = 1;
3392 do_rush = TRUE;
3393 } else if (movecmd(unctrl(*cmd))) {
3394 context.run = 3;
3395 do_rush = TRUE;
3397 break;
3400 /* some special prefix handling */
3401 /* overload 'm' prefix to mean "request a menu" */
3402 if (prefix_seen && cmd[0] == 'm') {
3403 /* (for func_tab cast, see below) */
3404 const struct func_tab *ft = Cmd.commands[cmd[1] & 0xff];
3405 int NDECL((*func)) = ft ? ((struct func_tab *) ft)->f_funct : 0;
3407 if (func && accept_menu_prefix(func)) {
3408 iflags.menu_requested = TRUE;
3409 ++cmd;
3413 if ((do_walk || do_rush) && !context.travel && !dxdy_moveok()) {
3414 /* trying to move diagonally as a grid bug;
3415 this used to be treated by movecmd() as not being
3416 a movement attempt, but that didn't provide for any
3417 feedback and led to strangeness if the key pressed
3418 ('u' in particular) was overloaded for num_pad use */
3419 You_cant("get there from here...");
3420 context.run = 0;
3421 context.nopick = context.forcefight = FALSE;
3422 context.move = context.mv = FALSE;
3423 multi = 0;
3424 return;
3427 if (do_walk) {
3428 if (multi)
3429 context.mv = TRUE;
3430 domove();
3431 context.forcefight = 0;
3432 return;
3433 } else if (do_rush) {
3434 if (firsttime) {
3435 if (!multi)
3436 multi = max(COLNO, ROWNO);
3437 u.last_str_turn = 0;
3439 context.mv = TRUE;
3440 domove();
3441 return;
3442 } else if (prefix_seen && cmd[1] == '\033') { /* <prefix><escape> */
3443 /* don't report "unknown command" for change of heart... */
3444 bad_command = FALSE;
3445 } else if (*cmd == ' ' && !flags.rest_on_space) {
3446 bad_command = TRUE; /* skip cmdlist[] loop */
3448 /* handle all other commands */
3449 } else {
3450 register const struct func_tab *tlist;
3451 int res, NDECL((*func));
3453 /* current - use *cmd to directly index cmdlist array */
3454 if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) {
3455 if (u.uburied && !tlist->can_if_buried) {
3456 You_cant("do that while you are buried!");
3457 res = 0;
3458 } else {
3459 /* we discard 'const' because some compilers seem to have
3460 trouble with the pointer passed to set_occupation() */
3461 func = ((struct func_tab *) tlist)->f_funct;
3462 if (tlist->f_text && !occupation && multi)
3463 set_occupation(func, tlist->f_text, multi);
3464 res = (*func)(); /* perform the command */
3466 if (!res) {
3467 context.move = FALSE;
3468 multi = 0;
3470 return;
3472 /* if we reach here, cmd wasn't found in cmdlist[] */
3473 bad_command = TRUE;
3476 if (bad_command) {
3477 char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */
3478 register char c;
3480 expcmd[0] = '\0';
3481 while ((c = *cmd++) != '\0')
3482 Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */
3484 if (!prefix_seen || !iflags.cmdassist
3485 || !help_dir(0, "Invalid direction key!"))
3486 Norep("Unknown command '%s'.", expcmd);
3488 /* didn't move */
3489 context.move = FALSE;
3490 multi = 0;
3491 return;
3494 /* convert an x,y pair into a direction code */
3496 xytod(x, y)
3497 schar x, y;
3499 register int dd;
3501 for (dd = 0; dd < 8; dd++)
3502 if (x == xdir[dd] && y == ydir[dd])
3503 return dd;
3504 return -1;
3507 /* convert a direction code into an x,y pair */
3508 void
3509 dtoxy(cc, dd)
3510 coord *cc;
3511 register int dd;
3513 cc->x = xdir[dd];
3514 cc->y = ydir[dd];
3515 return;
3518 /* also sets u.dz, but returns false for <> */
3520 movecmd(sym)
3521 char sym;
3523 register const char *dp = index(Cmd.dirchars, sym);
3525 u.dz = 0;
3526 if (!dp || !*dp)
3527 return 0;
3528 u.dx = xdir[dp - Cmd.dirchars];
3529 u.dy = ydir[dp - Cmd.dirchars];
3530 u.dz = zdir[dp - Cmd.dirchars];
3531 #if 0 /* now handled elsewhere */
3532 if (u.dx && u.dy && NODIAG(u.umonnum)) {
3533 u.dx = u.dy = 0;
3534 return 0;
3536 #endif
3537 return !u.dz;
3540 /* grid bug handling which used to be in movecmd() */
3542 dxdy_moveok()
3544 if (u.dx && u.dy && NODIAG(u.umonnum))
3545 u.dx = u.dy = 0;
3546 return u.dx || u.dy;
3549 /* decide whether a character (user input keystroke) requests screen repaint */
3550 boolean
3551 redraw_cmd(c)
3552 char c;
3554 return (boolean) (c == C('r') || (Cmd.num_pad && c == C('l')));
3558 * uses getdir() but unlike getdir() it specifically
3559 * produces coordinates using the direction from getdir()
3560 * and verifies that those coordinates are ok.
3562 * If the call to getdir() returns 0, Never_mind is displayed.
3563 * If the resulting coordinates are not okay, emsg is displayed.
3565 * Returns non-zero if coordinates in cc are valid.
3568 get_adjacent_loc(prompt, emsg, x, y, cc)
3569 const char *prompt, *emsg;
3570 xchar x, y;
3571 coord *cc;
3573 xchar new_x, new_y;
3574 if (!getdir(prompt)) {
3575 pline1(Never_mind);
3576 return 0;
3578 new_x = x + u.dx;
3579 new_y = y + u.dy;
3580 if (cc && isok(new_x, new_y)) {
3581 cc->x = new_x;
3582 cc->y = new_y;
3583 } else {
3584 if (emsg)
3585 pline1(emsg);
3586 return 0;
3588 return 1;
3592 getdir(s)
3593 const char *s;
3595 char dirsym;
3596 int is_mov;
3598 retry:
3599 if (in_doagain || *readchar_queue)
3600 dirsym = readchar();
3601 else
3602 dirsym = yn_function((s && *s != '^') ? s : "In what direction?",
3603 (char *) 0, '\0');
3604 /* remove the prompt string so caller won't have to */
3605 clear_nhwindow(WIN_MESSAGE);
3607 if (redraw_cmd(dirsym)) { /* ^R */
3608 docrt(); /* redraw */
3609 goto retry;
3611 savech(dirsym);
3613 if (dirsym == '.' || dirsym == 's') {
3614 u.dx = u.dy = u.dz = 0;
3615 } else if (!(is_mov = movecmd(dirsym)) && !u.dz) {
3616 boolean did_help = FALSE, help_requested;
3618 if (!index(quitchars, dirsym)) {
3619 help_requested = (dirsym == '?');
3620 if (help_requested || iflags.cmdassist) {
3621 did_help = help_dir((s && *s == '^') ? dirsym : 0,
3622 help_requested ? (const char *) 0
3623 : "Invalid direction key!");
3624 if (help_requested)
3625 goto retry;
3627 if (!did_help)
3628 pline("What a strange direction!");
3630 return 0;
3631 } else if (is_mov && !dxdy_moveok()) {
3632 You_cant("orient yourself that direction.");
3633 return 0;
3635 if (!u.dz && (Stunned || (Confusion && !rn2(5))))
3636 confdir();
3637 return 1;
3640 STATIC_OVL boolean
3641 help_dir(sym, msg)
3642 char sym;
3643 const char *msg;
3645 char ctrl;
3646 winid win;
3647 static const char wiz_only_list[] = "EFGIOVW";
3648 char buf[BUFSZ], buf2[BUFSZ], *explain;
3650 win = create_nhwindow(NHW_TEXT);
3651 if (!win)
3652 return FALSE;
3653 if (msg) {
3654 Sprintf(buf, "cmdassist: %s", msg);
3655 putstr(win, 0, buf);
3656 putstr(win, 0, "");
3658 if (letter(sym)) {
3659 sym = highc(sym);
3660 ctrl = (sym - 'A') + 1;
3661 if ((explain = dowhatdoes_core(ctrl, buf2))
3662 && (!index(wiz_only_list, sym) || wizard)) {
3663 Sprintf(buf, "Are you trying to use ^%c%s?", sym,
3664 index(wiz_only_list, sym)
3665 ? ""
3666 : " as specified in the Guidebook");
3667 putstr(win, 0, buf);
3668 putstr(win, 0, "");
3669 putstr(win, 0, explain);
3670 putstr(win, 0, "");
3671 putstr(win, 0, "To use that command, you press");
3672 Sprintf(buf, "the <Ctrl> key, and the <%c> key at the same time.",
3673 sym);
3674 putstr(win, 0, buf);
3675 putstr(win, 0, "");
3678 if (NODIAG(u.umonnum)) {
3679 putstr(win, 0, "Valid direction keys in your current form are:");
3680 Sprintf(buf, " %c ", Cmd.move_N);
3681 putstr(win, 0, buf);
3682 putstr(win, 0, " | ");
3683 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3684 putstr(win, 0, buf);
3685 putstr(win, 0, " | ");
3686 Sprintf(buf, " %c ", Cmd.move_S);
3687 putstr(win, 0, buf);
3688 } else {
3689 putstr(win, 0, "Valid direction keys are:");
3690 Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N,
3691 Cmd.move_NE);
3692 putstr(win, 0, buf);
3693 putstr(win, 0, " \\ | / ");
3694 Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E);
3695 putstr(win, 0, buf);
3696 putstr(win, 0, " / | \\ ");
3697 Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S,
3698 Cmd.move_SE);
3699 putstr(win, 0, buf);
3701 putstr(win, 0, "");
3702 putstr(win, 0, " < up");
3703 putstr(win, 0, " > down");
3704 putstr(win, 0, " . direct at yourself");
3705 if (msg) {
3706 /* non-null msg means that this wasn't an explicit user request */
3707 putstr(win, 0, "");
3708 putstr(win, 0,
3709 "(Suppress this message with !cmdassist in config file.)");
3711 display_nhwindow(win, FALSE);
3712 destroy_nhwindow(win);
3713 return TRUE;
3716 void
3717 confdir()
3719 register int x = NODIAG(u.umonnum) ? 2 * rn2(4) : rn2(8);
3721 u.dx = xdir[x];
3722 u.dy = ydir[x];
3723 return;
3726 const char *
3727 directionname(dir)
3728 int dir;
3730 static NEARDATA const char *const dirnames[] = {
3731 "west", "northwest", "north", "northeast", "east",
3732 "southeast", "south", "southwest", "down", "up",
3735 if (dir < 0 || dir >= SIZE(dirnames))
3736 return "invalid";
3737 return dirnames[dir];
3741 isok(x, y)
3742 register int x, y;
3744 /* x corresponds to curx, so x==1 is the first column. Ach. %% */
3745 return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1;
3748 static NEARDATA int last_multi;
3751 * convert a MAP window position into a movecmd
3753 const char *
3754 click_to_cmd(x, y, mod)
3755 int x, y, mod;
3757 int dir;
3758 static char cmd[4];
3759 cmd[1] = 0;
3761 if (iflags.clicklook && mod == CLICK_2) {
3762 clicklook_cc.x = x;
3763 clicklook_cc.y = y;
3764 cmd[0] = CMD_CLICKLOOK;
3765 return cmd;
3768 x -= u.ux;
3769 y -= u.uy;
3771 if (flags.travelcmd) {
3772 if (abs(x) <= 1 && abs(y) <= 1) {
3773 x = sgn(x), y = sgn(y);
3774 } else {
3775 u.tx = u.ux + x;
3776 u.ty = u.uy + y;
3777 cmd[0] = CMD_TRAVEL;
3778 return cmd;
3781 if (x == 0 && y == 0) {
3782 /* here */
3783 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ)
3784 || IS_SINK(levl[u.ux][u.uy].typ)) {
3785 cmd[0] = mod == CLICK_1 ? 'q' : M('d');
3786 return cmd;
3787 } else if (IS_THRONE(levl[u.ux][u.uy].typ)) {
3788 cmd[0] = M('s');
3789 return cmd;
3790 } else if ((u.ux == xupstair && u.uy == yupstair)
3791 || (u.ux == sstairs.sx && u.uy == sstairs.sy
3792 && sstairs.up)
3793 || (u.ux == xupladder && u.uy == yupladder)) {
3794 return "<";
3795 } else if ((u.ux == xdnstair && u.uy == ydnstair)
3796 || (u.ux == sstairs.sx && u.uy == sstairs.sy
3797 && !sstairs.up)
3798 || (u.ux == xdnladder && u.uy == ydnladder)) {
3799 return ">";
3800 } else if (OBJ_AT(u.ux, u.uy)) {
3801 cmd[0] =
3802 Is_container(level.objects[u.ux][u.uy]) ? M('l') : ',';
3803 return cmd;
3804 } else {
3805 return "."; /* just rest */
3809 /* directional commands */
3811 dir = xytod(x, y);
3813 if (!m_at(u.ux + x, u.uy + y)
3814 && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) {
3815 cmd[1] = Cmd.dirchars[dir];
3816 cmd[2] = '\0';
3817 if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) {
3818 /* slight assistance to the player: choose kick/open for them
3820 if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) {
3821 cmd[0] = C('d');
3822 return cmd;
3824 if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) {
3825 cmd[0] = 'o';
3826 return cmd;
3829 if (levl[u.ux + x][u.uy + y].typ <= SCORR) {
3830 cmd[0] = 's';
3831 cmd[1] = 0;
3832 return cmd;
3835 } else {
3836 /* convert without using floating point, allowing sloppy clicking */
3837 if (x > 2 * abs(y))
3838 x = 1, y = 0;
3839 else if (y > 2 * abs(x))
3840 x = 0, y = 1;
3841 else if (x < -2 * abs(y))
3842 x = -1, y = 0;
3843 else if (y < -2 * abs(x))
3844 x = 0, y = -1;
3845 else
3846 x = sgn(x), y = sgn(y);
3848 if (x == 0 && y == 0) /* map click on player to "rest" command */
3849 return ".";
3851 dir = xytod(x, y);
3854 /* move, attack, etc. */
3855 cmd[1] = 0;
3856 if (mod == CLICK_1) {
3857 cmd[0] = Cmd.dirchars[dir];
3858 } else {
3859 cmd[0] = (Cmd.num_pad
3860 ? M(Cmd.dirchars[dir])
3861 : (Cmd.dirchars[dir] - 'a' + 'A')); /* run command */
3864 return cmd;
3867 char
3868 get_count(allowchars, inkey, max, count)
3869 char *allowchars;
3870 char inkey;
3871 long max;
3872 long *count;
3874 char qbuf[QBUFSZ];
3875 int key;
3876 long cnt = 0;
3877 boolean backspaced = FALSE;
3878 char *ret;
3879 for (;;) {
3880 if (inkey) {
3881 key = inkey;
3882 inkey = '\0';
3883 } else
3884 key = readchar();
3885 if (digit(key)) {
3886 cnt = 10L * cnt + (long)(key - '0');
3887 } else if (key == '\b') {
3888 cnt = cnt / 10;
3889 backspaced = TRUE;
3890 } else if (key == '\033') {
3891 return '\033';
3892 } else if (allowchars) {
3893 if (ret = index(allowchars, key)) {
3894 *count = cnt;
3895 return *ret;
3897 } else {
3898 *count = cnt;
3899 return key;
3901 if (max && (cnt > max))
3902 cnt = max;
3903 if (cnt > 9 || backspaced) {
3904 clear_nhwindow(WIN_MESSAGE);
3905 if (backspaced && !cnt)
3906 Sprintf(qbuf, "Count: ");
3907 else {
3908 Sprintf(qbuf, "Count: %d", cnt);
3909 backspaced = FALSE;
3911 pline1(qbuf);
3912 mark_synch();
3918 STATIC_OVL char *
3919 parse()
3921 #ifdef LINT /* static char in_line[COLNO]; */
3922 char in_line[COLNO];
3923 #else
3924 static char in_line[COLNO];
3925 #endif
3926 register int foo;
3927 boolean prezero = FALSE;
3928 boolean backspaced = FALSE;
3930 multi = 0;
3931 context.move = 1;
3932 flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */
3934 #ifdef ALTMETA
3935 alt_esc = iflags.altmeta; /* readchar() hack */
3936 #endif
3937 if (!Cmd.num_pad || (foo = readchar()) == 'n') {
3938 long tmpmulti = multi;
3939 foo = get_count(NULL, '\0', LARGEST_INT, &tmpmulti);
3940 last_multi = multi = tmpmulti;
3942 #ifdef ALTMETA
3943 alt_esc = FALSE; /* readchar() reset */
3944 #endif
3946 if (foo == '\033') { /* esc cancels count (TH) */
3947 clear_nhwindow(WIN_MESSAGE);
3948 multi = last_multi = 0;
3949 } else if (foo == DOAGAIN || in_doagain) {
3950 multi = last_multi;
3951 } else {
3952 last_multi = multi;
3953 savech(0); /* reset input queue */
3954 savech((char) foo);
3957 if (multi) {
3958 multi--;
3959 save_cm = in_line;
3960 } else {
3961 save_cm = (char *) 0;
3963 /* in 3.4.3 this was in rhack(), where it was too late to handle M-5 */
3964 if (Cmd.pcHack_compat) {
3965 /* This handles very old inconsistent DOS/Windows behaviour
3966 in a different way: earlier, the keyboard handler mapped
3967 these, which caused counts to be strange when entered
3968 from the number pad. Now do not map them until here. */
3969 switch (foo) {
3970 case '5':
3971 foo = 'g';
3972 break;
3973 case M('5'):
3974 foo = 'G';
3975 break;
3976 case M('0'):
3977 foo = 'I';
3978 break;
3979 default:
3980 break; /* as is */
3984 in_line[0] = foo;
3985 in_line[1] = '\0';
3986 if (foo == 'g' || foo == 'G' || foo == 'm' || foo == 'M' || foo == 'F'
3987 || (Cmd.num_pad && (foo == '5' || foo == '-'))) {
3988 foo = readchar();
3989 savech((char) foo);
3990 in_line[1] = foo;
3991 in_line[2] = 0;
3993 clear_nhwindow(WIN_MESSAGE);
3994 if (prezero)
3995 in_line[0] = '\033';
3996 return in_line;
3999 #ifdef HANGUPHANDLING
4000 /* some very old systems, or descendents of such systems, expect signal
4001 handlers to have return type `int', but they don't actually inspect
4002 the return value so we should be safe using `void' unconditionally */
4003 /*ARGUSED*/
4004 void
4005 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
4006 int sig_unused UNUSED;
4008 if (program_state.exiting)
4009 program_state.in_moveloop = 0;
4010 nhwindows_hangup();
4011 #ifdef SAFERHANGUP
4012 /* When using SAFERHANGUP, the done_hup flag it tested in rhack
4013 and a couple of other places; actual hangup handling occurs then.
4014 This is 'safer' because it disallows certain cheats and also
4015 protects against losing objects in the process of being thrown,
4016 but also potentially riskier because the disconnected program
4017 must continue running longer before attempting a hangup save. */
4018 program_state.done_hup++;
4019 /* defer hangup iff game appears to be in progress */
4020 if (program_state.in_moveloop && program_state.something_worth_saving)
4021 return;
4022 #endif /* SAFERHANGUP */
4023 end_of_input();
4026 void
4027 end_of_input()
4029 #ifdef NOSAVEONHANGUP
4030 #ifdef INSURANCE
4031 if (flags.ins_chkpt && program_state.something_worth_saving)
4032 program_statue.preserve_locks = 1; /* keep files for recovery */
4033 #endif
4034 program_state.something_worth_saving = 0; /* don't save */
4035 #endif
4037 #ifndef SAFERHANGUP
4038 if (!program_state.done_hup++)
4039 #endif
4040 if (program_state.something_worth_saving)
4041 (void) dosave0();
4042 if (iflags.window_inited)
4043 exit_nhwindows((char *) 0);
4044 clearlocks();
4045 terminate(EXIT_SUCCESS);
4046 /*NOTREACHED*/ /* not necessarily true for vms... */
4047 return;
4049 #endif /* HANGUPHANDLING */
4051 char
4052 readchar()
4054 register int sym;
4055 int x = u.ux, y = u.uy, mod = 0;
4057 if (*readchar_queue)
4058 sym = *readchar_queue++;
4059 else
4060 sym = in_doagain ? pgetchar() : nh_poskey(&x, &y, &mod);
4062 #ifdef NR_OF_EOFS
4063 if (sym == EOF) {
4064 register int cnt = NR_OF_EOFS;
4066 * Some SYSV systems seem to return EOFs for various reasons
4067 * (?like when one hits break or for interrupted systemcalls?),
4068 * and we must see several before we quit.
4070 do {
4071 clearerr(stdin); /* omit if clearerr is undefined */
4072 sym = pgetchar();
4073 } while (--cnt && sym == EOF);
4075 #endif /* NR_OF_EOFS */
4077 if (sym == EOF) {
4078 #ifdef HANGUPHANDLING
4079 hangup(0); /* call end_of_input() or set program_state.done_hup */
4080 #endif
4081 sym = '\033';
4082 #ifdef ALTMETA
4083 } else if (sym == '\033' && alt_esc) {
4084 /* iflags.altmeta: treat two character ``ESC c'' as single `M-c' */
4085 sym = *readchar_queue ? *readchar_queue++ : pgetchar();
4086 if (sym == EOF || sym == 0)
4087 sym = '\033';
4088 else if (sym != '\033')
4089 sym |= 0200; /* force 8th bit on */
4090 #endif /*ALTMETA*/
4091 } else if (sym == 0) {
4092 /* click event */
4093 readchar_queue = click_to_cmd(x, y, mod);
4094 sym = *readchar_queue++;
4096 return (char) sym;
4099 STATIC_PTR int
4100 dotravel(VOID_ARGS)
4102 /* Keyboard travel command */
4103 static char cmd[2];
4104 coord cc;
4106 if (!flags.travelcmd)
4107 return 0;
4108 cmd[1] = 0;
4109 cc.x = iflags.travelcc.x;
4110 cc.y = iflags.travelcc.y;
4111 if (cc.x == -1 && cc.y == -1) {
4112 /* No cached destination, start attempt from current position */
4113 cc.x = u.ux;
4114 cc.y = u.uy;
4116 pline("Where do you want to travel to?");
4117 if (getpos(&cc, TRUE, "the desired destination") < 0) {
4118 /* user pressed ESC */
4119 return 0;
4121 iflags.travelcc.x = u.tx = cc.x;
4122 iflags.travelcc.y = u.ty = cc.y;
4123 cmd[0] = CMD_TRAVEL;
4124 readchar_queue = cmd;
4125 return 0;
4128 #ifdef PORT_DEBUG
4129 extern void NDECL(win32con_debug_keystrokes);
4130 extern void NDECL(win32con_handler_info);
4133 wiz_port_debug()
4135 int n, k;
4136 winid win;
4137 anything any;
4138 int item = 'a';
4139 int num_menu_selections;
4140 struct menu_selection_struct {
4141 char *menutext;
4142 void NDECL((*fn));
4143 } menu_selections[] = {
4144 #ifdef WIN32
4145 { "test win32 keystrokes (tty only)", win32con_debug_keystrokes },
4146 { "show keystroke handler information (tty only)",
4147 win32con_handler_info },
4148 #endif
4149 { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */
4152 num_menu_selections = SIZE(menu_selections) - 1;
4153 if (num_menu_selections > 0) {
4154 menu_item *pick_list;
4155 win = create_nhwindow(NHW_MENU);
4156 start_menu(win);
4157 for (k = 0; k < num_menu_selections; ++k) {
4158 any.a_int = k + 1;
4159 add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE,
4160 menu_selections[k].menutext, MENU_UNSELECTED);
4162 end_menu(win, "Which port debugging feature?");
4163 n = select_menu(win, PICK_ONE, &pick_list);
4164 destroy_nhwindow(win);
4165 if (n > 0) {
4166 n = pick_list[0].item.a_int - 1;
4167 free((genericptr_t) pick_list);
4168 /* execute the function */
4169 (*menu_selections[n].fn)();
4171 } else
4172 pline("No port-specific debug capability defined.");
4173 return 0;
4175 #endif /*PORT_DEBUG*/
4178 * Parameter validator for generic yes/no function to prevent
4179 * the core from sending too long a prompt string to the
4180 * window port causing a buffer overflow there.
4182 char
4183 yn_function(query, resp, def)
4184 const char *query, *resp;
4185 char def;
4187 char qbuf[QBUFSZ];
4189 iflags.last_msg = PLNMSG_UNKNOWN; /* most recent pline is clobbered */
4191 /* maximum acceptable length is QBUFSZ-1 */
4192 if (strlen(query) >= QBUFSZ) {
4193 /* caller shouldn't have passed anything this long */
4194 paniclog("Query truncated: ", query);
4195 (void) strncpy(qbuf, query, QBUFSZ - 1 - 3);
4196 Strcpy(&qbuf[QBUFSZ - 1 - 3], "...");
4197 query = qbuf;
4199 return (*windowprocs.win_yn_function)(query, resp, def);
4202 /* for paranoid_confirm:quit,die,attack prompting */
4203 boolean
4204 paranoid_query(be_paranoid, prompt)
4205 boolean be_paranoid;
4206 const char *prompt;
4208 boolean confirmed_ok;
4210 /* when paranoid, player must respond with "yes" rather than just 'y'
4211 to give the go-ahead for this query; default is "no" unless the
4212 ParanoidConfirm flag is set in which case there's no default */
4213 if (be_paranoid) {
4214 char qbuf[QBUFSZ], ans[BUFSZ];
4215 const char *promptprefix = "", *responsetype = ParanoidConfirm
4216 ? "(yes|no)"
4217 : "(yes) [no]";
4218 int trylimit = 6; /* 1 normal, 5 more with "Yes or No:" prefix */
4220 /* in addition to being paranoid about this particular
4221 query, we might be even more paranoid about all paranoia
4222 responses (ie, ParanoidConfirm is set) in which case we
4223 require "no" to reject in addition to "yes" to confirm
4224 (except we won't loop if response is ESC; it means no) */
4225 do {
4226 Sprintf(qbuf, "%s%s %s", promptprefix, prompt, responsetype);
4227 getlin(qbuf, ans);
4228 (void) mungspaces(ans);
4229 confirmed_ok = !strcmpi(ans, "yes");
4230 if (confirmed_ok || *ans == '\033')
4231 break;
4232 promptprefix = "\"Yes\" or \"No\": ";
4233 } while (ParanoidConfirm && strcmpi(ans, "no") && --trylimit);
4234 } else
4235 confirmed_ok = (yn(prompt) == 'y');
4237 return confirmed_ok;
4241 dosuspend_core()
4243 #ifdef SUSPEND
4244 /* Does current window system support suspend? */
4245 if ((*windowprocs.win_can_suspend)()) {
4246 /* NB: SYSCF SHELLERS handled in port code. */
4247 dosuspend();
4248 } else
4249 #endif
4250 Norep("Suspend command not available.");
4251 return 0;
4254 /*cmd.c*/